find() 仅返回原始文档,无法重包装结构;$replaceroot 要求输入为对象且易报错,$replacewith 更灵活安全,支持字面量、字段路径及数组处理,推荐在聚合中用于外层包装如 {list: [...]}.

为什么直接用 find() 无法按需格式化嵌套 JSON 层级
MongoDB 的 find() 返回的是原始文档集合,字段名、嵌套深度、甚至顶层结构都完全继承自存储时的 BSON 文档。如果你希望输出形如 {"data": [{"name": "a"}, {"name": "b"}]} 这样的结构,而不是默认的 [{"_id": ..., "name": "a"}, ...],find() 本身不提供重包装能力 —— 它只做“取”,不做“塑形”。
$replaceRoot 和 $replaceWith 的核心区别在哪
两者都能替换文档根节点,但语义和使用约束不同:
-
$replaceRoot要求输入必须是单个对象(不能是数组),且新根必须来自$newRoot表达式,例如:{$replaceRoot: {newRoot: "$user.profile"}}; -
$replaceWith更灵活:支持任意表达式,包括字面量对象、字段路径、甚至聚合操作结果,且能直接处理数组展开场景,例如:{$replaceWith: {data: "$$ROOT"}}或{$replaceWith: {data: "$items"}}。
Go / Node.js 驱动中推荐优先用 $replaceWith,因为 $replaceRoot 在遇到空字段或非对象值时容易报错 "newRoot must be an object"。
如何用 $replaceWith 包裹查询结果为 {"list": [...]}
假设你有一组用户文档,想统一包进 list 字段返回:
DMap GL地图JSAPI(dmapgl)开发指南。用于编写、审查、调试 DMap 地图 API 代码,涵盖地图初始化、覆盖物渲染、图层管理、事件处理、控件交互等任务。当用户提及 dmap、dmapgl、jsapi‑gl 或相关地图开发需求时自动触发。
[
{$replaceWith: {list: "$$ROOT"}},
{$group: {_id: null, list: {$push: "$list"}}},
{$replaceWith: "$list"}
]
但更简洁的做法是跳过 $group,直接在应用层构造外层结构;若必须在聚合内完成,推荐:
- 先用
$facet或$group收集所有文档到一个数组; - 再用
$replaceWith替换为带键的对象:{$replaceWith: {list: "$results"}}; - 注意:如果输入是空数组,
$replaceWith不会报错,但结果会是{list: []},符合预期。
嵌套字段提升 + 外层包装的典型组合写法
常见需求:把 users.address.city 提升为顶层 city,同时整个结果包进 payload 字段:
[
{$project: {city: "$address.city", name: 1, _id: 0}},
{$replaceWith: {payload: "$$ROOT"}}
]
这里 $project 先做字段投影和重命名,$replaceWith 再套一层;不能反过来,因为 $replaceWith 后文档结构已变,$project 就失效了。
容易踩的坑:$replaceWith 不接受字符串字面量(如 "$$ROOT" 加引号就错),必须是无引号表达式;$$ROOT 是变量,不是字符串。










