{field: null}不可靠,因它同时匹配字段值为null和字段不存在,但二者语义不同、索引行为不稳定;精准区分必须用$or组合{$eq: null}与{$exists: false},或加$exists: true限定。

直接用 {field: null} 会同时命中“字段值为 null”和“字段根本不存在”两类文档,但语义模糊、索引不可控、排查难——真要精准区分或安全覆盖,必须拆开写。
为什么 {field: null} 不可靠
它看起来最简,实际是陷阱。MongoDB 默认把字段缺失(key 未出现)和显式存 null 当作等价匹配项,但这两者业务含义往往完全不同:比如 phone 字段为 null 可能是用户主动清空,而字段缺失可能是注册流程未走到这一步。更关键的是,如果 field 上有索引,{field: null} 查询可能走索引也可能不走,执行计划不稳定;用 .explain("executionStats") 一看,executionStages.stage 常是 COLLSCAN。
$or 是唯一稳妥的全量覆盖写法
要查“字段值为 null”或“字段压根没定义”,必须用 $or 显式并列两个条件:
db.collection.find({
$or: [
{ field: { $eq: null } },
{ field: { $exists: false } }
]
})
-
{field: {$eq: null}}只匹配字段存在且值为null的文档 -
{field: {$exists: false}}只匹配字段键完全没出现的文档 - 两者用
$or组合,才真正覆盖全部目标场景 - 别用
{field: null}替代——它不是语法糖,是语义妥协
只想查“字段存在但值为 null”?加 $exists: true
常见需求:排除字段缺失干扰,只盯住那些“写了 null”的脏数据。这时不能只靠 {field: null},得双条件并列:
db.collection.find({
field: { $eq: null },
field: { $exists: true }
})
注意不是嵌套写法,而是同级两个键。MongoDB 会把它们理解为 AND 关系。这个组合能确保结果里每个文档都含 field 键,且其值严格等于 null。
$exists: false 必然全表扫描,提前规划替代方案
{field: {$exists: false}} 永远无法利用常规索引,因为索引项只记录“存在”的字段值。集合越大,性能越差。高频查询时必须换思路:
- 写入时统一补默认值:比如用空字符串
""或数字0代替“缺失”,再用{field: ""}+ 索引加速 - 加标记字段:如
field_missing: true,给它建索引,查{field_missing: true} - 聚合管道中兜底:用
$cond配$exists判断,比如{$cond: [{ $not: ["$field"] }, "default", "$field"]}
真正麻烦的从来不是怎么写查询,而是字段缺失在业务里到底代表什么——是正常可选,还是数据异常?想清楚这点,才能决定该用 $exists: false 还是该改写入逻辑。











