245 votes

Comment mettre partiellement à jour un objet dans MongoDB afin que le nouvel objet se superpose/fusionne avec l'objet existant ?

Étant donné ce document enregistré dans MongoDB

{
   _id : ...,
   some_key: { 
        param1 : "val1",
        param2 : "val2",
        param3 : "val3"
   }
}

Un objet contenant de nouvelles informations sur param2 y param3 du monde extérieur doit être sauvé

var new_info = {
    param2 : "val2_new",
    param3 : "val3_new"
};

Je veux fusionner / superposer les nouveaux champs sur l'état existant de l'objet afin que le paramètre 1 ne soit pas supprimé.

Faire cela

db.collection.update(  { _id:...} , { $set: { some_key : new_info  } } 

MongoDB fait exactement ce qu'on lui a demandé, et définit some_key à cette valeur. en remplaçant l'ancienne.

{
   _id : ...,
   some_key: { 
      param2 : "val2_new",
      param3 : "val3_new"
   }
}

Quel est le moyen de faire en sorte que MongoDB mette à jour uniquement les nouveaux champs (sans les déclarer un par un explicitement) ? pour obtenir ceci :

{
   _id : ...,
   some_key: { 
        param1 : "val1",
        param2 : "val2_new",
        param3 : "val3_new"
   }
}

J'utilise le client Java, mais tout exemple sera apprécié.

12voto

Xavier Guihot Points 6414

À partir de Mongo 4.2 , db.collection.updateMany() (ou db.collection.update() ) peut accepter un pipeline d'agrégation, ce qui permet d'utiliser des opérateurs d'agrégation tels que $addFields qui produit tous les champs existants des documents d'entrée et les champs nouvellement ajoutés :

var new_info = { param2: "val2_new", param3: "val3_new" }

// { some_key: { param1: "val1", param2: "val2", param3: "val3" } }
// { some_key: { param1: "val1", param2: "val2"                 } }
db.collection.updateMany({}, [{ $addFields: { some_key: new_info } }])
// { some_key: { param1: "val1", param2: "val2_new", param3: "val3_new" } }
// { some_key: { param1: "val1", param2: "val2_new", param3: "val3_new" } }
  • La première partie {} est la requête de correspondance, qui filtre les documents à mettre à jour (dans ce cas, tous les documents).

  • La deuxième partie [{ $addFields: { some_key: new_info } }] est le pipeline d'agrégation des mises à jour :

    • Notez les crochets qui indiquent l'utilisation d'un pipeline d'agrégation.
    • Puisqu'il s'agit d'un pipeline d'agrégation, nous pouvons utiliser $addFields .
    • $addFields réalise exactement ce dont vous avez besoin : mettre à jour l'objet de sorte que le nouvel objet se superpose / fusionne avec l'objet existant :
    • Dans ce cas, { param2: "val2_new", param3: "val3_new" } sera fusionné avec l'actuel some_key en gardant param1 intactes et soit ajouter ou remplacer les deux param2 y param3 .

9voto

Matt Smith Points 197

J'ai réussi à le faire de cette façon :

db.collection.update(  { _id:...} , { $set: { 'key.another_key' : new_info  } } );

J'ai une fonction qui gère mon profil mises à jour dynamiques

function update(prop, newVal) {
  const str = `profile.${prop}`;
  db.collection.update( { _id:...}, { $set: { [str]: newVal } } );
}

Note : "profil" est spécifique à mon implémentation, il s'agit simplement de la chaîne de la clé que vous souhaitez modifier.

5voto

Pravin Points 71

Vous pouvez plutôt faire un upsert, cette opération dans MongoDB est utilisée pour enregistrer le document dans la collection. Si le document correspond aux critères de la requête, l'opération de mise à jour sera effectuée, sinon un nouveau document sera inséré dans la collection.

quelque chose de similaire à ce qui suit

db.employees.update(
    {type:"FT"},
    {$set:{salary:200000}},
    {upsert : true}
 )

4voto

Veeram Points 36230

Vous pouvez utiliser $mergeObjects dans la mise à jour basée sur l'agrégation. Quelque chose comme

db.collection.update(
   { _id:...},
   [{"$set":{
      "some_key":{
        "$mergeObjects":[
          "$some_key",
          new info or { param2 : "val2_new", param3 : "val3_new"}
       ]
      }
   }}]
)

Autres exemples here

3voto

Vino Points 659
    // where clause DBObject
    DBObject query = new BasicDBObject("_id", new ObjectId(id));

    // modifications to be applied
    DBObject update = new BasicDBObject();

    // set new values
    update.put("$set", new BasicDBObject("param2","value2"));

   // update the document
    collection.update(query, update, true, false); //3rd param->upsertFlag, 4th param->updateMultiFlag

Si vous avez plusieurs champs à mettre à jour

        Document doc = new Document();
        doc.put("param2","value2");
        doc.put("param3","value3");
        update.put("$set", doc);

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