Docs 菜单
Docs 主页
/
MongoDB Manual
/ / /

多键索引边界

在此页面上

  • 多键索引的边界交集
  • 示例:边界交集
  • 不含 $elemMatch 的查询
  • 多键索引的复合边界
  • 示例:非数组字段和数组字段的复合边界
  • 示例:非数组字段和多个数组字段的复合边界
  • 同一数组中多个字段的复合边界
  • 示例:在字段路径(Field Path)上的 $elemMatch

索引边界定义了 MongoDB 使用索引执行查询时搜索的索引值的范围。 当您在索引字段上指定多个查询谓词时,MongoDB 会尝试合并这些谓词的边界,以生成边界更小的索引扫描。 较小的索引边界可加快查询速度并减少资源使用。

MongoDB 通过相交复合边界来组合边界。

边界交集是指多个边界重叠的点。 例如,给定边界 [ [ 3, Infinity ] ][ [ -Infinity, 6 ] ] ,边界的交集结果为[ [ 3, 6 ] ]

给定一个索引数组字段,考虑一个在数组上指定多个查询谓词并使用多键索引来完成查询的查询。 如果$elemMatch操作符连接查询谓词,MongoDB 可以与多键索引边界相交。

以下示例展示了 MongoDB 如何使用边界交集来定义要查询的较小范围的值,从而提高查询性能。

1

创建students集合,其中包含具有字段name和数组字段grades的文档:

db.students.insertMany(
[
{ _id: 1, name: "Shawn", grades: [ 70, 85 ] },
{ _id: 2, item: "Elena", grades: [ 92, 84 ] }
]
)
2

grades数组上创建多键索引

db.students.createIndex( { grades: 1 } )
3

运行以下查询:

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连接数组字段上的条件,则 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 如何使用复合边界来定义更高效的查询约束,从而提高查询性能。

1

创建survey集合,其中包含具有字段item和数组字段ratings的文档:

db.survey.insertMany(
[
{ _id: 1, item: "ABC", ratings: [ 2, 9 ] },
{ _id: 2, item: "XYZ", ratings: [ 4, 3 ] }
]
)
2

itemratings字段上创建复合多键索引

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

运行以下查询:

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

前面的查询在索引的两个键( itemratings )上指定了一个条件。

分别采用谓词:

  • item: "XYZ"谓词的边界为[ [ "XYZ", "XYZ" ]]

  • ratings: { $gte: 3 }谓词的边界为[ [ 3, Infinity ] ]

MongoDB 使用以下各项的组合边界:

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

以下示例显示了当索引包含一个非数组字段和多个数组字段时,MongoDB 如何使用复合边界。

1

创建survey2集合,其中包含具有字符串字段item和数组字段ratings的文档:

db.survey2.insertMany( [
{
_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" } ]
}
] )
2

对以下字段创建复合索引:

  • item (非数组)

  • ratings.score (数组)

  • ratings.by (数组)

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

运行以下查询:

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集合

1

ratings.scoreratings.by字段上创建复合索引:

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

字段"ratings.score""ratings.by"共享字段路径ratings

2

运行以下查询:

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

如果查询在偏离公共路径的字段上指定$elemMatch ,则 MongoDB无法复合来自同一数组的索引键的边界。

以下示例演示了在分叉字段路径(Field Path)上使用$elemMatch

1

创建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" }
]
}
] )
2

ratings.scores.q1ratings.scores.q2字段上创建复合索引:

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

字段"ratings.scores.q1""ratings.scores.q2"共享字段路径(Field Path)"ratings.scores" 。为了复合索引边界,查询必须在公共字段路径(Field Path)上使用$elemMatch

3

以下查询使用了不在所需路径上的$elemMatch

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

MongoDB 无法复合索引边界,并且"ratings.scores.q2"字段在索引扫描期间不受约束。

要复合边界,查询必须在公共路径$elemMatch 上使用"ratings.scores"

db.survey3.find( { 'ratings.scores': { $elemMatch: { 'q1': 2, 'q2': 8 } } } )
← 为数组中的嵌入字段创建索引