$expr 做什么用
$expr 允许在查询条件里使用聚合表达式语法,最典型的用途是让同一个文档的两个字段互相比较(如库存低于安全库存、支出超过预算)。它替代了老式的 $where 服务端 JavaScript 求值:$expr 不启动 JS 引擎,表达能力覆盖绝大多数比较与逻辑运算,性能与安全性都更好。
基本信息
| 语法形式 | { $expr: { <聚合表达式> } } |
|---|---|
| 字段引用 | 在表达式内用 "$字段名" 引用文档字段 |
| 适用位置 | find / deleteMany / updateMany 的过滤条件;聚合管道内直接用表达式即可 |
| 适用版本 | MongoDB 3.6 起支持 |
| 与 $where 对比 | 不执行 JavaScript,可用聚合运算符,官方推荐作为 $where 的替代 |
典型使用场景
- 库存预警:找出库存低于安全库存的商品
- 财务核对:筛选支出大于预算、或两个金额字段不相等的记录
- 时间差比较:判断更新时间早于创建时间的异常文档
- 字段间一致性校验:检查明细汇总值与表头金额是否相符
- 配合 $cond 与 $ifNull 处理缺失字段,避免空值导致条件整体为假
示例
示例
// 字段与字段比较:支出超过预算的记录
db.budgets.find({ $expr: { $gt: ["$spent", "$budget"] } })
// 库存低于安全库存
db.products.find({ $expr: { $lte: ["$stock", "$safetyStock"] } })
// 组合条件:状态为 active 且已用额度超过阈值
db.accounts.find({ status: "active", $expr: { $and: [ { $gt: ["$used", "$limit"] }, { $gt: ["$used", 0] } ] } })
// 缺失字段用 $ifNull 兜底,否则 null 参与比较会得到非预期结果
db.products.find({ $expr: { $lt: [ { $ifNull: ["$stock", 0] }, "$safetyStock" ] } })
// 聚合管道里不需要 $expr 包裹,直接写表达式即可
db.budgets.aggregate([ { $match: { $expr: { $gt: ["$spent", "$budget"] } } }, { $project: { gap: { $subtract: ["$spent", "$budget"] } } } ])常用聚合运算符
| 运算符 | 作用 | 注意点 |
|---|---|---|
| $eq / $ne / $gt / $gte / $lt / $lte | 比较两个表达式 | 写字段必须带 $ 前缀,否则按字面量比较 |
| $and / $or / $not | 逻辑组合 | 与查询操作符写法不同,这里是数组形式 |
| $ifNull | 空值兜底 | 字段缺失或为 null 时替换默认值 |
| $cond | 条件分支 | 三元写法便于在投影里派生标记字段 |
| $size / $arrayElemAt | 数组长度与取元素 | 对非数组字段使用会直接报错 |
索引能力有限:字段与字段比较无法借助普通索引,只能逐条求值;字段与常量比较的简单形式在较新版本可能走索引,但必须用 explain 验证执行计划,别凭版本号推断。
$expr 会禁用部分查询优化:在已有高选择性条件时把它与普通条件并列使用,让普通条件先缩小候选集,可以大幅降低求值成本。