MongoDB $project 和索引使用

Vol*_*kov 5 mongodb

我使用聚合框架对一些数据进行分组。据观察,当使用 $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 阶段的一些重要内容?

Sam*_*aye 5

如果您$project首先进行集合扫描,则以该形式输出该集合中的所有文档。这等于说:

“给我集合中的所有文档,仅包含country字段和_id

然后,该结果被传递到下一个管道,该管道恰好是$match导致完整收集扫描的管道。当然,您将在这里进行两次完整的集合扫描,因为也不$match能再使用索引。

您也许可以执行索引扫描而不是集合,但正如您所说,唯一真正的方法是实际切换两者的顺序,以便限制文档然后进行项目。