查询空值与缺失字段
本教程共 50 篇 · 第 19 篇 · 更新于 2026-07-30 · 约 6 分钟阅读
19. 查询空值与缺失字段
本节目标:弄懂
{字段: null}为什么既匹配空值又匹配缺失,并学会用$exists和$type把这两种情况精确分开。
「空值(null)」和「字段压根不存在」在 MongoDB 里是两回事,但查询时很容易混淆。我们先造一批带这两种情况的 users 数据:
db.users.insertMany([
{ _id: 1, name: "张三", age: 28, city: "北京", registered_at: ISODate("2024-01-15") },
{ _id: 2, name: "李四", age: 34, city: null, registered_at: ISODate("2024-03-20") },
{ _id: 3, name: "王五", age: 22, city: "北京" },
{ _id: 4, name: "赵六", age: 41, city: "广州", registered_at: null }
])
这里 _id:2 的 city 是空值;_id:3 根本没有 registered_at 字段(缺失);_id:4 的 registered_at 是空值。
{字段: null} 的双义性
直接用 null 去查,结果会超出你的预期:
// 既返回 city 为空值的,也返回根本没有 city 字段的
db.users.find({ city: null })
这条会命中 _id:2(city 是 null)和 _id:3(没有 city 字段)。因为 MongoDB 把「值为 null」和「字段不存在」都算作匹配。这个双义性经常让初学者一脸懵。
Warning
{ city: null }不等于「city 是空值」。它其实是「city 是空值,或者压根没有 city」。想精准区分,必须加运算符。
只查「字段存在且非空」:$ne
如果想排除空值和缺失,用 $ne(不等于):
// 只要 city 存在并且不是 null 的文档
db.users.find({ city: { $ne: null } })
这条只命中 _id:1 和 _id:4,它们都有真实城市值。
只查「字段缺失」:$exists: false
$exists 用来判断字段在不在。设为 false 就是「字段不存在」:
// 只找没有 registered_at 字段的用户
db.users.find({ registered_at: { $exists: false } })
这条只命中 _id:3。它不管值是什么,只关心字段在不在文档里。
Note
$exists: true则相反,只匹配「字段存在」的文档,无论它的值是 null 还是别的东西。
只查「值为空」:$type: 10
BSON 里 null 有自己的类型编号,是 10。用 $type 可以只认空值:
// 只找 registered_at 的值真的是 null 的文档
db.users.find({ registered_at: { $type: 10 } })
这条只命中 _id:4。注意它不会命中缺失字段的文档,因为缺失字段谈不上「类型是 null」。
三种写法对比
一句话记住三兄弟的区别:
| 写法 | 匹配空值 | 匹配缺失 |
|---|---|---|
{ f: null } | 是 | 是 |
{ f: { $ne: null } } | 否 | 否(只留存在的) |
{ f: { $exists: false } } | 否 | 是 |
{ f: { $type: 10 } } | 是 | 否 |
Tip实际开发里,我一般建议字段要么有值、要么是 null,别让它「时有时无」。统一了结构,查询时才不会掉进双义陷阱。
下章我们讲投影(Projection),也就是怎么控制查询「返回哪些字段」。