三个逻辑操作符的分工
过滤文档里 $or 表示任一条件成立即命中,$nor 表示数组内全部条件都不成立才命中,$not 对单个字段表达式取反(最常用于反转正则)。它们的层不同:$or/$nor 作用在文档层级,$not 只能写在字段内部。设计时只要能把 $or 的等值分支改写成 $in,就应该改写,因为每个 or 分支都可能在计划选择阶段各自试跑候选计划,分支越多规划与执行开销越大。
基本信息
| $or | 数组内任一条件成立即命中 |
|---|---|
| $nor | 数组内全部条件都不成立才命中 |
| $not | 对字段级子表达式取反,可包裹正则 |
| 限制 | $not 不能取反整个过滤文档,也不能包裹 $or/$and 这类顶层操作符 |
| 适用版本 | MongoDB 3.6+ 行为一致 |
示例
示例
// 任一成立:数量小于 2 或者是 VIP
db.orders.find({ $or: [ { qty: { $lt: 2 } }, { vip: true } ] })
// 全部不成立:既不是已取消也不是已退款
db.orders.find({ $nor: [ { status: "canceled" }, { status: "refunded" } ] })
// 取反正则:排除测试与内部账号
db.users.find({ name: { $not: /^test/i } })
// 等价改写:纯等值分支合并成 $in,索引利用率更好
db.orders.find({ status: { $in: ["paid", "shipped"] } })
// 用 explain 对比两种写法的扫描量
db.orders.find({ $or: [ { status: "paid" }, { status: "shipped" } ] }).explain("executionStats")典型使用场景
- 复杂备选条件:等值之外还要叠加范围或数组条件时用 $or
- 排除固定组合:"既不是已取消也不是已退款"用 $nor 比 $not 加 $in 好读
- 排除测试数据:$not 包正则过滤 test、demo 一类账号
- 条件分组:把 (A 或 B) 作为与条件之一,与其他字段并列写在顶层
- 查询改写:分支很多的 $or 拆成多次查询在客户端合并,便于控制每次扫描量
操作符对照
| 操作符 | 语义 | 适合与不适合 |
|---|---|---|
| $or | 数组内任一成立 | 适合混合类型的备选条件;纯枚举优先 $in |
| $in | 字段取值在列表内 | 适合单字段枚举,可走索引 |
| $nor | 数组内全部不成立 | 适合排除固定几种组合 |
| $not | 字段级取反 | 适合包正则;不能包 $or/$and |
$or 的每个分支都可能触发独立的计划选择,分支越多规划开销越大;大量等值分支先合并为 $in,再考虑是否拆查询。
$nor 与 $not 属于否定条件,无法靠自身在索引上定位(索引无法直接表达"不包含"),要用其他可索引条件先把候选集缩小,否则就是全集合扫描。