Updating Nested Documents in MongoDB

1,559 views
Skip to first unread message

Zeferinix Lihunger

unread,
Apr 14, 2016, 8:09:24 PM4/14/16
to mongodb-user
I currently want to achieve something similar to this question from SO http://stackoverflow.com/questions/10290621/how-do-i-partially-update-an-object-in-mongodb-so-the-new-object-will-overlay

What I want to achieve is perfectly described from the accepted answer:

If I understand the question correctly, you want to update a document with the contents of another document, but only the fields that are not already present, and completely ignore the fields that are already set (even if to another value).

The accepted answer from 4 years ago says it cannot be done in a single command. I'm new to MongoDB and can't seem to find more questions regarding this. Probably I'm not looking at the right term or this command just simply doesn't exist. Can someone enlighten me?

I'm using Python's driver.

William Byrne III

unread,
Apr 27, 2016, 6:50:38 PM4/27/16
to mongodb-user

Zeferinix,

You have a document nested inside another document, and you want to update some of the nested document fields but leave all other fields at their current values. You have a new document that holds just the fields you want to update and their new values, and you want to do the update as a single command.

A single call to update() can set the value of multiple fields, so in that sense this can be done in a single command:

// set up target document
db.coll1.insert({ _id : 999,
                embDoc: {f01:"val01",  f02:"val02",  f03:"val03",
                         f04:"val04",  f05:"val05",  f06:"val06",
                         ...
                         f99 : "val99"  }
                });

// new field values
var newVals = {f02:"val2_new",  f04:"val4_new",  f06:"val6_new"};

//
db.coll1.update({_id:999}, {$set: {"embDoc.f02" : newVals.f02,
                                   "embDoc.f04" : newVals.f04,
                                   "embDoc.f06" : newVals.f06}}) ;

You could also findAndModify() instead of update().

However, I think what the OP for the SO question you link to wanted was a single MongoDB document modifying command that he could pass the newVals variable to. He wanted something like this:

db.coll1.update({_id:999}, {$set: {"embDoc" : newVals}});

that didn’t wipe out all the fields which weren’t being updated. He didn’t want to have to specify all the “fNN” field names in his call to update like I did above. Presumably his newVals was built dynamically and could contain a different group of fields (and new values for them) at different times, so hard coding them was not a solution.

Here is code that reformats newVals into a JSON document that can be passed to $set in an update() command:

// build '{"key1:"value1", "key2:"value2", ... ' string from newVals
var setC = '{' ;
for (var ff in newVal) {
  setC = setC + ' "embDoc.' + ff + '":"newVal.' + ff + '",';
};
// trim end comma and replace with '}'
setC = setC.substring(0, setC.length -1) + '}' ;
// convert string to a JSON object
var setJ = JSON.parse(setC) ;

db.coll1.update({"_id":999}, {$set : setJ});

This is still only one MongoDB update command - the extra code is because the change information in the newVals document is not in the right form to pass to update().

The best solution overall would be to change the code that sets newVals variable equivalent in your application to use this format:

 var newVals = {"embDoc.f02" : "val2_new",
                 "embDoc.f04" : "val4_new",
                 "embDoc.f06" : "val6_new"
               }

That way it could replace setJ in my second update() command above and remove the need for the reformat code, making this the “one command solution” you are looking for.

III


William Byrne III

Reply all
Reply to author
Forward
0 new messages