Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I partially update an object in MongoDB so the new object will overlay / merge with the existing one

Given this document saved in MongoDB

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

An object with new information on param2 and param3 from the outside world needs to be saved

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

I want to merge / overlay the new fields over the existing state of the object so that param1 doesn't get removed

Doing this

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

Will lead to MongoDB is doing exactly as it was asked, and sets some_key to that value. replacing the old one.

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

What is the way to have MongoDB update only new fields (without stating them one by one explicitly)? to get this:

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

I'm using the Java client, but any example will be appreciated

like image 606
Eran Medan Avatar asked Apr 24 '12 01:04

Eran Medan


People also ask

How do you update one property in MongoDB?

To update a single field or specific fields just use the $set operator. This will update a specific field of "citiName" by value "Jakarta Pusat" that defined by $set operator.

What is the difference between update and Upsert in MongoDB?

Overview. Upsert is a combination of insert and update (inSERT + UPdate = upsert). We can use the upsert with different update methods, i.e., update, findAndModify, and replaceOne. Here in MongoDB, the upsert option is a Boolean value.


2 Answers

I solved it with my own function. If you want to update specified field in document you need to address it clearly.

Example:

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

If you want to update param2 only, it's wrong to do:

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

You must use:

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

So i wrote a function something like that:

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')); 
like image 192
mehmatrix Avatar answered Oct 09 '22 02:10

mehmatrix


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).

There is no way to do that in a single command.

You have to query the document first, figure out what you want to $set and then update it (using the old values as a matching filter to make sure you don't get concurrent updates in between).


Another reading of your question would be that you are happy with $set, but do not want to explicitly set all fields. How would you pass in the data then?

You know you can do the following:

db.collection.update(  { _id:...} , { $set: someObjectWithNewData }  
like image 22
Thilo Avatar answered Oct 09 '22 04:10

Thilo