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

Eran Medan picture Eran Medan · Apr 24, 2012 · Viewed 196.8k times · Source

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

Answer

mehmatrix picture mehmatrix · Apr 20, 2013

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'));