mongodb - 是否可以使用聚合管道进行 $setOnInsert ?
问题描述
MongoDB 最近添加了一个选项,update
通过提供聚合管道而不是标准修饰符对象来执行操作。检查 MongoDB 的有关此主题的文档。
使用聚合管道(其语句可以引用现有文档属性)的能力在需要基于其他字段评估某些字段的情况下非常有用,例如在数据迁移期间。
此外,大多数标准更新运算符,如$set
, $push
,$inc
等都可以使用聚合表达式语言成功复制,因此在某种意义上,这个新功能概括了良好的旧修饰符技术。不过,我必须承认,如果尝试执行诸如$addToSet
. 这当然会带来一大堆与性能相关的问题,但现在让我们忽略它们。
到目前为止,只有一件事我无法通过聚合管道更新完全复制,即$setOnInsert
操作员。假设我要执行 upsert:
db.test.update(selector, pipeline, { upsert: true });
我最初的直觉是,除非存在匹配的文档,否则$$ROOT
变量(我可以在 中使用pipeline
)将相等。不幸的是,但可能有一个很好的理由,MongoDB 开发人员决定默认情况下应该从它派生。当您考虑正常的工作原理时,这是有道理的,但它也使得实际上无法区分 update 和 insert within 。null
selector
$$ROOT
selector
$setOnInsert
pipeline
我知道你在想什么。你可以看看$$ROOT._id
。这是一个好主意,尽管如果它_id
是其中的一部分,selector
它就不再起作用了。我发现这可以通过稍微欺骗 MongoDB 并执行以下操作来绕过:
selector = {
_id: { $in: [value, 'fake'] },
}
相反,如果更简单{ _id: value }
,但这看起来并不干净。请注意,如果$in
只包含一个元素,那么 Mongo 实际上足够聪明,可以找出标识符应该是什么并$$ROOT
相应地填充(原文如此!)。
我想知道是否有人对如何解决这个问题有更好的想法。也许有一些隐藏变量我可以在其内部使用pipeline
来区分update
和insert
(例如,在$merge
阶段有$$new
用于类似目的的变量)?
解决方案
如果没有匹配的文件,$$ROOT
将只有_id
字段。因此,您可以$$ROOT
通过其键/值对转换为数组,并检查该数组的大小是否等于 1。如果是则创建一个新文档,如果不是则什么也不做。
$objectToArray
并通过其键/值对$size
将$$ROOT转换为数组并获取该数组的大小$cond
检查上面数组的大小是否等于 1。如果是,则将当前$$ROOT(仅_id字段)与更新对象合并。如果不是,则返回当前的$$ROOT。在这两种情况下,将结果放入结果字段。$mergeObjects
合并$$ROOT和您发送的更新,并将其放在结果字段中$replaceRoot
将根替换为上一阶段的结果字段
db.collection.update({
_id: 1
},
[
{
$set: {
result: {
$cond: {
if: {
"$eq": [
{
$size: {
$objectToArray: "$$ROOT"
},
},
1
]
},
then: {
$mergeObjects: [
"$$ROOT",
{
key: 3
}
]
},
else: "$$ROOT"
},
}
}
},
{
$replaceRoot: {
newRoot: "$result"
}
}
],
{
upsert: true
})
推荐阅读
- android - Qt和Android - 从相机返回的handleActivityResult,uri null
- c++ - 在 mac os 上的 Visual Studio 中获取外部库 C++ 代码的包含错误
- c# - 在 Linux Docker 中使用 .Net 标准库运行 .Net Core 应用程序
- ios - 如何通过 SecureEnclave 持久存储加密数据
- php - Laravel 默认登录身份验证生成意外错误
- javascript - 将国家/地区从 json 文件填充到 angular2-multiselect
- python - (discord.py)我怎样才能让我的机器人在当前命令完成之前不回复其他命令
- xpath - XPath 仅包含整个单词
- node.js - Vue/Node:由于 pkcs 错误而无法安装 Video.js
- python - 如何在 django 一天中的特定时间更新一些信息?