Comment mettre à jour partiellement un object dans MongoDB pour que le nouvel object superpose / fusionne avec l’object existant

Compte tenu de ce document enregistré dans MongoDB

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

Un object avec de nouvelles informations sur param2 et param3 du monde extérieur doit être enregistré

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

Je veux fusionner / superposer les nouveaux champs sur l’état existant de l’object pour que param1 ne soit pas supprimé

Ce faisant

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

Cela conduira à MongoDB fait exactement ce qui a été demandé, et définit some_key à cette valeur. remplacer l’ancien.

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

Comment faire pour que MongoDB ne mette à jour que les nouveaux champs (sans les énoncer explicitement un par un)? 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é

    Si je comprends bien la question, vous souhaitez mettre à jour un document avec le contenu d’un autre document, mais uniquement les champs qui ne sont pas déjà présents et ignorer complètement les champs déjà définis (même si c’est une autre valeur).

    Il n’y a aucun moyen de le faire en une seule commande.

    Vous devez d’abord interroger le document, déterminer ce que vous voulez $set et le mettre à jour (en utilisant les anciennes valeurs comme filtre correspondant pour vous assurer de ne pas avoir de mises à jour simultanées).


    Une autre lecture de votre question serait que vous êtes satisfait de $set , mais que vous ne voulez pas définir explicitement tous les champs. Comment passeriez-vous les données alors?

    Vous savez que vous pouvez faire ce qui suit:

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

    Je l’ai résolu avec ma propre fonction. Si vous souhaitez mettre à jour le champ spécifié dans le document, vous devez le traiter clairement.

    Exemple:

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

    Si vous souhaitez mettre à jour uniquement param2, il est faux de le faire:

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

    Tu dois utiliser:

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

    Donc j’ai écrit une fonction comme ça:

     function _update($id, $data, $options=array()){ $temp = array(); foreach($data as $key => $value) { $temp["some_key.".$key] = $value; } $collection->update( array('_id' => $id), array('$set' => $temp) ); } _update('1', array('param2' => 'some data')); 

    Vous pouvez utiliser la notation par points pour accéder et définir des champs à l’intérieur des objects, sans affecter les autres propriétés de ces objects.

    Compte tenu de l’object que vous avez spécifié ci-dessus:

     > db.test.insert({"id": "test_object", "some_key": {"param1": "val1", "param2": "val2", "param3": "val3"}}) WriteResult({ "nInserted" : 1 }) 

    Nous pouvons simplement mettre à jour some_key.param2 et some_key.param3 :

     > db.test.findAndModify({ ... query: {"id": "test_object"}, ... update: {"$set": {"some_key.param2": "val2_new", "some_key.param3": "val3_new"}}, ... new: true ... }) { "_id" : ObjectId("56476e04e5f19d86ece5b81d"), "id" : "test_object", "some_key" : { "param1" : "val1", "param2" : "val2_new", "param3" : "val3_new" } } 

    Vous pouvez plonger aussi profondément que vous le souhaitez. Ceci est également utile pour append de nouvelles propriétés à un object sans affecter celles existantes.

    La meilleure solution consiste à extraire les propriétés de l’object et à en faire des paires clé-valeur de notation point à plat. Vous pouvez utiliser par exemple cette bibliothèque:

    https://www.npmjs.com/package/mongo-dot-notation

    Il a .flatten fonction .flatten qui vous permet de changer d’object en un ensemble de propriétés que vous pouvez ensuite atsortingbuer au modificateur $ set, sans crainte que les propriétés de votre object DB existant soient supprimées / écrasées sans besoin.

    Tiré de la documentation de mongo-dot-notation :

     var person = { firstName: 'John', lastName: 'Doe', address: { city: 'NY', street: 'Eighth Avenu', number: 123 } }; var instructions = dot.flatten(person) console.log(instructions); /* { $set: { 'firstName': 'John', 'lastName': 'Doe', 'address.city': 'NY', 'address.street': 'Eighth Avenu', 'address.number': 123 } } */ 

    Et puis, il forme un sélecteur parfait – il ne mettra à jour que les propriétés données. EDIT: J’aime être archéologue à certains moments;)

    Mongo vous permet de mettre à jour des documents nesteds à l’aide d’un fichier . convention. Jetez un coup d’oeil: mise à jour des documents nesteds dans mongodb . Voici une autre question du passé à propos d’une mise à jour de fusion, comme celle que vous recherchez. Je crois: mise à jour atomique MongoDB via le document ‘merge’

    Il semble que vous puissiez définir isPartialObject, qui peut accomplir ce que vous voulez.

    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 mes mises à jour de profil de manière dynamic

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

    Note: ‘profile’ est spécifique à mon implémentation, c’est juste la chaîne de la clé que vous souhaitez modifier.

    Oui, le meilleur moyen est de convertir la notation d’object en une représentation de chaîne clé-valeur plate, comme mentionné dans ce commentaire: https://stackoverflow.com/a/39357531/2529199

    Je voulais mettre en évidence une méthode alternative utilisant cette bibliothèque NPM: https://www.npmjs.com/package/dot-object qui vous permet de manipuler différents objects en utilisant la notation par points.

    J’ai utilisé ce modèle pour créer par programme une propriété d’object nestede en acceptant la valeur-clé en tant que variable de fonction, comme suit:

     const dot = require('dot-object'); function(docid, varname, varvalue){ let doc = dot.dot({ [varname]: varvalue }); Mongo.update({_id:docid},{$set:doc}); } 

    Ce modèle me permet d’utiliser indifféremment les propriétés nestedes et à un seul niveau, et de les insérer proprement dans Mongo.

    Si vous avez besoin de jouer avec des objects JS au-delà de Mongo, en particulier du côté client mais que vous travaillez avec Mongo, cette bibliothèque vous offre plus d’options que le module NPM mongo-dot-notation mentionné précédemment.

    PS Au départ, je voulais juste mentionner ceci comme un commentaire, mais apparemment mon représentant S / O n’est pas assez haut pour poster un commentaire. Donc, ne pas essayer de muscler le commentaire de SzybkiSasza, je voulais juste mettre en évidence la fourniture d’un module alternatif.

    utiliser $ set faire ce processus

     .update({"_id": args.dashboardId, "viewData._id": widgetId}, {$set: {"viewData.$.widgetData": widgetDoc.widgetData}}) .exec() .then(dashboardDoc => { return { result: dashboardDoc }; }); 

    Créez un object de mise à jour avec les noms de propriété, y compris le chemin de points nécessaire. (“somekey.” + à partir de l’exemple de l’OP), puis utilisez cette fonction pour effectuer la mise à jour.

     //the modification that's requested updateReq = { param2 : "val2_new", param3 : "val3_new" } //build a renamed version of the update request var update = {}; for(var field in updateReq){ update["somekey."+field] = updateReq[field]; } //apply the update without modifying fields not originally in the update db.collection.update({._id:...},{$set:update},{upsert:true},function(err,result){...}); 
      // 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); 

    Dans Mongo 3.6, une fonction mergeObjects fait exactement ce dont vous avez besoin:

    https://docs.mongodb.com/manual/reference/operator/aggregation/mergeObjects/