$cond是MongoDB 4.2+聚合管道更新中的三元条件操作符,语法为{ $cond: { if: <bool>, then: <val>, else: <val> } },用于在updateMany等操作中服务端原子化计算字段值,如根据时间条件动态设priority。

什么是聚合管道更新语法中的 $cond
从 MongoDB 4.2 开始,updateOne、updateMany 和 replaceOne 支持传入聚合管道(而非传统更新器对象)作为第二个参数。这意味着你能在更新时用 $cond 做条件判断,动态计算字段值——不是简单地“设为固定值”,而是“如果 A 成立就设为 X,否则设为 Y”。
这和老式 $set + $ifNull 或应用层 if-else 的本质区别在于:整个条件逻辑在服务端执行,原子、高效、不依赖读取-计算-写入三步,也避免了竞态。
怎么写一个带 $cond 的 updateMany 调用
核心是把更新操作写成一个数组,每个元素是一个聚合阶段;$set 阶段内嵌 $cond 表达式。注意:不能直接在顶层用 { $set: { field: { $cond: ... } } } —— 这是旧语法,只支持单个更新器,不支持表达式。
常见错误现象:Unrecognized pipeline stage name: '$set',说明你误把聚合管道当成了普通更新器对象传入。
- 必须用数组形式传参:
db.collection.updateMany({ filter }, [ { $set: { ... } } ]) -
$cond必须是三元结构:{ if: ..., then: ..., else: ... },不能省略else - 所有字段引用用
$fieldName(带美元符),表示文档当前值;字面量用"string"或42 - 若需访问根字段以外的路径(如嵌套字段),用
$"path.to.field"或$arrayElemAt等辅助操作符
示例:将 status 为 "pending" 且 createdAt 超过 24 小时的文档,把 priority 升级为 "high",其余设为 "normal":
db.orders.updateMany(
{ status: "pending" },
[
{
$set: {
priority: {
$cond: {
if: { $lt: [{ $subtract: [new Date(), "$createdAt"] }, 24 * 60 * 60 * 1000] },
then: "high",
else: "normal"
}
}
}
}
]
)$cond 在嵌套字段或数组场景下的坑
直接对数组字段用 $cond 判断会失败,因为 $cond 的 if 分支期望布尔值,而 "$tags" 返回的是数组本身(非布尔)。常见错误现象:Cannot use $cond with array 或更新结果不符合预期。
正确做法是先用 $size、$in、$gt 等产出布尔值,再喂给 $cond。
- 判断数组是否为空:
{ if: { $eq: [{ $size: "$tags" }, 0] }, then: ..., else: ... } - 判断某值是否在数组中:
{ if: { $in: ["urgent", "$tags"] }, then: "critical", else: "low" } - 对数组元素逐个处理?那得用
$map,$cond本身不迭代 - 注意时区:MongoDB 中
new Date()是客户端时间,生产环境建议用服务器端$$NOW变量(MongoDB 5.0+)
性能与兼容性注意事项
聚合管道更新比普通更新略重,因为要解析、执行管道。但只要不滥用 $lookup 或深度嵌套,性能差异可忽略。真正要注意的是版本和驱动支持。
- 仅 MongoDB 4.2+ 支持聚合管道更新;低于此版本会报错
Unknown modifier: $set(当传入管道数组时) - Node.js 驱动需 v3.6+,Python PyMongo 需 v3.12+,旧版驱动可能静默降级为普通更新(导致语法被忽略)
-
$cond内部不能调用用户自定义函数,也不能访问外部变量(除系统变量如$$NOW) - 调试技巧:先把管道逻辑拆出来用
aggregate([ ... ])测试输出,确认字段计算正确后再套进updateMany
最易被忽略的一点:管道更新不触发 Change Streams 的 replace 类型事件,而是 update;如果你依赖变更流做后续处理,注意 fullDocument 字段在管道更新中默认不返回新文档(需显式加 { includeDocuments: true } 选项)。


















