在MongoDB的聚合管道里,$cond是最基础的条件表达式,它允许我们在文档流转的过程中,根据某个布尔条件选择不同的返回值。很多人在写报表统计或数据清洗任务时,都会用到它来替换空值、标记状态或者计算衍生字段。理解它的执行机制和参数结构,是写出稳定聚合查询的前提。

一、$cond的基本语法与求值原理
$cond本质上是一个三元运算符,在聚合框架中接收三种形式的参数。最常见的是数组形式:{ $cond: [ <booleanExpression>, <trueCase>, <falseCase> ] }。当文档进入包含该表达式的阶段时,MongoDB会先对第一个元素做求值,结果为真则返回第二个元素,否则返回第三个。这种求值发生在每一个文档的上下文里,而不是像关系型数据库的视图那样预先优化。
除了数组写法,也可以使用对象形式:{ $cond: { if: <expr>, then: <expr>, else: <expr> } }。两者在性能上没有差异,但对象形式在嵌套条件时结构更清晰。需要特别注意的是,$cond的三个分支本身都可以是复杂的表达式,比如另一个$cond或者$switch,但这也会让调试变得困难。
下面是一个最简单的使用示例,将年龄小于十八的文档标记为未成年,其余标记为成年:
db.users.aggregate([
{
$project: {
name: 1,
age: 1,
level: {
$cond: [
{ $lt: ["$age", 18] },
"未成年",
"成年"
]
}
}
}
])
从底层来看,$cond在聚合执行引擎中属于表达式节点,它不会减少输入的文档数量,只是转换字段值。因此如果条件写错,往往不会报错,而是默默返回不符合预期的数据,这种逻辑错误比语法错误更难排查。
二、常见逻辑错误与避坑实践
实际开发中,使用$cond最容易犯的错误是忽略字段可能缺失的情况。例如上面的例子中,如果某些文档没有age字段,$lt比较会返回null,而null在布尔上下文中不等于true,于是这些文档统统走到else分支,被标为成年,这显然不符合业务语义。
为了避免这类问题,应当先用$ifNull给字段补默认值,或者在$cond的布尔表达式里用$type判断存在性。改进后的写法如下,确保缺失年龄的文档被单独标记:
db.users.aggregate([
{
$project: {
name: 1,
age: 1,
level: {
$cond: [
{ $eq: [{ $type: "$age" }, "missing"] },
"未知",
{
$cond: [
{ $lt: ["$age", 18] },
"未成年",
"成年"
]
}
]
}
}
}
])
另一个典型误区是多层嵌套$cond做多分支。虽然语法上允许,但当分支超过三个时,代码会变成难以阅读的金字塔结构。此时应优先考虑$switch表达式,它专门用于处理多路分支,逻辑边界更明确,也方便后续扩展。
还需要警惕类型隐式转换。比如用$cond比较字符串和数字时,MongoDB不会自动转换,结果可能恒为假。建议在条件表达式前用$convert或$toInt统一类型,减少歧义。
三、$cond与$switch的选型对比
当业务规则只有两种状态时,$cond是最直接的选择,写法紧凑且易于理解。但在会员等级、订单状态等超过三种分类的场景中,$switch的优势就显现出来。它使用branches数组列出多个case和then,并支持default兜底,比嵌套$cond更不容易在修改时错位。
我们通过一个订单状态映射的例子来对比。假设状态字段为status,值为1到4,用嵌套$cond需要三层,而$switch只需平铺:
db.orders.aggregate([
{
$project: {
statusText: {
$switch: {
branches: [
{ case: { $eq: ["$status", 1] }, then: "待付款" },
{ case: { $eq: ["$status", 2] }, then: "已付款" },
{ case: { $eq: ["$status", 3] }, then: "已发货" },
{ case: { $eq: ["$status", 4] }, then: "已完成" }
],
default: "异常状态"
}
}
}
}
])
从维护成本看,$switch在分支增减时只需改动对应数组项,不会影响其他逻辑;而嵌套$cond一旦插入新层级,就要重构整段表达式。性能方面两者在分支数较少时差异微小,但分支多时$switch的线性匹配通常更易被优化器处理。
总结来说,把$cond用在真假二选一或简单缺省处理上,把多分支判断交给$switch,并在所有条件表达式前做好字段存在性和类型校验,就能大幅降低聚合管道中的逻辑错误风险。