HOWTO · MongoDB
在 MongoDB 中查询非空值
在 MongoDB 中查询字段不为 null 的文档:$ne: null 匹配字段存在且持有值的文档,$type 与 $exists 区分真正的 null 与缺失字段,再加一个 $ne 测试即可排除空字符串。
本页内容
MongoDB 没有 IS NOT NULL 关键字,因此 SQL 里的这个判断必须用查询运算符来表达。承担这一工作的运算符是 $ne:正如 MongoDB 手册所说,{ field: { $ne: null } } 匹配字段存在且值不为 null 的文档。本指南将构建一个小型示例集合,依次讲解在字符串字段和数值字段上的非 null 查询、与之相对的相等性测试、null 值与缺失字段的区别、空字符串陷阱,以及用 $eq 查询某一个具体值。
准备示例集合
示例使用一个包含七条文档的 teams 集合。其中三条承载了关键情形:Liverpool 的 position: null、Arsenal 的 points: null,以及 Everton 根本没有 position 字段。
db.teams.insertMany([
{ team: "Manchester City", position: "1st", points: 70 },
{ team: "Liverpool", position: null, points: 69 },
{ team: "Chelsea", position: "3rd", points: 59 },
{ team: "Arsenal", position: "4th", points: null },
{ team: "Tottenham", position: "5th", points: 51 },
{ team: "Manchester United", position: "6th", points: 50 },
{ team: "Everton", points: 31 }
])
下文查询中的 { _id: 0 } 投影只是隐藏自动生成的 _id 值,让输出更易读。
使用 $ne 运算符查询不为 Null 的值
要找出 position 不为 null 的球队,就用 $ne 把该字段与 null 比较:
db.teams.find({ position: { $ne: null } }, { _id: 0 })
输出:
[
{ team: 'Manchester City', position: '1st', points: 70 },
{ team: 'Chelsea', position: '3rd', points: 59 },
{ team: 'Arsenal', position: '4th', points: null },
{ team: 'Tottenham', position: '5th', points: 51 },
{ team: 'Manchester United', position: '6th', points: 50 }
]
Liverpool 不在结果中,因为它的 position 是 null。Everton 也不在结果中,而这正是读者最容易弄错的地方:对于针对 null 的比较,MongoDB 把 null 值和缺失字段归为一类(下一节的相等性测试两者都会匹配),所以 $ne: null 返回的是它的补集——只有字段存在且持有值的文档。同样的测试也适用于数值字段。要找出 points 不为 null 的球队:
db.teams.find({ points: { $ne: null } }, { _id: 0 })
输出:
[
{ team: 'Manchester City', position: '1st', points: 70 },
{ team: 'Liverpool', position: null, points: 69 },
{ team: 'Chelsea', position: '3rd', points: 59 },
{ team: 'Tottenham', position: '5th', points: 51 },
{ team: 'Manchester United', position: '6th', points: 50 },
{ team: 'Everton', points: 31 }
]
这一次只有 Arsenal 被排除,因为它的 points 是 null。Everton 留在结果中:它的 points 字段存在且持有值,而缺失的 position 字段与本查询无关。
使用相等性过滤找出 Null 与缺失值
与非 null 测试相对的自然写法是相等性过滤 { field: null }。它匹配字段为 null 或字段根本不存在的文档:
db.teams.find({ position: null }, { _id: 0 })
输出:
[
{ team: 'Liverpool', position: null, points: 69 },
{ team: 'Everton', points: 31 }
]
该过滤把 Liverpool(真正的 null)和 Everton(没有 position 字段)一起返回。把同样的测试写成 { position: { $eq: null } } 行为完全相同——这也正是 $eq 本身不能当作非 null 工具的原因:给它 null,它执行的是这个相等性测试;给它任何其他值,它就只是匹配那个值,如最后一节所示。
区分真正的 Null 与缺失字段
当“null 与缺失”的区别重要时,使用 $type 或 $exists。null 的 BSON 类型编号是 10,所以下面的查询只返回 position 真正为 null 的文档:
db.teams.find({ position: { $type: 10 } }, { _id: 0 })
输出:
[ { team: 'Liverpool', position: null, points: 69 } ]
而下面的查询只返回根本没有 position 字段的文档:
db.teams.find({ position: { $exists: false } }, { _id: 0 })
输出:
[ { team: 'Everton', points: 31 } ]
同时排除空字符串
null 测试对空字符串不作任何判断:文档可以持有 position: "",这是一个真实的值,能通过 $ne: null。再加入两条这样的文档:
db.teams.insertMany([
{ team: "West Ham", position: "", points: 48 },
{ team: "Wolves", position: "", points: 46 }
])
一个诱人的捷径是改拿空字符串来测试:
db.teams.find({ position: { $ne: "" } }, { _id: 0 })
输出:
[
{ team: 'Manchester City', position: '1st', points: 70 },
{ team: 'Liverpool', position: null, points: 69 },
{ team: 'Chelsea', position: '3rd', points: 59 },
{ team: 'Arsenal', position: '4th', points: null },
{ team: 'Tottenham', position: '5th', points: 51 },
{ team: 'Manchester United', position: '6th', points: 50 },
{ team: 'Everton', points: 31 }
]
它返回七条文档,包括 Liverpool(null)和 Everton(缺失):$ne: "" 只排除了两个空字符串,因为其他所有值——包括 null 和缺失字段——都“不等于”""。要得到既不是 null 也不是空字符串的值,用 $and 把两个 $ne 测试组合起来:
db.teams.find(
{ $and: [{ position: { $ne: null } }, { position: { $ne: "" } }] },
{ _id: 0 }
)
输出:
[
{ team: 'Manchester City', position: '1st', points: 70 },
{ team: 'Chelsea', position: '3rd', points: 59 },
{ team: 'Arsenal', position: '4th', points: null },
{ team: 'Tottenham', position: '5th', points: 51 },
{ team: 'Manchester United', position: '6th', points: 50 }
]
使用 $eq 匹配某一个具体值
最后,$eq 是相等性运算符:它匹配字段等于给定值的文档。当你已经知道要找的值时,它才是合适的工具——例如排在第 3 位的球队:
db.teams.find({ position: { $eq: "3rd" } }, { _id: 0 })
输出:
[ { team: 'Chelsea', position: '3rd', points: 59 } ]
简写形式 { position: "3rd" } 含义相同。只是不要把 $eq 当作 null 测试来用:$eq: null 执行的是前面那节的相等性过滤,返回 null 或缺失的文档,与本指南的目标恰好相反。
结论
要在 MongoDB 中查询“is not null”,使用 { field: { $ne: null } }:它精确匹配字段存在且持有值的文档。把相邻的几个测试区分清楚——{ field: null } 找的是 null 或缺失的文档,$type: 10 只找真正的 null,$exists: false 只找缺失字段,而空字符串需要再加一个 $ne 测试。