是什么
$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 只在前置条件命中的文档上执行,前置条件写得越准,脚本执行的次数越少。