104 votes

Comment puis-je mettre à jour une propriété d'un objet contenu dans un tableau d'un objet parent en utilisant mongodb?

Je suis en train d'utiliser MongoDB comme base de données. j'ai une donnée:

 {
   _id : '123'
   amis: [
     {nom: 'allen', emails: [{email: '11111', using: 'true'}]}
   ]
 }

maintenant, je veux modifier l'email des amis de l'utilisateur dont l'_id est '123' je code comme ça:

db.users.update ({_id: '123'}, {$set: {"amis.0.emails.$.email" : '2222'} })

c'est facile, mais, c'est incorrect, quand le tableau d'emails a deux données ou plus. alors, ma question est: comment puis-je modifier les données dans un champ imbriqué --- ayant simplement deux ou plus de tableaux imbriqués? Merci.

126voto

w0lf Points 11825

Vous devez utiliser la notation pointée (Dot Notation) pour les tableaux.

Cela signifie que vous devez remplacer le $ par l'index basé sur zéro de l'élément que vous souhaitez mettre à jour.

Par exemple :

db.users.update ({_id: '123'}, { '$set': {"amis.0.emails.0.email" : '2222'} });

mettra à jour le premier e-mail du premier ami, et

db.users.update ({_id: '123'}, { '$set': {"amis.0.emails.1.email" : '2222'} })

mettra à jour le deuxième e-mail du premier ami.

26voto

Nick Dario Points 38

Un moyen flexible de faire des mises à jour sur un tableau multiniveau est d'utiliser arrayFilters qui permet d'interroger des index et de les attribuer à un identifiant.

db.collection.update(
   {  },
   { : { "array.$[].field" : value } },
   { arrayFilters: [ { :  } ] }
)

Voici l'exemple que vous avez fourni plus un nouvel ami avec deux adresses e-mail :

{
   _id : '123'
   friends: [
     {name: 'allen', emails: [
        {email: '11111', using: 'true'}
     ]},
     {name: 'lucy' , emails: [
        {email: 'lucy@work.com', using:'true'}, 
        {email:'lucyGucy@zmail.com', using : 'false'}
     ]}
   ]
 }

Supposons que lucyGucy@zmail.com soit mis à jour en lucy.is.gucy@zmail.com. Nous pouvons utiliser les champs name et email dans un filtre de tableau pour identifier l'index que nous voulons mettre à jour.

db.users.update({_id:123}, {$set:{
    "friends.$[updateFriend].emails.$[updateEmail].email" : "lucy.is.gucy@zmail.com"
}}, {
    "arrayFilters": [
      {"updateFriend.name" : "lucy"},
      {"updateEmail.email" : "lucyGucy@zmail.com"}
    ]
})

De cette manière, la mise à jour n'est pas sensible à un ordre particulier dans le tableau. L'exemple ci-dessus utilise deux identifiants updateFriend et updateEmail qui correspondront - pour le tableau sur lequel ils sont appliqués - à tous les éléments remplissant les critères du filtre de tableau. Comme indiqué dans la documentation :

L' doit commencer par une lettre minuscule et ne contenir que des caractères alphanumériques.

Par ailleurs, bien que les adresses e-mail soient probablement uniques, je vous recommande d'inclure un identifiant unique sur tous les sous-documents afin que les arrayFilters puissent être exacts.

3voto

Kacper Points 47

Solution utilisant Mongoose:

    Users.findById("123", function(err, user) {

      var friends = user.friends;
        for ( i=0; i < friends.length; i++ ) {
          if (friends[i].name == 'allen') {
            friends[i].email = '2222';

            user.save(function(err) {
              if (err) throw err;
              console.log("email mis à jour");
            });
          } else {
            console.log("pas d'amis nommés allen");
          }
        }

    }

0voto

Eisneim Points 41

Mettre à jour quelque chose dans un tableau multidimensionnel est vraiment pénible, ma façon de le faire : remplacer le tableau profond.

db.user.findOne({_id:'123'},{friends:1}).lean().exec(function(err,user){
   var whichArrayToUpdate;
   for (var ii = 0; ii < user.friends.length; ii++) {
        for (var jj = 0; i < user.friends[ii].emails; jj++) {
            if(user.friends[ii].emails[jj].email == '1111' ){// mettez à jour ci-dessous

                user.friends[ii].emails[jj].email == 'ce que vous voulez définir.';

                whichArrayToReplace = user.friends[ii].emails;
                break;
            }
        };
   };

   db.user.update({'friends.name':'allen'},{$set{'friends.$.email': whichArrayToReplace} })
})

mais, pourquoi ne pas utiliser la méthode save()? la méthode save() remplacera tout votre document, si votre document est petit c'est ok, mais si votre document est vraiment grand, c'est une meilleure idée de remplacer juste une partie de votre document.

ou faire la boucle, utiliser la position du tableau de niveau supérieur et du deuxième niveau (ii et jj) pour mettre à jour.

mon conseil est le suivant : lorsque vous concevez un schéma, ne mettez pas de tableau dans un autre tableau sauf si vous ne prévoyez aucune mise à jour pour ce tableau.

-1voto

adam Points 49

J'ai une situation similaire où j'ai une catégorie principale telle que :

{
    "service": {
        "isReviewed": false,
        "_id": "5ea36b27d7ae560845afb88d",
        "mainCategory": "message ",
        "SubCategory": [
            {
                "priceChanged": true,
                "_id": "5ea36b27d7ae560845afb88e",
                "subCatPrice": "26",
                "subCatName": "mustach trimming"
            }
        ],
        "shopName": "paddy the barber",
        "user": "5ea1fd5b69512dc72ad2f48c",
        "date_created": "2020-04-24T22:41:43.368Z",
        "__v": 5
    }
}

Maintenant, il a fallu un certain temps pour comprendre comment mettre à jour une partie du sous-document, car le sous-document est stocké en tant qu'objet.

Pour commencer, je vérifie d'abord l'_id du document lorsqu'il est passé par le put/:id. Ensuite, je vérifie le propriétaire du document par rapport à req.user.

Si tout est bon, je boucle à travers le tableau puis je crée un nouvel objet. Ensuite, tout ce que je fais, c'est vérifier chaque req.body pour le prix ou le nom, s'il y a un nouveau prix, je mettrais simplement à jour la valeur du prix dans l'objet. S'il y a une nouvelle valeur, alors l'objet reste le même.

Ensuite, tout ce que j'ai dit, c'est que service.SubCategory = newObject et enregistrer les données.

Tout fonctionne bien, cependant, j'ai rencontré le problème de la mise à jour de l'_id du sous-document, j'ai donc réglé cela en conservant la vieille valeur telle qu'elle.

Maintenant, en termes de performance et d'optimisation, je ne suis pas entièrement sûr que ce soit la bonne méthode, mais cela fonctionne et s'il y a une meilleure façon, je suis prêt à changer.

Voici le code :

const newObject = {
      subCatPrice: "",
      subCatName: "",
      _id: "",
      priceChanged: false
    };

    let oldDetails = service.SubCategory;
    for (const old of oldDetails) {
      newObject.subCatPrice = old.subCatPrice;
      newObject.subCatName = old.subCatName;
      newObject._id = old._id;
    }

    price ? (newObject.subCatPrice = price ) && (newObject.priceChanged = true)  : newObject.subCatPrice;
    name ? (newObject.subCatName = name) : newObject.subCatName;

    (service.SubCategory = newObject), await service.save();

J'ai utilisé l'idée de l'état de React pour être honnête, où je prends simplement une copie de l'objet et la garde et j'applique une mise à jour à la partie que je veux mettre à jour.

Encore une fois, en termes de code propre et de tout ça, je ne suis pas sûr que ce soit la bonne façon de le faire, mais je suis aussi un étudiant en programmation et j'aimerais aussi apprendre.

J'espère que cela aidera quelqu'un

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X