$unionWith仅合并文档不统计,必须搭配$project统一字段、$group聚合才能得到统计值;它类似SQL的UNION ALL,不自动求和去重,也不保证顺序。

$unionWith 是 MongoDB 4.4+ 合并集合数据的最直接方式,但它本身不统计,只做“堆叠”;要合并统计数据,必须在 $unionWith 前后搭配 $group、$sum 等阶段——否则你得到的只是混在一起的原始文档,不是聚合结果。
为什么 $unionWith 不能直接返回统计值
$unionWith 的本质是 $unionWith 阶段只负责把两个集合(或其管道输出)的文档拼到一起,它不改变文档结构,也不执行计算。就像 SQL 中的 UNION ALL,不是 SELECT SUM(x) FROM ... UNION SELECT SUM(y) FROM ...。
常见错误现象:
- 写了
db.orders_april.aggregate([{$unionWith: "orders_may"}]),结果返回了全部明细文档,但没看到总数量 - 误以为
$unionWith自带去重或求和能力,实际它连排序都不保证
合并统计的正确链路:project → unionWith → group
典型场景:统计 April 和 May 两个月份订单的总数量、总金额。两个集合结构相似但字段名可能不一致(如 qty vs quantity),需先对齐再聚合。
实操建议:
- 每个分支都用
$project统一字段名,比如都映射为amount和count,避免$group阶段因字段缺失漏计 -
$unionWith的pipeline参数里不要写$out或$merge,会报错 - 最终
$group必须用空对象{_id: null}表示全局聚合,否则按某个字段分组就得不到“总数”
示例(合并 orders_april 和 orders_may 并统计):
db.orders_april.aggregate([
{ $project: { amount: "$quantity", count: 1 } },
{ $unionWith: {
coll: "orders_may",
pipeline: [ { $project: { amount: "$qty", count: 1 } } ]
}
},
{ $group: { _id: null, totalAmount: { $sum: "$amount" }, totalCount: { $sum: "$count" } } }
])
遇到字段不一致时,$project 不够用怎么办
当两个集合字段差异大(比如一个有 price * qty,另一个存的是 total 字段),仅靠 $project 映射无法完成等价转换,此时必须在 $unionWith 的 pipeline 内提前计算。
关键点:
- 不能依赖外部逻辑补字段,所有归一化操作必须在各自 pipeline 内完成
- 若某集合缺少必要字段(如
qty),可用$ifNull设默认值,否则该文档在$sum时会被跳过 - 注意类型一致性:
"100"(字符串)和100(数字)在$sum中表现不同,建议用$toInt或$toDouble显式转换
性能与分片集合的注意事项
$unionWith 支持分片集合,但跨分片合并会带来网络开销,尤其当 pipeline 中含复杂计算时。
容易被忽略的细节:
- 如果两个集合都很大,且你只需要统计结果(而非明细),优先考虑分别
$group再$unionWith两个单文档结果,比全量合并再$group快得多 -
$unionWith不支持索引下推,所以前置的$match尽量放在各 pipeline 内部(如pipeline: [{ $match: { status: "paid" } }, ...]),而不是放在$unionWith外面 - 结果顺序不确定,别依赖
$unionWith输出的文档顺序做后续逻辑(比如取第一条当最新记录)











