考虑以下代码:
findAndModify({id: id}, undefined, {$inc: {counter: 1}, {$max: {counter: 10})
此操作会失败并出现错误,因为
$inc
和 $max
都尝试更新同一字段。
但是,如果我想在计数器的值小于 10 时将其设置为 10,如果不是,则将其值增加 1,该怎么办?我如何在一个原子操作中做到这一点?
有没有办法在一次操作中按顺序应用更新?
我认为这不可能通过一次操作来完成。但是,您可以使用条件语句创建聚合查询,然后在更新中使用。
(async () => {
try {
const results = await Model.aggregate([
{ '$match': { 'id': id } },
{ '$project': {
'counter': {
'$cond': [
{ '$lt': ['$counter', 10 ] },
10,
{ '$add': ['$counter', 1 ] }
]
}
} }
]).exec();
const data = results[0];
const { counter } = data;
const doc = await Model.findOneAndUpdate({ id },
{ '$set': { counter } },
{ new: true }
).exec();
console.log(doc);
}
catch (err) {
console.error(err);
}
})()
对于原子更新,包括一个查询,该查询限制仅具有
counter
小于给定上限值 10 的文档的增量:
findAndModify(
{
'id': id,
'counter': { '$lt': 10 }
}, undefined,
{ '$inc': { 'counter': 1 } },
callback
)
添加 MongoDB 最新版本的答案。 4.4 的网络存档显示相同。
update()
与聚合管道一起使用。使用它,更新变得原子,因为只有一个条件适用,而不是两者都适用。
使用
$cond
,并检查 counter < 10
,更新操作看起来与之前的旧答案类似,但作为一个操作。请注意在 update 子句周围使用 [ ... ]
以使用该命令的管道版本;它允许引用文档值和其他变量。
db.collection.update({
_id: 1
},
[
{
$set: {
counter: {
$cond: {
if: { $lt: ["$counter", 10] },
then: 10,
else: { $add: ["$counter", 1] }
}
}
}
}
])
这可以转换为类似于现有更新查询的内容,使用
$max
和 $add
并给出相同的结果。在这里,从 (10, current + 1)
中选择最大值:
1
将得到小于 10 的结果;所以选择了10个1
的从 10 到 +无穷大的数字将得到大于 10 的结果,因此选择 +1
值9
,比较是在 10 和 10 (9+1) 之间,所以是 10。db.collection.update({
_id: 1
},
[
{
$set: {
counter: { $max: [{ $add: ["$counter", 1] }, 10] }
}
}
])
要对每个文档执行一次更新查询,请从查询参数中删除
_id
并将其保留为所有文档 {}
,如链接的 Playground 中所做的那样。