给定此文档保存在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'));
您可以使用点符号来访问和设置对象内部深处的字段,而不会影响这些对象的其他属性。
给定你上面指定的对象:
> 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"
}
}
你想挖多深就挖多深。这对于在不影响现有属性的情况下向对象添加新属性也很有用。
最好的解决方案是从对象中提取属性,并使它们成为平面点表示法键值对。例如,你可以使用这个库:
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
}
}
*/
然后它形成完美选择器-它只会更新给定的属性。
编辑:有时我喜欢成为考古学家;)
// where clause DBObject
DBObject query = new BasicDBObject("_id", new ObjectId(id));
// modifications to be applied
DBObject update = new BasicDBObject();
// set new values
update.put("$set", new BasicDBObject("param2","value2"));
// update the document
collection.update(query, update, true, false); //3rd param->upsertFlag, 4th param->updateMultiFlag
如果您有多个字段要更新
Document doc = new Document();
doc.put("param2","value2");
doc.put("param3","value3");
update.put("$set", doc);
是的,最好的方法是将对象表示法转换为平面键值字符串表示法,就像这个评论中提到的: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的评论,只是想强调提供一个替代模块。
从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.