首页/数据/NoSQL 速查/聚合 $replaceWith 重塑文档
MongoDB99 条

聚合 $replaceWith 重塑文档

$replaceWith 用指定对象替换输出文档结构(旧名 $replaceRoot + newRoot),配合 $arrayElemAt 可把数组元素提升为文档。

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

是什么

$replaceWith 用表达式求出的文档整体替换输出的根文档,旧名写法是 $replaceRoot 配 newRoot。它最常见的用法是把 $unwind 或 $arrayElemAt 取出的子文档提升为顶层结构,让后续阶段直接按子文档的字段路径书写;也可以用 $mergeObjects 把新算出的字段与原文档拼在一起再当作根。

基本信息

新写法{ $replaceWith: <表达式> }(MongoDB 4.2+)
旧写法{ $replaceRoot: { newRoot: <表达式> } }(3.4+)
典型组合$unwind、$arrayElemAt、$mergeObjects、$$ROOT
效果替换后字段路径随之改变,后续阶段按新结构写
注意表达式求值为数组或字段缺失时可能出错,先过滤不满足条件的文档

典型使用场景

  • 展开数组后按元素统计:$unwind 明细数组再重塑,管道里就不用写「数组.字段」这种长路径
  • 保留原文档并追加字段:用 $mergeObjects 把新字段与 $$ROOT 合并,既有新信息又不丢原字段
  • 取数组首元素:用 $arrayElemAt 取出主联系人、主地址等元素作为根
  • 嵌套结构拍平:把深层子文档提升一层,简化后续匹配与排序
  • 多集合合并输出统一结构:不同来源重塑成同一套字段后统一写入

重塑文档结构

重塑文档结构
db.orders.aggregate([
  { $match: { "items.0": { $exists: true } } },      # 先排除明细为空、重塑会出问题的文档
  { $unwind: "$items" },                            # 展开明细数组
  { $replaceWith: "$items" },                       # 明细子文档成为新的根
  { $sort: { qty: -1 } }                            # 之后直接按明细字段排序
])

db.users.aggregate([                                # 保留原字段并追加计算字段
  { $replaceWith: { $mergeObjects: [ { nameUpper: { $toUpper: "$name" } }, "$$ROOT" ] } },
  { $limit: 5 }
])

db.users.aggregate([                                # 取数组首元素作为根
  { $match: { contacts: { $ne: [] } } },
  { $replaceWith: { $arrayElemAt: ["$contacts", 0] } }
])

相关写法对照

写法作用版本
$replaceWith: expr表达式结果整体作为新根4.2+
$replaceRoot: { newRoot: expr }旧写法,效果相同3.4+
$mergeObjects: [新增字段, "$$ROOT"]保留原文档并追加字段3.6+
$arrayElemAt: ["$arr", 0]取数组首个元素作为新根3.2+
重塑之后所有的字段路径都变了:前面写的 $match 用旧路径,后面的阶段要用新路径,管道里混用会得到空结果或报错,写完最好先用 $limit 小样验证。
表达式求值为数组、或目标字段缺失的类型不满足要求时会出错,而错误是逐文档发生的:先用 $match 把不合格文档挡在前面,比事后排查半截失败的结果成本低得多。

易错点

  • 忘记重塑会改变字段路径:后续阶段继续用旧路径,结果为空却难以察觉
  • 对空数组做 $arrayElemAt:取不到元素会让整个文档出错,重塑前要过滤掉空值文档
  • 用 $replaceWith 替换后丢失文档主键:下游还要靠 _id 关联时,要显式把它拼回新结构
  • $mergeObjects 的顺序写反:后面的对象会覆盖前面的字段,新算出的字段反被原文档同名值盖掉

数据来源与参考

  • MongoDB Manual: $replaceWith
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌