2014-01-09 40 views
1

我使用聚合框架來分組一些數據。據觀察,當使用$ project pipeline階段時,它會以某種方式阻止跟隨$ match使用索引。 我有一個字段'時間戳'的索引,集合包含500 000條記錄。

如果我使用下面的命令和管道:

db.collection.runCommand('aggregate', {pipeline: [ { "$match" : { "timestamp" : { "$gt" : 1388425361294 , "$lt" : 1388443361294}}} ], explain: true}) 

執行計劃是相當多的期望是什麼,也就是4號文件掃描。 'explain'的摘錄:

"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 
         ] 
       ] 
     }, 
     ....... 

但是,一旦我使用任何$ project參數,行爲就會徹底改變。下面的命令(「國家」字段可能甚至沒有任何文檔的存在,它不會使任何區別):

db.collection.runCommand('aggregate', {pipeline: [ { "$project" : { "country" : "$country"} , { "$match" : { "timestamp" : { "$gt" : 1388425361294 , "$lt" : 1388443361294}}} ], explain: true}) 

產生這個計劃:

"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" : { 

      }, 

....

顯然會強制掃描集合的所有記錄,這對我來說是不可接受的。

我在使用$ project pipeline階段時錯過了一些重要的東西嗎?

回答

2

如果您首先使用$project,那麼您正在進行收集掃描,使用該表格輸出該集合中的所有文檔。這是相同的話說:

「給我的所有文檔的收集只與country場和_id

然後這個結果被傳遞到下一個管道這恰好是一個$match導致全收集掃描。當然,由於$match不能再使用索引,因此當然會有兩個完整的收集掃描。

您可能可以執行索引掃描而不是集合,但正如您所說的,唯一真正的方法是實際切換兩者的順序,以限制文檔,然後進行項目。

+0

非常感謝,爲了全面的解釋。 –

0

似乎像$ project和$ match應該以相反的順序在這種情況下使用。