我正在尝试更新MongoDB集合中的用户文档。如果用户不存在,则应该创建它,但如果它已经存在,则应该只在查询解析某些检查时才更新。
这是我第一次尝试的:
var ts = new Date('2015-01-01T00:00:00.000Z');
var query = { name: 'bob', updatedAt: { $lt: ts } };
var update = { $set: { age: 42, updatedAt: ts }, $inc: { updates: 1 } };
db.users.update(query, update, { upsert: true });
该解决方案的问题在于,如果用户的updatedAt
属性小于给定的ts
日期,则会尝试创建用户两次。
如果查询的name
部分与任何文档不匹配,我怎样才能确保创建它,但如果updatedAt
没有做任何事情,我该怎么做?部分没有?
答案 0 :(得分:3)
基本上:
upset
和$setOnInsert
; update
。简单地说,您需要两个更新语句。但是,您可以将它们包装在同一个update command:
中db.runCommand({
update: 'users',
updates: [
{ q: { name: 'bob' },
u: { $setOnInsert: { updatedAt: now, updates: 0 }},
upsert: true },
{ q: { name: 'bob', updatedAt: { $lt: now } },
u: { $set: { updatedAt: now }, $inc: { updates: 1 } },
upsert: false },
]
})
第一次运行时生成(空集合):
> var now = new Date()
> db.runCommand({ update: 'users', updates: [ { q: { name: 'bob' }, u: { $setOnInsert: { updatedAt: now, updates: 0 }}, upsert: true }, { q: { name: 'bob', updatedAt: { $lt: now } }, u: { $set: { updatedAt: now }, $inc: { updates: 1 } }, upsert: false }, ] })
{
"ok" : 1,
"nModified" : 0,
"n" : 1,
"upserted" : [
{
"index" : 0,
"_id" : ObjectId("556f672cd418ed1506eb2ca3")
}
]
}
然后,再次运行时:
> var now = new Date()
> db.runCommand({ update: 'users', updates: [ { q: { name: 'bob' }, u: { $setOnInsert: { updatedAt: now, updates: 0 }}, upsert: true }, { q: { name: 'bob', updatedAt: { $lt: now } }, u: { $set: { updatedAt: now }, $inc: { updates: 1 } }, upsert: false }, ] })
{ "ok" : 1, "nModified" : 1, "n" : 2 }
请注意,更新命令不是原子的。但是,其他客户端无法将部分创建或更新的用户文档视为第一个更新语句创建完全填充的文档,或者它已经存在(并保持不变)直到第二个完全更新它的声明。
如果用户在两个语句之间同时创建,它甚至是安全的 - 如果需要,它将被透明地更新。