只有首个$match能触发索引扫描,因其被优化器映射为底层索引查找;后续$match仅内存过滤,且若首阶段非$match(如$lookup)、含$expr或$regex无锚点等,索引均失效。

$match 放在聚合管道最开头,才能真正用上索引。其他位置的 $match 无法触发索引扫描,只会走全表扫描(COLLSCAN),哪怕查询条件完全匹配索引字段。
为什么只有第一个 $match 能走索引
MongoDB 的查询优化器只会在聚合管道的「入口阶段」将 $match 映射为底层的索引查找操作。一旦管道进入后续阶段(比如 $project、$addFields、$lookup),数据流已脱离原始集合结构,后续 $match 就只能对中间结果做内存过滤。
- 即使你写两个 $match,第二个也
$match不会用索引 - 如果第一个阶段不是
$match(比如是$lookup或$unwind),那整个管道就彻底失去索引能力 - 复合索引字段顺序必须严格匹配
$match中的查询顺序,否则部分字段可能失效
常见误用:看似合理,实则索引失效
这些写法会让索引形同虚设:
- 在
$lookup后加$match筛选关联集合字段——查的是临时生成的嵌套文档,不走任何索引 - 先
$addFields计算一个新字段,再用这个新字段$match——新字段不存在于原始索引中 -
$match里用了$expr,但没配$gt/$lt等可下推的操作符——多数$expr表达式无法下推到索引层
带文本搜索的 $match 必须是第一阶段
如果你要用 $text,$match 不仅得是第一个阶段,还必须满足:
- 该阶段内只能有一个
$text表达式 - 不能和
$or、$not套在一起 - 字段必须提前建好
text索引:db.collection.createIndex({ title: "text", content: "text" }) - Atlas 用户优先考虑
$search复合操作符,性能比$text+$match高得多
怎么确认索引真被用了
别只看结果对不对,要查执行计划:
- 加
.explain("executionStats"),看executionStages.stage是否为IXSCAN - 检查
totalDocsExamined是否接近nReturned;如果前者远大于后者,大概率没走索引 - 注意
indexName字段是否是你预期的那个索引名
容易被忽略的一点:即使 $match 在第一位,如果查询条件里有正则表达式($regex)且没加锚点(^),MongoDB 仍可能放弃索引。











