给定此文档保存在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客户端,但是任何示例都将受到欢迎


当前回答

您可以在基于聚合的更新中使用$mergeObjects。类似的

db.collection.update(
   { _id:...},
   [{"$set":{
      "some_key":{
        "$mergeObjects":[
          "$some_key",
          new info or { param2 : "val2_new", param3 : "val3_new"}
       ]
      }
   }}]
)

这里有更多的例子

其他回答

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

例子:

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

Mongo允许您使用。公约。请看:更新mongodb中的嵌套文档。这是过去关于合并更新的另一个问题,就像你正在寻找的那样:MongoDB通过“合并”文档进行原子更新

您可以在基于聚合的更新中使用$mergeObjects。类似的

db.collection.update(
   { _id:...},
   [{"$set":{
      "some_key":{
        "$mergeObjects":[
          "$some_key",
          new info or { param2 : "val2_new", param3 : "val3_new"}
       ]
      }
   }}]
)

这里有更多的例子

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

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

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


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

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

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

从Mongo 4.2开始,db.collection.updateMany()(或db.collection.update())可以接受一个聚合管道,它允许使用聚合操作符,如$addFields,它输出输入文档中所有现有的字段和新添加的字段:

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

// { some_key: { param1: "val1", param2: "val2", param3: "val3" } }
// { some_key: { param1: "val1", param2: "val2"                 } }
db.collection.updateMany({}, [{ $addFields: { some_key: new_info } }])
// { some_key: { param1: "val1", param2: "val2_new", param3: "val3_new" } }
// { some_key: { param1: "val1", param2: "val2_new", param3: "val3_new" } }

The first part {} is the match query, filtering which documents to update (in this case all documents). The second part [{ $addFields: { some_key: new_info } }] is the update aggregation pipeline: Note the squared brackets signifying the use of an aggregation pipeline. Since this is an aggregation pipeline, we can use $addFields. $addFields performs exactly what you need: updating the object so that the new object will overlay / merge with the existing one: In this case, { param2: "val2_new", param3: "val3_new" } will be merged into the existing some_key by keeping param1 untouched and either add or replace both param2 and param3.