我使用聚合框架对一些数据进行分组。据观察,当使用 $project pipeline stage 时,它会以某种方式阻止以下 $match 使用索引。我在字段“timestamp”上有一个索引,集合包含 500 000 条记录。
如果我使用以下命令和管道:
db.collection.runCommand('aggregate', {pipeline: [ { "$match" : { "timestamp" : { "$gt" : 1388425361294 , "$lt" : 1388443361294}}} ], explain: true})
Run Code Online (Sandbox Code Playgroud)
执行计划与预期基本一致,即扫描了 4 个文档。摘自“解释”:
"cursor" : {
"cursor" : "BtreeCursor timestamp_1",
"isMultiKey" : false,
"n" : 4,
"nscannedObjects" : 4,
"nscanned" : 4,
"nscannedObjectsAllPlans" : 4,
"nscannedAllPlans" : 4,
"scanAndOrder" : false,
"indexOnly" : false,
"nYields" : 0,
"nChunkSkips" : 0,
"millis" : 0,
"indexBounds" : {
"timestamp" : [
[
1388425361294,
1388443361294
]
]
},
.......
Run Code Online (Sandbox Code Playgroud)
但是,一旦我使用任何 $project 参数,行为就会发生巨大变化。以下命令(“国家”字段甚至可能不存在于任何文档中,它没有任何区别):
db.collection.runCommand('aggregate', {pipeline: [ { "$project" : { "country" : "$country"} , { "$match" : { "timestamp" : { "$gt" : 1388425361294 , "$lt" : 1388443361294}}} ], explain: true})
Run Code Online (Sandbox Code Playgroud)
产生这个计划:
"cursor" : {
"cursor" : "BasicCursor",
"isMultiKey" : false,
"n" : 500001,
"nscannedObjects" : 500001,
"nscanned" : 500001,
"nscannedObjectsAllPlans" : 50
"nscannedAllPlans" : 500001,
"scanAndOrder" : false,
"indexOnly" : false,
"nYields" : 0,
"nChunkSkips" : 0,
"millis" : 101,
"indexBounds" : {
},
Run Code Online (Sandbox Code Playgroud)
....
显然强制扫描集合的所有记录,这对我来说是无法接受的。
我是否错过了使用 $project pipeline 阶段的一些重要内容?
如果您$project首先进行集合扫描,则以该形式输出该集合中的所有文档。这等于说:
“给我集合中的所有文档,仅包含country字段和_id”
然后,该结果被传递到下一个管道,该管道恰好是$match导致完整收集扫描的管道。当然,您将在这里进行两次完整的集合扫描,因为也不$match能再使用索引。
您也许可以执行索引扫描而不是集合,但正如您所说,唯一真正的方法是实际切换两者的顺序,以便限制文档然后进行项目。
| 归档时间: |
|
| 查看次数: |
3710 次 |
| 最近记录: |