首页/数据/NoSQL 速查/聚合 $merge 增量写出
MongoDB99 条

聚合 $merge 增量写出

$merge 把管道结果合并回集合:按 on 字段匹配,支持 insert/merge/replace/keepExisting/fail 等冲突处理方式。

NoSQL 速查数据更新于 2026-10-13

$merge 的增量写出

$out 会清空目标集合再整体写入,适合一次性生成快照;$merge 则按指定字段匹配,逐条决定插入、合并还是替换,因此可以反复运行而不丢其他数据。做增量汇总、维表更新、指标回填这类任务时,$merge 是更合适的选择,代价是必须为匹配字段准备唯一性保证。

基本信息

语法{ $merge: { into, on, whenMatched, whenNotMatched } }
目标集合into 可写字符串,或用 { db, coll } 指定同实例内其他库的集合
匹配字段on 指定匹配字段,默认 _id,需要唯一索引支撑
whenMatchedmerge(默认,合并字段)/ replace / keepExisting / fail 或自定义管道
whenNotMatchedinsert(默认)/ discard / fail
位置要求必须是聚合管道最后一个阶段,与写入类阶段互斥

典型使用场景

  • 增量汇总:按维度累计计数与金额,任务可重复执行
  • 维表更新:把上游变更合并进维表,未变化的维度不受影响
  • 指标回填:重算某段时间的统计结果并合并回目标集合
  • 分批修复:只更新匹配到的文档,避免整体重写带来的风险
  • 多任务并存:多个汇总任务写同一个集合时,各自维护自己的字段

示例

示例
// 默认行为:匹配则合并字段,未匹配则插入
db.orders.aggregate([
  { $match: { ts: { $gte: ISODate("2026-10-01T00:00:00Z") } } },
  { $group: { _id: "$sku", qty: { $sum: "$qty" }, orders: { $sum: 1 } } },
  { $merge: { into: "sku_stats", on: "_id", whenMatched: "merge", whenNotMatched: "insert" } }
])

// 只更新指定字段:whenMatched 用管道自定义合并逻辑
// db.orders.aggregate([
//   { $group: { _id: "$sku", qty: { $sum: "$qty" } } },
//   { $merge: { into: "sku_stats", on: "_id", whenMatched: [ { $addFields: { qty: "$$new.qty", updatedAt: "$$NOW" } } ] } }
// ])

冲突处理选项读法

选项行为适用
merge匹配时把新字段合并进已有文档(默认)增量汇总,保留目标集合的其他字段
replace用新文档整体替换已有文档结果字段完全由本次计算决定
keepExisting匹配时保留已有文档不变只新增不修改的补数场景
fail匹配到就报错终止校验前提条件,用于流程断言
whenNotMatched: discard未匹配时不插入只更新已存在记录
whenNotMatched: fail未匹配时终止任务要求目标集合必须已有对应记录
into 可以写成 { db, coll } 指定同实例内其他库的集合,跨库汇总时不必先导出再导入;但跨实例仍需外部链路搬运。
on 字段需要唯一性保证:目标集合上缺少唯一索引时,匹配语义可能不符合预期,建议为目标集合的匹配字段建唯一索引兜底。

易错点

  • 把 $merge 当 $out 使用:$out 会整体替换目标集合,容易抹掉其他任务写入的字段
  • on 字段没有唯一索引:匹配到多条时行为不符合预期,结果难以解释
  • 重复执行同一批任务不做设计:merge 语义是幂等的,但 replace 语义会覆盖其他来源的更新,选错选项后果不同
  • 把 $merge 放在管道中间:该阶段必须是最后一步,放错位置直接报错

数据来源与参考

  • MongoDB Manual: $merge
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌