2012-09-20 111 views
21

我在mongo有一個查詢,這樣我想優先選擇第一個字段,然後第二個字段。排序在多個字段mongo DB

說我有查詢,使得

db.col.find({category: A}).sort({updated: -1, rating: -1}).limit(10).explain() 

因此,我創建了以下索引

db.col.ensureIndex({category: 1, rating: -1, updated: -1}) 

它的工作只是罰款掃描儘可能多的對象,即需要10

但現在我需要查詢

db.col.find({category: { $ne: A}}).sort({updated: -1, rating: -1}).limit(10) 

因此,我創建了以下索引

db.col.ensureIndex({rating: -1, updated: -1}) 

但是這會導致整個文檔的掃描,當我創建

db.col.ensureIndex({ updated: -1 ,rating: -1}) 

它掃描少的文件數量

我只想問要清楚在多個領域進行排序以及在執行此操作時要保留的順序是什麼。通過閱讀mongo-DB文件,清楚地知道我們需要進行排序的領域應該是最後一個領域。所以這是我在上面的$ ne查詢中假設的情況。我做錯了什麼?

回答

25

MongoDB query optimizer通過嘗試不同的計劃來確定哪種方法最適合給定查詢。然後爲下一個約1,000條查詢緩存該查詢模式的獲勝計劃,或者直到您執行explain()

要了解哪些查詢計劃被認爲是,你應該使用explain(1),如:

db.col.find({category:'A'}).sort({updated: -1}).explain(1) 

allPlans細節將顯示進行比較,所有的計劃。

如果您運行的查詢不是非常有選擇性的(例如,如果很多記錄符合您的條件{category: { $ne:'A'}}),MongoDB可能會更快地使用BasicCursor(表掃描)查找結果,而不是匹配索引。

查詢中的字段順序通常對索引選擇沒有影響(範圍查詢有一些例外)。 類別中的字段順序確實會影響索引選擇。如果您的sort()條件與索引順序不匹配,則必須在使用索引後重新排序結果數據(如果發生這種情況,您應在explain輸出中看到scanAndOrder:true)。

另外值得注意的是,MongoDB將只使用one index per query$or的例外)。

所以,如果你想優化查詢:

db.col.find({category:'A'}).sort({updated: -1, rating: -1}) 

你會想包括索引中的所有三個字段:

db.col.ensureIndex({category: 1, updated: -1, rating: -1}) 

僅供參考,如果您想強制某個特定查詢要使用索引(通常不需要或推薦),您可以嘗試使用hint()選項。

+0

這是如何回答這個問題保持「名稱」?您仍然無法確保按照「評分降序,更新降序」而不是「更新降序,評級降序」的順序進行排序。 – jobermark

+0

@jobermark在原始問題中,查詢條件是「類別」,複合排序順序爲「 {updated:-1,rating:-1}'。索引中鍵的順序(和方向)很重要;建議的索引將不會有效地支持「'rating:-1,updated:-1}」排序的'content'的搜索。如果您在驅動程序中操作複合排序值,請使用有序散列/字典來確保排序被保留。有關更多信息,請參閱MongoDB文檔中的[對多個字段進行排序](https://docs.mongodb.org/manual/tutorial/sort-results-with-indexes/#sort-on-multiple-fields)。 – Stennie

1

這是真的,但是由於您在複合索引上進行排序,因此您在此處有兩層排序。

正如你注意到的那樣,當索引的第一個字段與第一個字段的排序工作和索引被看到。然而,當以其他方式工作時,它不會。

就像你自己的obersvations一樣,需要保存的順序是從第一個到最後一個字段的查詢順序。 mongo分析器有時可以在字段中移動以匹配索引,但通常它只會嘗試匹配第一個字段,如果它不能跳過它。

0

試試這個代碼,它會首先基於名稱的數據進行排序,然後在鑰匙扣它將排序「過濾器」

var cursor = db.collection('vc').find({ "name" : { $in: [ /cpu/, /memo/ ] }  }, { _id: 0, }).sort({ "name":1 , "filter": 1 });