首页/数据/NoSQL 速查/$expr 比较同文档两字段
MongoDB99 条

$expr 比较同文档两字段

在查询里引用字段值做计算或跨字段比较:$expr 使用聚合表达式语法,适合“同文档字段互比”的场景。

NoSQL 速查数据更新于 2026-10-13

$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 会禁用部分查询优化:在已有高选择性条件时把它与普通条件并列使用,让普通条件先缩小候选集,可以大幅降低求值成本。

易错点

  • 字段引用漏写 $ 前缀:条件变成与固定字符串比较,恒真或恒假且不报错,排查时很难第一眼看出
  • 对可能出现缺失或 null 的字段直接比较:比较结果为 null 时文档不匹配,预警漏报却无任何错误提示
  • 在超大集合上单独使用 $expr:缺少可用的索引条件,退化为全集合扫描
  • 把 $expr 当 $where 的等价替换:聚合运算符的写法与 JavaScript 表达式不同,直接搬运算式会语法报错

数据来源与参考

  • MongoDB Manual: $expr
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌