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


当前回答

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

例子:

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

其他回答

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

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

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


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

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

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

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

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

这里有更多的例子

是的,最好的方法是将对象表示法转换为平面键值字符串表示法,就像这个评论中提到的:https://stackoverflow.com/a/39357531/2529199

我想强调使用NPM库的另一种方法:https://www.npmjs.com/package/dot-object,它允许你使用点表示法操作不同的对象。

当接受key-value作为函数变量时,我使用这个模式以编程方式创建一个嵌套的对象属性,如下所示:

const dot = require('dot-object');

function(docid, varname, varvalue){
  let doc = dot.dot({
      [varname]: varvalue 
  });

  Mongo.update({_id:docid},{$set:doc});
}

这个模式允许我交替使用嵌套和单层属性,并将它们干净地插入到Mongo中。

如果你需要处理Mongo之外的JS对象,特别是在客户端,但在使用Mongo时要保持一致性,这个库比前面提到的Mongo -dot-notation NPM模块提供了更多的选项。

附:我最初只是想把这作为一个评论,但显然我的S/O代表不够高,不能发表评论。所以,我并不是想强行干涉SzybkiSasza的评论,只是想强调提供一个替代模块。

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

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

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

使用包含必要的点路径的属性名创建一个更新对象。(“somekey。+来自OP的例子),然后使用它进行更新。

//the modification that's requested
updateReq = { 
   param2 : "val2_new",
   param3 : "val3_new"   
}

//build a renamed version of the update request
var update = {};
for(var field in updateReq){
    update["somekey."+field] = updateReq[field];
}

//apply the update without modifying fields not originally in the update
db.collection.update({._id:...},{$set:update},{upsert:true},function(err,result){...});