MongoDB聚合:$merge 阶段(1)

$merge的用途是把聚合管道产生的结果写入指定的集合,有时候可以用$merge来做物化视图。需要注意,$meger操作必须是聚合管道的最后一个阶段。具体功能有:

  • 能够输出到当前或不同的数据库
  • 能够输出到正在聚合的集合(慎重:可能会导致频繁的更新甚至死循环)
  • 可以在副本集的二级节点运行,前提是群集所有节点的featureCompatibilityVersion不小于4.4,且读选项允许二级读取。注意:
    • $merge读取操作会发送到二级节点,写入操作只发生在主节点。
    • 并非所有版本的驱动都支持$merge在副本集二级节点的操作,在使用前要确认驱动支持。
  • 输出集合不存在时可以自动创建输出集合,数据库不存在时也可以自动创建
  • 输出结果可以与现有集合合并,包括:插入新文档、合并文档、替换文档、保持已存在的文档、让操作失败、使用自定义的更新管道处理文档等。

语法

js 复制代码
{ $merge: {
     into: <collection> -or- { db: <db>, coll: <collection> },
     on: <identifier field> -or- [ <identifier field1>, ...], // 可选
     let: <variables>, // 可选
     whenMatched: <replace|keepExisting|merge|fail|pipeline>,  // 可选
     whenNotMatched: <insert|discard|fail>  //可选
} }

举例:

js 复制代码
{ $merge: {into: "mycollection", on: "_id", whenMatched: "replace", whenNotMatched: "insert" } }

如果都使用$merge的默认选项并且输出到当前数据库,可以使用简化形式:

js 复制代码
{ $merge: <collection> } //输出到当前数据库

字段说明

into

输出到的集合,可以指定到数据库,也可以不指定数据库,输出到当前数据库。例如:

输出到当前库的my_coll集合:into: "my_coll"

输出到mydbmy_coll集合:into:{db:"mydb", coll:"my_coll"}

注意:

  • 如果目标库或集合不存在,会自动创建
  • 如果是共享的群集,目标集合必须要存在
  • 输出集合也可以是共享集合
on

可选字段,可以指定一个或多个字段作为判断文档唯一性的ID,用于匹配目标集合中是否已经存在相同ID的文档。

举例:

  • 一个字段:on: "_id"
  • 多个字段:on: ["date", "name"]

注意:

  • 如果指定了on字段,除非on_id,否则在聚合结果文档中必须存在on中指定的字段,如果聚合结果中没有_id字段,会自动添加一个。
  • on指定的字段的值不允许为空或数组。
  • $merge必须要有一个包含on字段的唯一索引,索引键顺序无关紧要。
    • 索引必须有跟聚合集合同样的集合
    • 唯一索引必须是一个稀疏索引
    • 唯一索引不能是部分索引
    • 对已经存在的输出集合,相应的索引必须已经存在
  • on的缺省值依赖于输出集合:
    • 如果输出集合不存在,on的标识符必须且缺省为_id字段,相应的唯一索引_id是被自动创建的。
    • 如果输出集合是已经存在的分片集合,on标识符缺省为_id字段
    • 如果输出集合是已经存在的非分片集合,on标识符缺省为所有分片键值字段和_id字段,如果指定了一个不同的on标识符,on必须包含所有的分片键值字段。
whenMatched

可选字段,指定当输出集合中存在与on字段指定的键值相同的文档时的处理方式,可以为以下值:

"replace" 替换

使用聚合结果替换已经存在的文档。当执行替换的时候不能修改对应文档_id字段的值,如果输出集合是分片集合,也不能修改分片键值,否则操作会产生错误。为了避免这个错误,如果on字段中没有包含_id字段,可以从聚合结果中移除_id字段以避免这个错误,比如可以使用类似$unset的阶段预先处理以下。

"keepExistin" 保留已存在的

不替换已经存在的文档

"merge" 合并

缺省值,合并匹配的文档,类似于$mergeObjects操作

  • 如果聚合结果文档的字段在目标文档中不存在,就添加
  • 如果聚合结果文档的字段在目标文档中已存在,则替换

举例:

如果目标集合有一个文档:

json 复制代码
{_id: 1, a: 1, b: 1 }

聚合结果的文档是:

json 复制代码
{ _id: 1, b: 5, z: 1 }

则合并后的文档是:

json 复制代码
{ _id: 1, a: 1, b: 5, z: 1 }

"replace"类似,合并的时候"_id"字段或分片键值是不能被修改的。

"fail" 失败

停止并且报错,之前所有的输出和更改都不能撤销。

使用聚合管道更新输出集合文档

on指定的键值相同时,使用一个聚合管道更新输出集合的文档,如:

js 复制代码
[ <stage1>, <stage2> ... ]

但是,管道只能包含下面的阶段:

  • $addFields及其别名$set
  • $projecct及其别名$unset
  • $replaceRoot及其别名$replaceWith

管道不能修改on涉及字段的值,比如匹配字段year,管道是不能修改year字段值的。另外whenMatched管道可以使用$<field>直接访问输出文档的字段。如果要在管道中访问聚合结果文档(就是输入文档)的字段,可以使用下面两种方式:

  • 使用内置的$$new变量来访问字段,就是$$new.<field>$$new变量只能在省略let时才能使用。
  • let字段使用用户自定义的变量。以$$符号为前缀指定变量名$$<variable_name>,如:$$year。如果变量是文档,也可以包含文档字段,格式为$$<变量名>.<字段>。例如,$$year.month
let

可选字段,为whenMatched的管道指定变量。可以指定文档的变量名和表达式:

js 复制代码
{ <variable_name_1>: <expression_1>,
  ...,
  <variable_name_n>: <expression_n> }
whenNotMatch

可选字段,决定了$merge在输出文档匹没有配到对应文档的情况,可以指定下面的预定义的字符串常量:

  • "insert"插入,缺省值,将聚合后的文档插入到输出集合。
  • discard丢弃,就是不向输出集合中插入文档。
  • fail失败,停止并宣告聚合操作失败,之前已经在输出集合中写入或修改的文档不能回滚。
相关推荐
Leo.yuan8 分钟前
数据量大Excel卡顿严重?选对报表工具提高10倍效率
数据库·数据分析·数据可视化·powerbi
Runing_WoNiu16 分钟前
MySQL与Oracle对比及区别
数据库·mysql·oracle
天道有情战天下37 分钟前
mysql锁机制详解
数据库·mysql
看山还是山,看水还是。40 分钟前
Redis 配置
运维·数据库·redis·安全·缓存·测试覆盖率
谷新龙00143 分钟前
Redis运行时的10大重要指标
数据库·redis·缓存
CodingBrother44 分钟前
MySQL 中单列索引与联合索引分析
数据库·mysql
精进攻城狮@1 小时前
Redis缓存雪崩、缓存击穿、缓存穿透
数据库·redis·缓存
小酋仍在学习1 小时前
光驱验证 MD5 校验和
数据库·postgresql
keep__go1 小时前
Linux 批量配置互信
linux·运维·服务器·数据库·shell
小王同学mf1 小时前
怎么尽可能保证 Kafka 的可靠性
数据库