不能直接用 $eq 写在查询条件顶层,因为 MongoDB 的 $eq 等操作符仅支持字段与字面量比较,无法比较两个字段;必须用 $expr 包裹,如 {"$expr": {"$eq": ["$field1", "$field2"]}},且需注意字段存在性、类型一致性及全表扫描性能问题。

为什么不能直接用 $eq 写在查询条件顶层
因为 MongoDB 的常规查询操作符(如 $eq、$gt)只能比较字段值与**字面量**(比如 {"name": "Alice"}),不能拿两个字段互相比较。一写 {"field1": {"$eq": "$field2"}},MongoDB 会把 "$field2" 当成字符串字面量,不是字段引用——结果永远不匹配。
必须用 $expr 包裹字段比较逻辑
$expr 是唯一允许在查询阶段使用聚合表达式的地方,它让字段间比较成为可能。核心写法是:{"$expr": {"$eq": ["$field1", "$field2"]}}。注意两点:
-
$eq是数组形式,两个元素都必须是带$前缀的字段路径(如"$status"),不能漏掉引号或$ - 字段名必须加双引号,且
$是字符串的一部分,不是语法符号 - 如果字段可能为
null或缺失,$eq会返回false(MongoDB 的三值逻辑),这点和 SQL 的IS NULL不同
示例:查 email 和 backup_email 相同的用户文档:
{"$expr": {"$eq": ["$email", "$backup_email"]}}
字段类型不一致时比较会静默失败
MongoDB 的 $eq 在字段类型不同时(比如一个是字符串 "123",另一个是数字 123)直接返回 false,不会自动转换。这容易被忽略,导致查不到预期结果。
- 先用
db.collection.aggregate([{"$project": {"t1": {"$type": "$field1"}, "t2": {"$type": "$field2"}}}])检查类型 - 若需跨类型比较,得用
$toString或$toInt显式转换,例如:{"$expr": {"$eq": [{"$toString": "$id"}, "$str_id"]}} - 索引对
$expr查询无效——这类查询必然走全表扫描,大数据量时务必评估性能
替代方案:用聚合管道 + $match 更灵活
如果后续还要做其他计算(比如拼接字段、取子串后再比),直接在 $expr 里嵌套表达式会变复杂。这时更适合进聚合流程:
db.collection.aggregate([
{"$addFields": {"normalized_name": {"$toLower": "$name"}}},
{"$match": {"$expr": {"$eq": ["$normalized_name", "$username"]}}}
])
这种写法可拆解步骤、便于调试,也支持更复杂的字段派生逻辑。但记住:只要用了 $expr,就绕不开全集合扫描这个硬限制。
真正要注意的是字段是否存在、类型是否一致、以及性能代价——这三个点没确认清楚,查出来的空结果往往不是语法错,而是逻辑陷阱。


















