首页/数据/NoSQL 速查/$where 表达式过滤
MongoDB99 条

$where 表达式过滤

用 JavaScript 表达式逐文档过滤(this 指向文档);无法利用索引且执行任意脚本,只适合小集合或临时排查。

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

是什么

$where 在查询文档里传入一段 JavaScript 表达式或函数,服务端对每个候选文档执行一次,表达式为真则返回该文档;字符串形式里 this 指向当前文档。它来自早期 MongoDB 的查询能力,能表达查询语法写不出来的任意逻辑,代价是逐文档执行脚本、完全用不上索引,而且服务端 JavaScript 执行在新版本中已被标记为过时。

基本信息

写法{ $where: "this.a > this.b" } 或 { $where: function() { return this.a > this.b } }
执行位置服务端 JavaScript 引擎,对候选文档逐个执行
索引无法使用索引,无法覆盖查询,扫描量取决于前置条件
权限与开关可用启动参数禁用服务端脚本;托管环境常直接禁用 $where
替代$expr(MongoDB 3.6+)用聚合表达式表达同类逻辑,部分场景可用索引

典型使用场景

  • 临时对比字段:找出 a 字段大于 b 字段的文档,这类文档内字段互比用普通查询语法写不出来
  • 临时排查:线上出现异常文档时,用任意条件快速筛出候选样本
  • 小集合校验:迁移后核对少量数据是否符合业务约束
  • 历史脚本兼容:旧代码里已有的 $where 逻辑在升级前先摸清使用范围
  • 表达式原型验证:先用 $where 快速验证逻辑,再改写成 $expr 上生产

示例与替代写法

示例与替代写法
db.metrics.find({ $where: "this.rx > this.tx" })            # 字段互比,逐文档执行脚本
db.metrics.find({ $expr: { $gt: ["$rx", "$tx"] } })        # 推荐写法:聚合表达式,无需脚本
db.metrics.find({ status: "active", $where: "this.retry > 3" })  # 先用普通条件缩小候选集

db.metrics.aggregate([                                      # 管道内用 $expr 做同样的比较
  { $match: { status: "active" } },
  { $match: { $expr: { $gt: ["$rx", "$tx"] } } },
  { $count: "rows" }
])

# 排查前先确认当前环境允许执行服务端脚本(部分托管版本直接禁用)

行为与代价对照

写法能否用索引执行开销适用判断
普通查询条件能,按字段索引选择最低首选
$expr部分运算符可命中索引中等字段互比、表达式过滤的推荐写法
$where不能高,且随候选文档数增长仅限小集合与临时排查
聚合 $match 中的 $expr可配合前面阶段减少输入中等复杂管道内做字段间比较
服务端 JavaScript 执行在新版本 MongoDB 中已被标记为过时,并可能在托管环境中被禁用;用 $where 拼接外部输入还会引入脚本注入风险,生产查询不要走这条路。
即使必须用 $where,也要先用普通条件把候选集缩小:$where 只在前置条件命中的文档上执行,前置条件写得越准,脚本执行的次数越少。

易错点

  • 在生产大集合上直接用 $where 做字段互比:变成全集合扫描加逐文档执行脚本,长时间占用 CPU 并拖慢其他请求
  • 把外部输入拼进 $where 的字符串表达式:等于把脚本执行入口暴露给调用方,存在注入风险
  • 以为加上索引字段就能加速:$where 部分本身永远用不上索引,性能只取决于被它过滤之前的候选集大小
  • 不知道环境已禁用脚本:$where 在部分托管版本或配置下直接报错,迁移前要先确认可用性

数据来源与参考

  • MongoDB Manual: $where
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌