$push 配合 $each 是唯一可靠方式批量追加数组元素,否则会嵌套数组或引发并发错乱;必须显式使用 { $push: { field: { $each: [val1, val2] } } },且支持 $position、$slice 等修饰符。

用 $push 配合 $each 是唯一可靠方式
直接对数组字段多次执行 $push 不仅低效,还会因并发写入导致元素顺序错乱或重复。MongoDB 原生只支持单次操作中批量追加——必须用 $push 的 $each 修饰符,否则新元素只会逐个进队列,无法原子性插入。
常见错误是漏写 $each,比如误写成:{ $push: { tags: ["a", "b", "c"] } },这会把整个数组当做一个嵌套元素推入,结果变成 ["old", ["a","b","c"]],而非预期的 ["old", "a", "b", "c"]。
- 正确语法必须显式包裹:
{ $push: { tags: { $each: ["a", "b", "c"] } } } -
$each接收数组,不能是对象或单值;传字符串或数字会报errmsg: "$each requires an array" - 若字段尚不存在,MongoDB 自动创建空数组并追加,无需预设字段
$push + $each 支持去重和位置控制
批量追加时经常需要避免重复或指定插入位置,$push 可叠加其他修饰符:
- 去重用
$addToSet不行——它不支持$each批量,且只对单值生效;正确做法是加$slice或配合$position - 想插到开头:
{ $push: { tags: { $each: ["x", "y"], $position: 0 } } } - 限制数组长度(如只保留最新5个):
{ $push: { tags: { $each: ["new"], $slice: -5 } } },负数表示从尾部截取 -
$position和$slice可同时使用,但$position优先级更高
驱动层调用要注意数组参数的包装层级
不同语言驱动对 $each 的封装程度不同,容易在嵌套结构上出错。Node.js 的 mongodb 包要求完整写出修饰符对象,Python 的 pymongo 同理,但部分 ORM(如 Mongoose)可能自动补全,反而掩盖问题。
- Node.js 示例:
collection.updateOne({ _id: 123 }, { $push: { items: { $each: ["foo", "bar"] } } }) - Python 示例:
coll.update_one({"_id": 123}, {"$push": {"items": {"$each": ["foo", "bar"]}}}) - Mongoose 中若用
Model.updateOne(..., { runValidators: true }),需确认 schema 中该字段是[String]类型,否则$each会静默失败
大数组批量追加可能触发 BSON 大小限制
单次 $push + $each 的总操作大小不能超过 16MB(BSON 文档上限),实际安全阈值建议控制在 1–2MB 内。如果要追加几百个长字符串,得先估算体积,而不是依赖客户端报错才察觉。
- 一个长度为 100 的字符串约占用 100 字节,1000 个就是 100KB;但若含 Unicode 或 base64 图片数据,体积会陡增
- 服务端不会拆分请求,超限直接返回
errmsg: "BSONObj size too large" - 真要塞大量数据,应改用分批
updateMany或先读出原数组、合并后再$set整体写回(注意并发覆盖风险)
最易被忽略的是 $each 必须作为 $push 的子字段出现,少一层花括号或错放层级,操作就退化为单元素插入或语法错误。线上环境别靠试错验证,先在 mongosh 里用小数据跑通再上。











