多键索引边界
在此页面上
索引边界定义了 MongoDB 使用索引执行查询时搜索的索引值的范围。 当您在索引字段上指定多个查询谓词时,MongoDB 会尝试合并这些谓词的边界,以生成边界更小的索引扫描。 较小的索引边界可加快查询速度并减少资源使用。
多键索引的边界交集
边界交集是指多个边界重叠的点。 例如,给定边界 [ [ 3, Infinity ] ]
和[ [
-Infinity, 6 ] ]
,边界的交集结果为[ [ 3, 6
] ]
。
给定一个索引大量字段,考虑一个在大量上指定多个查询谓词并使用多键索引来完成查询的查询。 如果$elemMatch
操作符连接查询谓词, MongoDB可以与多键索引边界相交。
示例:边界交集
以下示例展示了 MongoDB 如何使用边界交集来定义要查询的较小范围的值,从而提高查询性能。
创建多键索引
在grades
数组上创建多键索引:
db.students.createIndex( { grades: 1 } )
查询集合
运行以下查询:
db.students.find( { grades : { $elemMatch: { $gte: 90, $lte: 99 } } } )
前面的查询使用$elemMatch
返回文档,其中grades
数组至少包含一个与两个指定条件匹配的元素。
分别采用查询谓词:
大于或等于 90 谓词 (
$gte: 90
) 的边界为[ [ 90, Infinity ] ]
。小于或等于 99 谓词 (
$lte: 99
) 的边界是[ [ -Infinity, 99 ] ]
。
由于查询使用$elemMatch
连接这些谓词,因此 MongoDB 与边界相交:
ratings: [ [ 90, 99 ] ]
不含 $elemMatch 的查询
如果查询未使用$elemMatch
连接数组字段上的条件,则 MongoDB 无法与多键索引边界相交。
请考虑以下查询:
db.students.find( { grades: { $gte: 90, $lte: 99 } } )
该查询在grades
数组中搜索:
至少有一个元素大于或等于
90
至少有一个元素小于或等于
99
同一元素可以同时满足这两个条件。
由于前面的查询未使用$elemMatch
,因此 MongoDB 不会与边界相交。 相反,MongoDB 会使用以下任一边界:
[ [ 90, Infinity ] ]
[ [ -Infinity, 99 ] ]
MongoDB 不保证它会选择这两个边界中的哪一个。
多键索引的复合边界
复合边界组合复合索引的多个键的边界。 使用多个键的边界可以减少处理查询所需的时间,因为 MongoDB 不需要单独计算每个边界的结果。
例如,考虑具有以下边界的复合索引{ temperature: 1, humidity: 1
}
:
temperature
边界为[ [ 80, Infinity ] ]
。humidity
边界为[ [ -Infinity, 20 ] ]
。
对边界进行复合会导致使用两个边界:
{ temperature: [ [ 80, Infinity ] ], humidity: [ [ -Infinity, 20 ] ] }
如果 MongoDB 无法组合这两个边界,则 MongoDB 将按前导字段上的边界限制索引扫描。 在此示例中,前导字段为temperature
,因此约束条件为temperature: [
[ 80, Infinity ] ]
。
示例:非数组字段和数组字段的复合边界
以下示例展示了 MongoDB 如何使用复合边界来定义更高效的查询约束,从而提高查询性能。
创建复合多键索引
在item
和ratings
字段上创建复合多键索引:
db.survey.createIndex( { item: 1, ratings: 1 } )
示例:非数组字段和多个数组字段的复合边界
以下示例显示了当索引包含一个非数组字段和多个数组字段时,MongoDB 如何使用复合边界。
查询集合
运行以下查询:
db.survey2.find( { item: "XYZ", "ratings.score": { $lte: 5 }, "ratings.by": "anon" } )
分别采用谓词:
item: "XYZ"
谓词的边界为[ [ "XYZ", "XYZ" ] ]
。score: { $lte: 5 }
谓词的边界为[ [ -Infinity, 5] ]
。by: "anon"
谓词的边界为[ "anon", "anon" ]
。
MongoDB 将item
键的边界与"ratings.score"
的边界或"ratings.by"
的边界进行复合,具体取决于查询谓词和索引键值。 MongoDB 不保证它与item
字段进行复合的边界。
MongoDB 通过以下方式之一完成查询:
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
。
同一数组中多个字段的复合边界
要复合同一数组中索引键的边界,以下两个条件必须为 true:
索引键必须共享相同的字段路径(Field Path),但不包括字段名称。
查询必须在该路径上使用
$elemMatch
指定字段的谓词。
对于嵌入式文档中的字段,虚线字段名称(例如"a.b.c.d"
)是d
的字段路径(Field Path)。要复合来自同一数组的索引键的边界, $elemMatch
必须位于路径上,但不包括字段名称本身(即"a.b.c"
)。
例子
以下示例显示了 MongoDB 如何组合来自同一数组的索引键的边界。此示例使用上一示例中的 survey2
集合。
查询集合
运行以下查询:
db.survey2.find( { ratings: { $elemMatch: { score: { $lte: 5 }, by: "anon" } } } )
前面的查询在ratings
字段上使用$elemMatch
,以要求数组至少包含一个同时匹配这两个条件的单个元素。
分别采用谓词:
score: { $lte: 5 }
谓词的边界为[ [ -Infinity, 5 ] ]
。by: "anon"
谓词的边界为[ [ "anon", "anon" ] ]
。
MongoDB 将两个边界复合为以下边界:
{ "ratings.score" : [ [ -Infinity, 5 ] ], "ratings.by" : [ [ "anon", "anon" ] ] }
示例:在字段路径(Field Path)上的 $elemMatch
如果查询在偏离公共路径的字段上指定$elemMatch
,则 MongoDB无法复合来自同一数组的索引键的边界。
以下示例演示了在分叉字段路径(Field Path)上使用$elemMatch
。
填充样本collection
创建collectionsurvey3
,其中包含具有字符串字段item
和数组字段ratings
的文档:
db.survey3.insertMany( [ { _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" } ] } ] )