给定此文档保存在MongoDB中

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

需要保存具有来自外部世界的关于param2和param3的新信息的对象

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

我想在对象的现有状态上合并/覆盖新字段,这样param1就不会被删除

这样做

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

将导致MongoDB完全按照要求执行,并将some_key设置为该值。更换旧的。

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

如何让MongoDB只更新新字段(而不显式地逐个声明它们)?要得到这个:

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

我正在使用Java客户端,但是任何示例都将受到欢迎


当前回答

如果我理解正确的话,您希望用另一个文档的内容更新一个文档,但只更新尚未出现的字段,并完全忽略已经设置的字段(即使是另一个值)。

在单个命令中不可能做到这一点。

您必须首先查询文档,找出您想要$set的内容,然后更新它(使用旧值作为匹配过滤器,以确保您不会在两者之间获得并发更新)。


对你的问题的另一种解读是,你对$set很满意,但不想显式地设置所有字段。那么如何传递数据呢?

你知道你可以做以下事情:

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

其他回答

我用自己的函数解出来了。如果你想更新文档中的指定字段,你需要清楚地解决它。

例子:

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

如果你只想更新param2,这样做是错误的:

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

你必须使用:

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

所以我写了一个这样的函数:

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

您可以使用点符号来访问和设置对象内部深处的字段,而不会影响这些对象的其他属性。

给定你上面指定的对象:

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

我们可以只更新some_key。Param2和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"
    }
}

你想挖多深就挖多深。这对于在不影响现有属性的情况下向对象添加新属性也很有用。

看起来你可以设置isPartialObject,这可能完成你想要的。

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

to

db.collection.update( { _id: ..} , { $set: { some_key: { param1: newValue} } } ); 

希望这对你有所帮助!

最好的解决方案是从对象中提取属性,并使它们成为平面点表示法键值对。例如,你可以使用这个库:

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

它有.flatten函数,允许您将对象更改为平坦的属性集,然后可以给$set修饰符,而不用担心您现有DB对象的任何属性将被删除/覆盖而不需要。

摘自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
  }
}
*/

然后它形成完美选择器-它只会更新给定的属性。 编辑:有时我喜欢成为考古学家;)