$push仅在$group阶段合法,用于收集字段值成数组;在$project等阶段会报错,需改用$concatarrays或$addtoset等管道支持的操作符。

为什么$push在聚合管道里不生效?
因为$push不是聚合阶段操作符,它只在$group阶段合法;直接写在$project或$addFields里会报错"unknown operator: $push"。想往数组里追加元素,得先用$group聚合上下文,或者改用$concatArrays、$addToSet等真正支持管道的数组操作符。
$push必须配合$group使用
它的作用是把匹配文档的某个字段值收集进数组,不是“向已有数组追加”。比如统计每个用户最近三条订单ID:
[
{ $group: {
_id: "$userId",
recentOrders: { $push: "$orderId" }
}
},
{ $addFields: { recentOrders: { $slice: ["$recentOrders", -3] } } }
]
-
$push本身不控制顺序,结果顺序取决于输入文档顺序(通常就是插入顺序) - 如果要按时间倒序取最新三条,得先
$sort再$group,否则$slice可能截到旧数据 - 不能用
$push更新嵌套数组字段,比如"profile.tags"——它只接受顶层字段名或简单表达式如"$tags"
想往现有数组字段追加元素?用$concatArrays代替
比如给用户文档的roles数组加一个新角色"editor":
[
{ $addFields: {
roles: { $concatArrays: ["$roles", ["editor"]] }
}
}
]
-
$roles如果是null或缺失,$concatArrays会报错,得先用$ifNull兜底:{ $concatArrays: [{ $ifNull: ["$roles", []] }, ["editor"]] } - 如果要避免重复,用
$addToSet,但它不保证顺序,且只对数组元素做去重,不是对整个数组 -
$push无法实现这个需求——它没有“目标字段路径 + 新值”的语法,这是常见误解来源
聚合中修改数组容易忽略的兼容性问题
MongoDB 4.2+ 支持$merge直接写回集合,但$push在$group里的行为和updateOne里的$push完全不同:
- 聚合里的
$push是“收集”,update里的$push是“追加”,语义隔离,不能混用逻辑 - 聚合输出的数组默认无索引,如果后续要用
$unwind展开,注意空数组会直接跳过整条文档 - 在分片集群上,
$group必须带_id,且_id字段不能是数组或对象(除非显式指定为$expr),否则路由失败
真正难的是区分场景:你要的是“汇总多个文档的值”还是“更新单个文档的数组字段”——选错操作符,后面全得重写管道。











