使用索引对查询结果进行排序

在本页面

在 MongoDB 中,排序操作可以通过基于索引中的 ordering 检索文档来获取 sort order。如果查询规划器无法从索引获取 sort order,它将在 memory 中对结果进行排序。使用索引的排序操作通常比不使用索引的操作具有更好的 performance。此外,不使用索引的排序操作将在使用 32 兆字节的 memory 时中止。

注意 由于对 MongoDB 3.6 中 array 字段的排序行为进行了更改,因此在对使用多键索引编制索引的 array 进行排序时,查询计划包括阻塞 SORT 阶段。新的排序行为可能会对 performance 产生负面影响。 在阻塞 SORT 中,sort step 必须使用所有输入才能生成输出。在 non-blocking 或索引排序中,sort step 扫描索引以在请求的 order 中生成结果。

使用单字段索引排序

如果升序索引或降序索引位于单个字段上,则字段上的排序操作可以在任一方向上。

对于 example,在字段a上为集合records创建升序索引:

db.records.createIndex( { a: 1 } )

此索引可以支持a上的升序排序:

db.records.find().sort( { a: 1 } )

索引还可以通过遍历反向 order 中的索引来支持a上的以下降序排序:

db.records.find().sort( { a: -1 } )

在多个字段上排序

创建复合指数以支持对多个字段进行排序。

您可以在索引的所有键或子集上指定排序;但是,排序键必须列在与索引中出现的相同的 order 中。对于 example,索引 key pattern { a: 1, b: 1 }可以支持{ a: 1, b: 1 }上的排序,但不支持{ b: 1, a: 1 }上的排序。

对于使用复合索引进行排序的查询,cursor.sort()文档中所有键的指定排序方向必须匹配索引 key pattern 或 match 索引 key pattern 的反转。对于 example,索引 key pattern { a: 1, b: -1 }可以在{ a: 1, b: -1 }{ a: -1, b: 1 }上支持排序,但在{ a: -1, b: -1 }{a: 1, b: 1}上不支持**。

排序和索引前缀

如果排序键对应于索引键或索引前缀,MongoDB 可以使用索引对查询结果进行排序。复合索引的前缀是由索引 key pattern 开头的一个或多个键组成的子集。

对于 example,在data集合上创建复合索引:

db.data.createIndex( { a:1, b: 1, c: 1, d: 1 } )

然后,以下是该索引的前缀:

{ a: 1 }
{ a: 1, b: 1 }
{ a: 1, b: 1, c: 1 }

以下查询和排序操作使用索引前缀对结果进行排序。这些操作不需要在 memory 中对结果集进行排序。

索引前缀
db.data.find().sort( { a: 1 } ){ a: 1 }
db.data.find().sort( { a: -1 } ){ a: 1 }
db.data.find().sort( { a: 1, b: 1 } ){ a: 1, b: 1 }
db.data.find().sort( { a: -1, b: -1 } ){ a: 1, b: 1 }
db.data.find().sort( { a: 1, b: 1, c: 1 } ){ a: 1, b: 1, c: 1 }
db.data.find( { a: { $gt: 4 } } ).sort( { a: 1, b: 1 } ){ a: 1, b: 1 }

请考虑以下 example,其中索引的前缀键同时出现在查询谓词和排序中:

db.data.find( { a: { $gt: 4 } } ).sort( { a: 1, b: 1 } )

在这种情况下,MongoDB 可以使用索引来检索由排序指定的 order 中的文档。如 example 所示,查询谓词中的索引前缀可以与排序中的前缀不同。

排序和 Non-prefix 索引的子集

索引可以支持索引 key pattern 的 non-prefix 子集上的排序操作。为此,查询必须在排序键之前的所有前缀键上包含相等条件。

对于 example,集合data具有以下索引:

{ a: 1, b: 1, c: 1, d: 1 }

以下操作可以使用索引来获取 sort order:

索引前缀
db.data.find( { a: 5 } ).sort( { b: 1, c: 1 } ){ a: 1 , b: 1, c: 1 }
db.data.find( { b: 3, a: 4 } ).sort( { c: 1 } ){ a: 1, b: 1, c: 1 }
db.data.find( { a: 5, b: { $lt: 3} } ).sort( { b: 1 } ){ a: 1, b: 1 }

如上一个操作所示,只有排序子集之前的索引字段必须在查询文档中具有相等条件;其他索引字段可以指定其他条件。

如果查询确实在索引前缀上指定与排序规范之前或重叠的相等条件,则操作将不会**有效地使用索引。对于 example,以下操作指定{ c: 1 }的排序文档,但查询文档在前面的索引字段ab上不包含相等匹配:

db.data.find( { a: { $gt: 2 } } ).sort( { c: 1 } )
db.data.find( { c: 5 } ).sort( { c: 1 } )

这些操作**不会有效地使用索引{ a: 1, b: 1, c: 1, d: 1 },甚至可能不使用索引来检索文档。

索引使用和整理

要使用索引进行 string 比较,操作还必须指定相同的排序规则。也就是说,如果操作指定了不同的排序规则,则具有排序规则的索引不能支持对索引字段执行 string 比较的操作。

对于 example,集合myColl在 string 字段category上有一个索引,其中包含整理 locale "fr"

db.myColl.createIndex( { category: 1 }, { collation: { locale: "fr" } } )

以下查询操作(指定与索引相同的排序规则)可以使用索引:

db.myColl.find( { category: "cafe" } ).collation( { locale: "fr" } )

但是,以下查询操作(默认情况下使用“简单”二进制文件夹)无法使用索引:

db.myColl.find( { category: "cafe" } )

对于索引前缀键不是 strings,数组和嵌入文档的复合索引,指定不同排序规则的操作仍然可以使用索引来支持对索引前缀键的比较。

对于 example,集合myColl在数字字段scoreprice以及 string 字段category上具有复合索引;使用用于 string 比较的排序规则 locale "fr"创建索引:

db.myColl.createIndex(
   { score: 1, price: 1, category: 1 },
   { collation: { locale: "fr" } } )

以下操作(使用"simple"二进制排序规则进行 string 比较)可以使用索引:

db.myColl.find( { score: 5 } ).sort( { price: 1 } )
db.myColl.find( { score: 5, price: { $gt: NumberDecimal( "10" ) } } ).sort( { price: 1 } )

以下操作在索引的category字段上使用"simple"二进制排序规则进行 string 比较,可以使用索引仅满足查询的score: 5部分:

db.myColl.find( { score: 5, category: "cafe" } )