该用$unionwith时:同库同结构集合需物理拼接(如分片日志),后续统一聚合;禁用$lookup(关联)和应用层拼接(低效)。注意字段冲突、无索引、不跨分片。

什么时候该用 $unionWith,而不是 $lookup 或应用层拼接?
$unionWith 本质是把两个集合的文档“堆在一起”,不做关联、不建索引、不校验字段一致性。它适合:需要合并多个结构相似的集合(比如按时间分片的日志集合 logs_202401、logs_202402),且后续要统一做聚合统计;或者临时调试时快速拉取多源数据对比。别拿它去替代 $lookup——后者是关联查询,前者是物理拼接。应用层拼接则意味着多次 round-trip、内存合并、丢失 pipeline 优化机会,尤其当数据量超几万条时延迟明显上升。
$unionWith 的基本写法和必须注意的字段冲突
语法很简单:{$unionWith: {coll: "other_collection"}}。但实际跑起来常卡在字段名冲突上:如果两个集合都有 _id 字段,MongoDB 不会自动重命名或忽略,而是直接报错 duplicate key(即使 ObjectId 不同)。解决方法只有两个:
• 在 $unionWith 前加 $project 重命名一方的 _id,比如 {_id: "$_id", src_id: "$_id"};
• 或用 $unset: ["_id"] 干掉其中一方的 _id(前提是业务允许无主键);
• 别指望 $unionWith 自动处理类型差异——status 在 A 集合是字符串,在 B 集合是数字,合并后字段类型混杂,后续 $match 或 $sort 可能静默失效。
性能陷阱:没有索引、不能跨分片、慎用在大集合上
$unionWith 操作本身不走索引,它先全量扫描目标集合,再全量扫描 coll 指定的集合,最后内存合并。这意味着:
• 如果两个集合各 500 万文档,操作可能耗时数秒甚至分钟,且消耗大量 RAM;
• 跨分片集群中,$unionWith 要求两个集合必须在同一个分片键上分片,否则直接报错 unionWith must be performed on collections in the same database and with the same shard key;
• 它不支持写入,只能用在 aggregate() 中,不能放进 update 或 bulkWrite。
替代方案:什么时候该放弃 $unionWith?
如果只是想查“用户 + 用户扩展信息”,用 $lookup 更安全高效;如果集合结构差异大(比如字段名、嵌套层级完全不同),硬凑 $unionWith 会导致后续每个 stage 都得写冗余 $cond 判断字段是否存在;如果数据来自不同数据库,MongoDB 原生不支持跨库 $unionWith,此时只能靠应用层或 change stream + 预聚合表。真正省事的场景其实很窄:同库、同结构、需一次性拉平、且量级可控。











