我收集foo
文件,如:
{site_id: 'xxx', title: {ru: 'a', en: 'b'}, content: {ru: 'a', en: 'b'}}
{site_id: 'xxx', title: {ru: 'c', de: 'd'}, content: {ru: 'c', de: 'd'}}
我需要更新可能存在或不存在的多个字段:
db.foo.update(
{ site_id: 'xxx'},
{ $set: {'title.de': '', 'content.de': ''}},
{multi: true}
)
但我需要像$set
这样的东西,如果它存在,它将不会覆盖价值。
您可以向update语句添加查询:
db.foo.update({'title.de': {$exists : false}}, {$set: {'title.de': ''}})
对于您修改过的问题,我的解决方案看起来像这样 - 这对您有用吗? (如果不是,为什么?)
db.foo.update({site_id: 'xxx', 'title.de': {$exists : false}}, {$set: {'title.de': ''}, {multi: true})
db.foo.update({site_id: 'xxx', 'content.de': {$exists : false}}, {$set: {'content.de': ''}}, {multi: true})
有一个更新字段运算符$ setOnInsert,它符合您的要求。请阅读这里的文件:https://docs.mongodb.com/manual/reference/operator/update/setOnInsert/#up._S_setOnInsert
我有一个特定案例的解决方案,但可能对某人有所帮助。
我的情况是:更新几个字段,其中一个字段必须只更新一次(让我们称之为“Date_of_first_update”)。
> db.test.find();
{ "_id" : ObjectId("57f298fdeb30478a033c70e4"), "a" : "1", "b" : "2" }
First update:
> db.test.updateOne({ "_id" : ObjectId("57f298fdeb30478a033c70e4")},
{$set: {a: 100, b: 200 }, $min : {'Date_of_first_update' : (new Date()) }});
Result: 'a', 'b' updated, 'Date_of_first_update' is set.
{ "_id" : ObjectId("57f298fdeb30478a033c70e4"), "a" : 100, "b" : 200, "Date_of_first_update" : ISODate("2016-10-03T**17:47:43**.570Z") }
Second update:
> db.test.updateOne({ "_id" : ObjectId("57f298fdeb30478a033c70e4")},
{$set: {a: 400, b: 800 }, $min : {'Date_of_first_update' : (new Date()) }});
Result: 'a', 'b' updated, 'Date_of_first_update' left unchanged, as I needed!!!
{ "_id" : ObjectId("57f298fdeb30478a033c70e4"), "a" : 400, "b" : 800, "Date_of_first_update" : ISODate("2016-10-03T**17:47:43**.570Z") }
尽管给出的答案基本上概述了该方法,但由于支持“批量更新”的实现,您可以使用MongoDB 2.6或更高版本执行此类操作。
从原子角度讲,这仍然是单独的更新语句。但你可以一次性“通过网络”提交它们。这至少可以确保更新之间的延迟在服务器上执行时要短得多:
var bulk = db.foo.initializeBulkOrderedOp();
bulk.find({ "site_id": "xxx",
"title.de": { "$exists" false } })
.update({ "$set": { "title.de": "" } });
bulk.find({ "site_id": "xxx",
"content.de": { "$exists" false } })
.update({ "$set": { "content.de": "" } });
bulk.execute();
所以这实际上是一次往返服务器,因为一切只发送到.execute()
但是在您目前的形式中(尽管这可能不是您数据的准确表示),您实际上可以“重新构造”以便在单个操作中执行此操作。所以如果你的文件看起来像这样:
{
"site_id": "xxx",
"docs": [
{ "title": "a", "content": "a", "lang": "ru" },
{ "title": "b", "content": "b", "lang": "en" }
]
},
{
"site_id": "xxx",
"docs": [
{ "title": "c", "content": "c", "lang": "ru" },
{ "title": "d", "content": "d", "lang": "de" }
]
}
然后,以下工作由$addToSet
规则,其中“set”元素将是“唯一的”:
db.foo.update(
{ "site_id": "xxx" },
{ "$addToSet": { "docs": { "title": "d", content: "d", "lang": "de" } } },
{ "multi": true }
)
或者甚至没有那里的逻辑,只是检查存在:
db.foo.update(
{ "site_id": "xxx", "docs.lang": { "$ne": "de" } },
{ "$push": { "docs": { "title": "", "content": "", "lang": "de" } } },
{ "multi": true }
)
在最后一种情况中会导致这种情况:
{
"_id" : ObjectId("53c936265117367f5ff2038b"),
"site_id" : "xxx",
"docs" : [
{
"title" : "a",
"content" : "a",
"lang" : "ru"
},
{
"title" : "b",
"content" : "b",
"lang" : "en"
},
{
"title" : "",
"content" : "",
"lang" : "de"
}
]
}
{
"_id" : ObjectId("53c936265117367f5ff2038c"),
"site_id" : "xxx",
"docs" : [
{
"title" : "c",
"content" : "c",
"lang" : "ru"
},
{
"title" : "d",
"content" : "d",
"lang" : "de"
}
]
}
因此,选择是以不同方式“处理”事物,或者只是改变您的模式以适应您想要原子化的更新类型。
@ nutlike的答案确实解决了这个问题,但是,如果你想更新项目上的多个字段,它将需要许多数据库操作。简而言之,你想要的并不是完全可能的。
如果你的文档有更多的更新要做,而不是你想一次做一个(任何大于2,IMO),那么你应该只获取文档,更新字段然后保存它。这是我在几个OAuth用户创建/更新路由时所做的。