必须用数组语法传聚合管道,否则 $multiply、$cond 等表达式报错;updateOne/updateMany 的 update 参数需为数组形式才能启用聚合上下文,支持 $$NOW、$$REMOVE 等变量,仅允许 $set/$unset/$replaceWith 三类阶段,嵌套更新须配 arrayFilters,深度合并需 $mergeObjects + $replaceWith。

必须用数组语法传聚合管道,否则 $multiply、$cond 这类表达式直接报 Unrecognized expression 错误。
updateOne/updateMany 的 update 参数必须是数组
传统写法 { $set: { total: { $multiply: ["$price", "$qty"] } } } 在 4.2+ 里仍被当作老式更新操作符处理,不支持聚合表达式。只有数组形式才能启用聚合上下文。
- ✅ 正确:
db.orders.updateOne({ _id: 1 }, [ { $set: { total: { $multiply: ["$price", "$qty"] } } } ]) - ❌ 错误:
db.orders.updateOne({ _id: 1 }, { $set: { total: { $multiply: ["$price", "$qty"] } } }) - 管道中可安全使用
$$NOW、$$REMOVE等系统变量,对象语法不认这些
只允许 $set、$unset、$replaceWith 三类阶段
别想在 update 管道里加 $match 或 $group——filter 已在第一个参数完成,聚合阶段仅用于“改什么”,不是“选谁”。
-
$set等价于$addFields,可计算新字段或重写已有字段 -
$unset等价于$project,设为0表示删字段,显式保留字段需设为1 -
$replaceWith会丢掉原始_id,除非你手动写进新文档:{ $replaceWith: { _id: "$_id", ... } } - 分片集群不支持
$replaceWith,踩坑前先查部署类型
嵌套数组更新必须配 arrayFilters 选项
光写 "tags.$[t]" 不生效,MongoDB 会报 array filter was not specified。
- 正确写法:
db.users.updateOne({ _id: 1 }, [ { $set: { "tags.$[t]": "legacy" } } ], { arrayFilters: [{ "t": "old" }] }) -
arrayFilters是独立选项,不能塞进 pipeline 里 - 多个数组字段要更新?得定义多个占位符:
"list.$[l].items.$[i]"+arrayFilters: [{ "l.category": "A" }, { "i.name": "x" }]
深度合并嵌套对象得靠 $mergeObjects + $replaceWith
$set: { "package.newField": "x" } 只适合静态路径;想把新对象和旧 package 字段做一级合并(保留原有 parameter 字段),必须走 pipeline。
- 写法:
[ { $replaceWith: { $mergeObjects: ["$$ROOT", { package: { newField: "x" } }] } } ] -
$mergeObjects不递归,package.nested.field会被整体替换,不是字段级更新 - 若结构动态不可预知,点号路径拼接更轻量;若需逻辑判断再合并,pipeline 是唯一原子解
最容易忽略的是:所有聚合阶段在 update 场景下都受严格限制——不是“能用的聚合语法都能用”,而是只放行三个阶段的别名,且不允许任何副作用操作(比如 $out 或 $merge)。写之前先确认 MongoDB 版本 ≥4.2、PyMongo ≥3.9、驱动调用方式匹配数组入参规范。

















