我的文档中有一个字段可能为空;我想更新该文档的内部字段,如果该字段为空,则将该字段创建为对象。作为演示:
假设文档如下所示:
{
id: "xyz",
name: null
}
db.update({id: "xyz"}, {$set: {"name.first": "Joe"}})
现在,在运行时我不知道名称字段是否为空。如果
name
为空,我只想将 { first: "Joe" }
更新为 name
。
我实际上是通过 Mongoose 模型来完成此操作,其架构如下:
{
name: {
default: null,
type: {
first: {
type: String
},
middle: {
type: String,
},
last: {
type: String,
},
display: {
type: String,
}
}
}
}
有没有一种方法可以在一个查询中执行更新?或者我需要分两步完成(查询以获取
name
的当前值,然后对其进行扩充并设置)?
编辑:这是我在控制台中运行它时得到的结果
> db.users.findAndModify({ query: { _id: ObjectId("596958d60782a56eb3ba3d93") }, update: { '$set': { 'name.display': 'dfgdfgfgf' } }})
2017-07-14T20:13:45.855-0400 findAndModifyFailed failed: {
"value" : {
"_id" : ObjectId("596958d60782a56eb3ba3d93"),
"sessionID" : "2bPzS2lqkDxN-3XEQRw60GAB93XODi4Y",
"profilePhotoURL" : null,
"name" : null,
"__v" : 0
},
"errmsg" : "exception: cannot use the part (name of name.display) to traverse the element ({name: null})",
"code" : 16837,
"ok" : 0
} at src/mongo/shell/collection.js:614
> db.users.update({ _id: ObjectId("596958d60782a56eb3ba3d93") }, {$set: {"name.display": "sdfsdfsfd"}})
WriteResult({
"nMatched" : 0,
"nUpserted" : 0,
"nModified" : 0,
"writeError" : {
"code" : 16837,
"errmsg" : "cannot use the part (name of name.display) to traverse the element ({name: null})"
}
})
我最近偶然发现了同样的问题,并且我设法使用从版本 4.2 开始随 Mongodb 附带的聚合管道更新功能和$ifNull运算符解决了该问题。
举个例子,如果我有以下文档:
db.test.insertOne({ _id: "foo", a: null })
我想将a设置为一个对象,例如:
a: { b: 1 }
您可以使用以下语句:
db.test.updateOne({_id: "foo"}, [{$set: {'a.b': { $ifNull: [1, 1] } } }])
这样做的目的是,如果 'a.b' 为空或不为空,它将用值 1 更新它。这种方法无需事先知道该字段是否为空或是否已初始化。
使用 MongoDB,您可以使用 findAndModify(),它允许您执行查询并仅更新匹配的文档。
该操作是原子执行的,因此一旦发现没有其他操作可以读取文档,直到更新发生。这在其他情况下很有用。这也是为什么您可以在 API 中调整
writeConcern
。
示例:
db.collection.findAndModify({
query: {
name: null
},
update: {
name: { first: "Joe" }
}
})