MongoDB的聚合管道提供了丰富的表达式操作符,其中$eq专门用于判断两个值是否相等,返回布尔值true或false。它看起来简单,但在实际使用中,很多人会把它和查询语句中的相等操作符混淆,或者在使用$match时错误地嵌套$eq导致查询报错。这篇文章将系统讲解$eq的语法规则、典型应用场景以及常见的踩坑点,帮助你真正掌握聚合管道中的相等判断。

$eq的基本语法与工作原理
$eq是聚合表达式操作符,语法结构为{ $eq: [表达式1, 表达式2] }。它会分别计算两个表达式的值,然后比较这两个值是否相等,相等返回true,不相等返回false。两个表达式可以是字段路径(如$status)、常量、嵌套表达式,甚至可以是从数组元素计算出来的结果。
需要特别注意的是,$eq是聚合表达式,只能在聚合管道的表达式上下文中使用,比如$project、$addFields、$cond的判断条件、$switch的分支条件等位置。它不能直接放在普通的find查询条件里。find查询中的相等匹配直接写{ status: "active" }即可,不需要也不应该使用$eq。
下面是一个基础的示例,假设有一个订单集合,我们想在聚合中为每个文档添加一个布尔字段,标记订单状态是否为已完成:
db.orders.aggregate([
{
$addFields: {
isCompleted: { $eq: ["$status", "completed"] }
}
}
])这段代码会遍历所有文档,将status字段的值与字符串"completed"比较,结果存入新增的isCompleted字段。如果某个文档的status是"completed",输出中isCompleted就是true,否则为false。这种做法在数据加工、报表统计中非常常见。
$eq在$match中的正确用法与常见错误
一个高频错误是在$match阶段直接写{ $match: { $eq: ["$a", "$b"] } },然后收到"$eq is not allowed in this context"之类的报错。原因是$match在普通匹配模式下不支持聚合表达式。正确的做法有两种:第一种是普通字段匹配,直接写等值条件;第二种是当需要比较同一个文档的两个字段是否相等时,使用$expr配合$eq。
$expr的作用是允许在$match中使用聚合表达式,示例如下:
// 查找password和confirmPassword两个字段相等的文档
db.users.aggregate([
{
$match: {
$expr: { $eq: ["$password", "$confirmPassword"] }
}
}
])再比如查找库存数量等于销量的商品:
db.products.aggregate([
{
$match: {
$expr: { $eq: ["$stock", "$soldCount"] }
}
}
])这两种写法在普通find查询中同样适用,db.users.find({ $expr: { $eq: ["$password", "$confirmPassword"] } })也能正常工作。但如果只是简单的字段等于某个固定值,就没有必要绕这个弯,直接写等值条件性能更好,因为普通匹配模式可以充分利用索引,而$expr在某些版本中无法有效利用索引。
配合$cond和$switch实现条件逻辑
$eq最常见的搭配对象是$cond。$cond接受三个参数:条件、条件为真时的结果、条件为假时的结果。$eq通常充当其中的条件部分。例如根据订单金额是否等于某个阈值来打标签:
db.orders.aggregate([
{
$project: {
amount: 1,
level: {
$cond: {
if: { $eq: ["$amount", 100] },
then: "刚好一百",
else: "其他金额"
}
}
}
}
])当条件分支较多时,$switch会比多层嵌套的$cond更清晰。$switch的每个case的then部分都可以使用$eq做判断:
db.orders.aggregate([
{
$project: {
statusText: {
$switch: {
branches: [
{ case: { $eq: ["$status", "pending"] }, then: "待支付" },
{ case: { $eq: ["$status", "paid"] }, then: "已支付" },
{ case: { $eq: ["$status", "completed"] }, then: "已完成" }
],
default: "未知状态"
}
}
}
}
])需要注意,$eq是严格相等判断,不仅比较值还比较类型。数字1和字符串"1"用$eq比较会返回false。这一点和JavaScript的宽松相等不同,更接近JavaScript的严格相等运算符。因此在比较前要确认字段类型,必要时可以先用$convert或$toString做类型转换。
数组与嵌套值的比较规则
$eq对数组的比较遵循逐元素比较的规则。两个数组只有在长度相同且每个位置的元素都相等时才被视为相等,例如{ $eq: [[1, 2], [1, 2]] }返回true,而{ $eq: [[1, 2], [2, 1]] }返回false。但有一个特殊情况:当$eq的一端是数组字段路径,另一端是单个值时,只要数组中任意一个元素等于该值,结果就是true。这继承自查询语义的数组匹配行为。
举个例子,文档中tags是一个数组:
// 文档示例: { _id: 1, tags: ["news", "tech"] }
db.articles.aggregate([
{
$addFields: {
hasTechTag: { $eq: ["$tags", "tech"] }
}
}
])上面代码中hasTechTag的结果是true,因为数组tags包含"tech"。如果想判断整个数组是否等于某个数组,比如tags是否恰好是["tech"]这一个元素,就需要写成{ $eq: ["$tags", ["tech"]] },这样只有当数组内容完全一致时才返回true。理解这两种语义的区别非常重要,否则统计结果会出现偏差。
另一个容易出问题的地方是null和缺失字段。在MongoDB中,字段不存在和字段值为null在$eq的判断下都会与null相等,即{ $eq: ["$missingField", null] }返回true。这一点可以用来筛选缺失字段的文档,但也要警惕它带来的误判,如果业务上需要严格区分字段缺失和字段为null,需要额外配合$type操作符判断。
性能 considerations 与最佳实践总结
在性能方面,如果$eq被包裹在$expr中用于$match阶段,早期版本无法使用索引,大量数据下会触发全集合扫描。从MongoDB 4.4开始,部分简单的$expr等值比较已经可以走索引,但复杂的多字段比较仍然受限。因此建议尽可能把简单的等值过滤放在$match的普通匹配模式下,只在需要跨字段比较时才使用$expr加$eq的组合。
此外,把$eq放在管道越早的阶段执行越好。聚合管道的优化器会尝试将$match移动到管道前端,但如果$match中包含$expr表达式,某些优化会受到限制。合理的做法是先用普通条件过滤掉大部分文档,再在后续阶段使用表达式做精细判断。
总结一下使用要点:第一,$eq是聚合表达式,在$match中使用必须配合$expr;第二,$eq是严格相等,类型不同直接判为不相等;第三,数组与单值比较遵循元素包含语义,与数组比较遵循全等语义;第四,null和缺失字段在$eq看来等价;第五,能用普通等值匹配的场景优先用普通匹配,性能更好。掌握这些细节后,$eq会成为你在聚合管道中做条件判断时最可靠的工具之一。
MongoDB聚合管道$eq相等判断MongoDB条件表达式修改时间:2026-08-31 18:07:07