$concatArrays不能直接合并同文档多个数组字段,因其要求所有参数必须为数组类型,而实际中字段常为null、缺失或字符串,需用$ifNull预处理为[]再拼接。

为什么 $concatArrays 不能直接合并同文档的多个数组字段
$concatArrays 要求所有参数都是数组类型,且必须显式传入每个数组表达式。如果你写 {$concatArrays: ["$arr1", "$arr2"]},它确实能工作——但前提是 $arr1 和 $arr2 在当前文档中**都存在且值为数组**。常见错误是其中某个字段为 null、missing 或字符串,这时整个聚合阶段会报错:concatArrays requires all arguments to be arrays。
实际场景中,字段缺失很常见(比如部分文档没写 tags,或前端传了空字符串而非空数组),所以不能默认“字段存在即安全”。
- 用
$ifNull统一兜底:把可能为null/缺失的字段转成空数组[] - 避免在
$concatArrays里直接引用未校验的字段名 - 如果字段可能是字符串(如
"[1,2]"),需先用$cond+$isArray判断再转换,否则报错
正确写法:用 $ifNull 预处理再拼接
最稳妥的方式是给每个待拼接字段套一层 $ifNull,确保输入一定是数组:
{
$addFields: {
mergedArr: {
$concatArrays: [
{ $ifNull: ["$arr1", []] },
{ $ifNull: ["$arr2", []] },
{ $ifNull: ["$arr3", []] }
]
}
}
}
这个写法能扛住以下情况:
-
arr1: null→ 当作[] -
arr2: undefined(字段不存在)→ 当作[] -
arr3: ["a", "b"]→ 原样参与拼接
注意:$ifNull 第二个参数必须是字面量数组 [],不能写成 "[]" 或变量引用,否则类型不匹配。
合并嵌套数组时别漏掉 $reduce
如果目标不是平铺拼接,而是想把 [[1,2], [3,4], [5]] 合并成 [1,2,3,4,5](即“扁平化”),$concatArrays 本身不支持递归。它只做一层拼接,$concatArrays: [[1,2], [3,4], [5]] 结果是 [1,2,3,4,5] —— 这没问题;但若数据是 [[[1,2]], [[3,4]]],那得先用 $reduce + $concatArrays 层层展开。
- 两层嵌套(如
comments.replies是数组的数组):用$reduce累积调用$concatArrays - 三层及以上:建议在应用层处理,聚合管道深度增加会明显拖慢性能
- 别试图用
$map+$concatArrays替代$reduce,那样只会生成新数组而非扁平结果
性能和兼容性提醒
$concatArrays 从 MongoDB 3.2 就支持,无版本焦虑。但要注意两点:
- 拼接结果数组长度没有硬限制,但单文档总大小不能超 16MB,大数组拼接前最好加
$size校验或用$slice截断 - 如果拼接字段来自
$lookup的as输出(比如userOrders),要确认该字段确实是数组——有时$lookup匹配不到会返回空数组,这反而是安全的;但若上游用了$unwind再$group,可能产出非数组结构 - 在分片集群上,
$concatArrays本身不触发跨片 shuffle,但上游 stage(如$lookup)可能有网络开销,别在$project里无谓重复计算
真正容易被忽略的是字段类型漂移——今天后端保证存数组,明天运营手动改了一条记录成字符串,整个聚合就崩。生产环境务必对关键拼接字段做 $isArray 断言或日志告警。

















