null 与缺失字段的区别
文档库没有固定表结构,"值是 null"与"字段不存在"是两种不同状态,但查询 {field: null} 会把两者一起匹配。只想要显式存了 null 的文档,用 $type: "null";只想要字段存在且有值的,用 $exists: true 加 $ne: null 组合。做数据回填与质量核查时,这两种状态混淆会直接决定清理语句会不会误伤数据。
基本信息
| {field: null} | 匹配值为 null 或字段缺失的文档 |
|---|---|
| {field: {$type: "null"}} | 仅匹配显式存储的 null |
| {field: {$exists: true, $ne: null}} | 字段存在且值不为 null |
| {field: {$exists: false}} | 字段完全不存在 |
| 适用版本 | MongoDB 3.6+ 行为一致 |
示例
示例
db.users.find({ phone: null }) # 空值或没这个字段都算
db.users.find({ phone: { $type: "null" } }) # 只有显式存了 null
db.users.find({ phone: { $exists: true, $ne: null } }) # 字段存在且有值
db.users.find({ phone: { $exists: false } }) # 字段完全缺失
// 按类型分组:missing 与 null 会分成两组,一眼看清比例
db.users.aggregate([ { $group: { _id: { $type: "$phone" }, n: { $sum: 1 } } } ])
// 回填前先计数,再批量补齐缺失字段
db.users.countDocuments({ phone: { $exists: false } })
db.users.updateMany({ phone: { $exists: false } }, { $set: { phone: "" } })典型使用场景
- 数据质量核查:分清"没填"和"填了空值",再决定回填口径
- 空值查询优化:null 会被编入普通索引,查空值的扫描量要先评估
- 字段清理:$unset 移除字段与把字段置为 null 语义不同,迁移前先区分
- 唯一索引设计:唯一索引把缺失字段视为 null,需配合稀疏或部分索引
- 接口兼容:老客户端写缺失字段、新客户端写 null,查询要同时兼容
写法与匹配范围
| 写法 | 匹配范围 | 典型用途 |
|---|---|---|
| {phone: null} | null 值加字段缺失 | 兼容旧数据的空值查询 |
| $type: "null" | 仅显式 null | 确认"填了空值"的文档 |
| $exists: true, $ne: null | 存在且有值 | 有效性校验与统计 |
| $exists: false | 完全缺失 | 回填缺失字段 |
普通索引会把 null 值一起编入索引,用索引优化"查空值"时要注意索引形态:稀疏索引与部分索引不包含缺失字段的文档,查询结果会因此不同。
数组元素为 null 与字段本身为 null 是两回事,$type 会对数组元素逐个判断,核查前先确认字段的实际类型。