$merge 的增量写出
$out 会清空目标集合再整体写入,适合一次性生成快照;$merge 则按指定字段匹配,逐条决定插入、合并还是替换,因此可以反复运行而不丢其他数据。做增量汇总、维表更新、指标回填这类任务时,$merge 是更合适的选择,代价是必须为匹配字段准备唯一性保证。
基本信息
| 语法 | { $merge: { into, on, whenMatched, whenNotMatched } } |
|---|---|
| 目标集合 | into 可写字符串,或用 { db, coll } 指定同实例内其他库的集合 |
| 匹配字段 | on 指定匹配字段,默认 _id,需要唯一索引支撑 |
| whenMatched | merge(默认,合并字段)/ replace / keepExisting / fail 或自定义管道 |
| whenNotMatched | insert(默认)/ 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 字段需要唯一性保证:目标集合上缺少唯一索引时,匹配语义可能不符合预期,建议为目标集合的匹配字段建唯一索引兜底。