【MongoDB】多鍵索引的邊界處理(二)

此文接上篇《多鍵索引的邊界處理(一)

 

二、多鍵索引的複合邊界

複合邊界是指對複合索引的多鍵使用邊界。例如,給定一個複合索引{a:1,b:1},其字段a上的邊界[ [ 3, ] ],字段b上的邊界[ [ -, 6 ] ],則將邊界複合會導致使用兩個界限:

{ a: [ [ 3, ∞] ], b: [ [ -∞, 6 ] ] }

如果MongoDB無法將這兩個邊界組合在一起,則MongoDB始終通過其前導字段的邊界來限制索引掃描,在這種情況下,a: [ [ 3, ] ]

1.數組字段上的複合索引

考慮一個複合多鍵索引(其中一個索引字段是數組的複合索引)。例如,一個集合survey包含帶有一個字段 item 和一個數組字段ratings的文檔:

{ _id: 1, item: "ABC", ratings: [ 2, 9 ] }

{ _id: 2, item: "XYZ", ratings: [ 4, 3 ] }

item字段和ratings字段上創建複合索引:

db.survey.createIndex( { item: 1, ratings: 1 } )

以下查詢在索引的兩個鍵上指定一個條件:

db.survey.find( { item: "XYZ", ratings: { $gte: 3 } } )

將謂詞分解:

  • item的邊界:“ XYZ”謂詞是[ [ "XYZ", "XYZ" ] ];
  • ratings的邊界:{$ gte:3}謂詞是[ [ 3, ] ]

MongoDB可以將兩個邊界組合起來以使用以下組合的邊界:

{ item: [ [ "XYZ", "XYZ" ] ], ratings: [ [ 3, ∞] ] }

2.標量索引字段上的範圍查詢(WiredTiger)

標量字段是其值既不是文檔也不是數組的字段。例如一個值爲字符串或整數的字段是標量字段。

標量字段可以是嵌套在文檔中的字段,只要該字段本身不是數組或文檔即可。例如,在文檔{a:{b:{c:5,d:5}}}中,c和d是標量字段,而a和b不是。

標量類型(Scalar type)是相對複合類型(Compound type)來說的:標量類型只能有一個值,而複合類型可以包含多個值。複合類型是由標量類型構成的。
在C語言中,整數類型(int、short、long等)、字符類型(char、wchar_t等)、枚舉類型(enum)、小數類型(float、double等)、布爾類型(bool)都屬於標量類型,一份標量類型的數據只能包含一個值。例如:
int n = 100;

n 就表示100這一個整數。當然,n的值可以被改變,但無論如何,都只能表示一個值。
結構體(struct)、數組都屬於複合類型,一份複合類型的數據可以包含多個標量類型的值,也可以包含其他複合類型的值。例如:
int nums[ ] = { 5, 38, 49, 92 };

在版本3.4中進行了更改:僅對於WiredTiger和內存中存儲引擎,

從MongoDB 3.4開始,對於使用MongoDB 3.4或更高版本創建的多鍵索引,MongoDB跟蹤一個或多個多鍵索引字段。跟蹤此信息使MongoDB的查詢索引邊界更嚴格。

上述複合索引位於標量字段 item和數組字段ratings上:

db.survey.createIndex( { item: 1, ratings: 1 } )

對於WiredTiger和In-Memory存儲引擎,如果查詢操作在MongoDB 3.4或更高版本中創建的複合多鍵索引標量字段上指定多個謂詞,則MongoDB將與該字段的邊界相交

例如,以下操作在標量字段上指定範圍查詢以及在數組字段上指定範圍查詢:

db.survey.find( {

   item: { $gte: "L", $lte: "Z"}, ratings : { $elemMatch: { $gte: 3, $lte: 6 } }

} )

MongoDB將把item的邊界相交後爲[ [ "L", "Z" ] ] ,並將ratings的邊界相交後爲[[3.0, 6.0]],以使用以下組合的邊界:

"item" : [ [ "L", "Z" ] ], "ratings" : [ [3.0, 6.0] ]

再舉一個例子,考慮標量字段屬於嵌套文檔的位置。例如,survey 集合包含以下文檔:

{ _id: 1, item: { name: "ABC", manufactured: 2016 }, ratings: [ 2, 9 ] }

{ _id: 2, item: { name: "XYZ", manufactured: 2013 },  ratings: [ 4, 3 ] }

在標量字段“ item.name”,“ item.manufactured”和數組字段ratings 上創建複合多鍵索引:

db.survey.createIndex( { "item.name": 1, "item.manufactured": 1, ratings: 1 } )

考慮以下在標量字段上指定查詢謂詞的操作:

db.survey.find( {

   "item.name": "L" ,

   "item.manufactured": 2012

} )

對於此查詢,MongoDB可以使用以下組合範圍:

"item.name" : [ ["L", "L"] ], "item.manufactured" : [ [2012.0, 2012.0] ]

MongoDB的早期版本無法將這些範圍組合爲標量字段。

 

3.嵌入式文檔數組中字段的複合索引

如果數組包含嵌入式文檔,則要在嵌入式文檔中的字段上建立索引,請在索引規範中使用虛線的字段名稱。例如,給定以下嵌入式文檔數組:

ratings: [ { score: 2, by: "mn" }, { score: 9, by: "anon" } ]

score 字段的附點字段名稱爲“ ratings.score”。

 

4.非數組字段和數組字段的複合界

考慮一個集合survey2包含具有字段item和數組字段ratings的文檔:

{

  _id: 1,

  item: "ABC",

  ratings: [ { score: 2, by: "mn" }, { score: 9, by: "anon" } ]

}

{

  _id: 2,

  item: "XYZ",

  ratings: [ { score: 5, by: "anon" }, { score: 7, by: "wv" } ]

}

在非數組字段item 以及數組ratings.score和ratings.by的兩個字段上創建複合索引:

db.survey2.createIndex( { "item": 1, "ratings.score": 1, "ratings.by": 1 } )

以下查詢在所有三個字段上指定一個條件:

db.survey2.find( { item: "XYZ",  "ratings.score": { $lte: 5 }, "ratings.by": "anon" } )

將謂詞分解後:

  • 謂詞  item: "XYZ" 的範圍是[ [ "XYZ", "XYZ" ] ];
  • 謂詞  score: { $lte: 5 } 的範圍是 [ [ -, 5 ] ];
  • 謂詞  by: "anon"  的範圍是 [ "anon", "anon" ]

MongoDB可以根據查詢謂詞和索引鍵值,將item鍵的範圍與"ratings.score"的範圍或"ratings.by"的範圍組合在一起。 MongoDB不保證哪個字段與item字段的邊界進行復合。例如,MongoDB將選擇將item邊界與"ratings.score"邊界進行復合:

{

  "item" : [ [ "XYZ", "XYZ" ] ],

  "ratings.score" : [ [ -Infinity, 5 ] ],

  "ratings.by" : [ [ MinKey, MaxKey ] ]

}

或者,MongoDB可以選擇將item邊界與"ratings.by"邊界進行復合:

{

  "item" : [ [ "XYZ", "XYZ" ] ],

  "ratings.score" : [ [ MinKey, MaxKey ] ],

  "ratings.by" : [ [ "anon", "anon" ] ]

}

但是,要將"ratings.score"的界限與" ratings.by"的界限混合在一起,查詢必須使用$ elemMatch。有關更多信息,請參見數組中索引字段的複合邊界

 

5.數組中索引字段的複合邊界

要將來自同一數組的索引鍵的邊界複合在一起:

  1. 索引鍵必須共享相同的字段路徑,但不能超過字段名稱,並且
  2. 查詢必須指定字段上的謂詞在該路徑上使用$ elemMatch

對於嵌入式文檔中的字段,附點字段名稱(例如“ a.b.c.d”)是d的字段路徑。要複合來自同一數組的索引鍵的邊界,$ elemMatch必須位於直到但不包括字段名稱本身的路徑上;即“ a.b.c”。

 

例如,在ratings.score和ratings.by字段上創建複合索引:

db.survey2.createIndex( { "ratings.score": 1, "ratings.by": 1 } )

字段“ ratings.score”和“ ratings.by”共享字段路徑ratings。以下查詢在字段ratings 上使用$ elemMatch,要求數組包含至少一個與兩個條件都匹配的元素:

db.survey2.find( { ratings: { $elemMatch: { score: { $lte: 5 }, by: "anon" } } } )

將謂詞分解:

  • 謂詞 score: { $lte: 5 } 的範圍是 [ -, 5 ];
  • 謂詞 by: "anon" 的範圍是 [ "anon", "anon" ]

MongoDB可以將兩個邊界組合起來以使用以下組合的邊界:

{ "ratings.score" : [ [ -∞, 5 ] ], "ratings.by" : [ [ "anon", "anon" ] ] }

 

6.沒有$ elemMatch的查詢

如果查詢未使用$ elemMatch聯接索引數組字段中的條件,則MongoDB無法複合其邊界。考慮以下查詢:

db.survey2.find( { "ratings.score": { $lte: 5 }, "ratings.by": "anon" } )

由於數組中的單個嵌入式文檔不需要同時滿足兩個條件,因此MongoDB不會增加界限。使用複合索引時,如果MongoDB無法約束索引的所有字段,則MongoDB始終會約束索引的前導字段,在這種情況下爲“ ratings.score”:

{

  "ratings.score": [ [ -∞, 5 ] ],

  "ratings.by": [ [ MinKey, MaxKey ] ]

}

7.$ elem匹配不完整的路徑

如果查詢未在嵌入字段的路徑上指定$ elemMatch(最多但不包括字段名),則MongoDB無法複合來自同一數組的索引鍵的範圍。

例如,集合Survey3包含具有字段項和數組字段等級的文檔:

{

  _id: 1,

  item: "ABC",

  ratings: [ { scores: [ { q1: 2, q2: 4 }, { q1: 3, q2: 8 } ], loc: "A" },

             { scores: [ { q1: 2, q2: 5 } ], loc: "B" } ]

}

{

  _id: 2,

  item: "XYZ",

  ratings: [ { scores: [ { q1: 7 }, { q1: 2, q2: 8 } ], loc: "B" } ]

}

在ratings.scores.q1和ratings.scores.q2字段上創建複合索引:

db.survey3.createIndex( { "ratings.scores.q1": 1, "ratings.scores.q2": 1 } )

字段“ ratings.scores.q1”和“ ratings.scores.q2”共享字段路徑“ ratings.scores”,並且$ elemMatch必須在該路徑上。

但是,以下查詢使用$ elemMatch,但未在所需路徑上使用:

db.survey3.find( { ratings: { $elemMatch: { 'scores.q1': 2, 'scores.q2': 8 } } } )

因此,MongoDB無法合併邊界,並且在索引掃描期間“ ratings.scores.q2”字段將不受限制。要增加界限,查詢必須在路徑“ ratings.scores”上使用$ elemMatch:

db.survey3.find( { 'ratings.scores': { $elemMatch: { 'q1': 2, 'q2': 8 } } } )

 

上一篇:多鍵索引

原文:https://docs.mongodb.com/manual/core/multikey-index-bounds/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章