如何部分更新MongoDB中的对象,以便新对象与现有对象叠加/合并

鉴于这个文件保存在MongoDB中

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

需要保存来自外部的具有关于param2param3新信息的对象

 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 ,然后更新它(使用旧的值作为匹配的filter,以确保你没有获得并发的更新)。


你的问题的另一个解读是你对$set感到满意,但不想明确设置所有的字段。 那么你将如何传递数据呢?

你知道你可以做到以下几点:

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

我用自己的function解决了它。 如果要更新文档中的指定字段,则需要清楚地说明。

例:

 { _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允许你使用a来更新嵌套文件. 惯例。 看看: 在MongoDB中更新嵌套的文档 。 以下是关于合并更新的另一个问题,比如你正在寻找的合并更新:我相信: 通过“合并”文档的MongoDBprimefaces更新

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

鉴于上面指定的对象:

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

我们可以更新some_key.param2some_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" } } 

你可以深入钻研。 这对于在不影响现有对象的情况下向对象添加新的属性也是有用的。

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

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

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

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

然后它形成完美的select器 – 它将更新只给定的属性。 编辑:我喜欢成为考古学家;)

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

我这样做是成功的:

 db.collection.update( { _id:...} , { $set: { 'key.another_key' : new_info } } ); 

我有一个function,dynamic处理我的个人资料更新

 function update(prop, newVal) { const str = `profile.${prop}`; db.collection.update( { _id:...}, { $set: { [str]: newVal } } ); } 

注意:“configuration文件”是特定于我的实现,它只是您想要修改的密钥的string。

使用$ set做这个过程

 .update({"_id": args.dashboardId, "viewData._id": widgetId}, {$set: {"viewData.$.widgetData": widgetDoc.widgetData}}) .exec() .then(dashboardDoc => { return { result: dashboardDoc }; });