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


当前回答

你可以做一个upsert,这个操作在MongoDB是用来保存文档到集合。 如果文档符合查询条件,则执行更新操作,否则将向集合中插入新文档。

类似如下所示

db.employees.update(
    {type:"FT"},
    {$set:{salary:200000}},
    {upsert : true}
 )

其他回答

从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.

我是这样成功做到的:

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

我有一个动态处理我的个人资料更新的函数

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

注意:'profile'是特定于我的实现,它只是你想修改的键的字符串。

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

to

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

希望这对你有所帮助!

您应该考虑交换地更新对象,然后简单地将对象与更新后的字段存储在一起。如下所示

function update(_id) {
    return new Promise((resolve, reject) => {

        ObjModel.findOne({_id}).exec((err, obj) => {

            if(err) return reject(err)

            obj = updateObject(obj, { 
                some_key: {
                    param2 : "val2_new",
                    param3 : "val3_new"
                }
            })

            obj.save((err, obj) => {
                if(err) return reject(err)
                resolve(obj)
            })
        })
    })
}


function updateObject(obj, data) {

    let keys = Object.keys(data)

    keys.forEach(key => {

        if(!obj[key]) obj[key] = data[key]

        if(typeof data[key] == 'object') 
            obj[key] = updateObject(obj[key], data[key])
        else
            obj[key] = data[key]
    })

    return obj
}

使用$set执行此过程

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