【发布时间】:2021-10-29 14:11:02
【问题描述】:
MongoDB 最近添加了一个选项,通过提供聚合管道而不是标准修饰符对象来执行 update 操作。 Check MongoDB's docs on this topic.
使用聚合管道(其语句可以引用现有文档属性)的能力在需要根据其他字段评估某些字段的情况下非常有用,例如在数据迁移期间。
此外,大多数标准更新运算符,如$set、$push、$inc 等都可以使用聚合表达式语言成功复制,因此在某种意义上,这个新功能概括了良好的旧修饰符技术。不过,我必须承认,如果尝试执行$addToSet 之类的操作,管道可能会变得非常冗长。这当然会带来一大堆与性能相关的问题,但我们现在先忽略它们。
到目前为止,只有一件事我无法通过聚合管道更新完全复制,即$setOnInsert 运算符。假设我要执行 upsert:
db.test.update(selector, pipeline, { upsert: true });
我最初的直觉是$$ROOT 变量(我可以在pipeline 中使用)将等于null,除非存在与selector 匹配的文档。不幸的是,MongoDB 开发人员认为$$ROOT 应该默认派生自selector,这可能是有充分理由的。当您考虑$setOnInsert 的正常工作方式时,这是有道理的,但它实际上也使得在pipeline 中区分更新和插入几乎是不可能的。
我知道你在想什么。你可以看看$$ROOT._id。这是一个好主意,但如果_id 是selector 的一部分,它就不再起作用了。我发现这可以通过稍微欺骗 MongoDB 并执行以下操作来绕过:
selector = {
_id: { $in: [value, 'fake'] },
}
而不是更简单的{ _id: value },但这看起来并不干净。请注意,如果 $in 只包含一个元素,那么 Mongo 实际上足够聪明,可以确定标识符应该是什么,并相应地填充 $$ROOT(原文如此!)。
我想知道是否有人对如何解决这个问题有更好的想法。也许我可以在 pipeline 本身内部使用一些隐藏变量来区分 update 和 insert(例如,在 $merge 阶段有 $$new 变量用于类似目的)?
【问题讨论】:
标签: mongodb mongodb-query aggregation-framework aggregation