排序规则
从MongoDB 3.4版开始支持排序。
概述
本指南展示了如何使用 排序规则,一组排序规则,以特定语言和地区(一个共享共同语言习语的社区或地区)的字符串排序来运行操作。
默认情况下,MongoDB使用 二进制排序 对字符串进行排序。此排序方法使用ASCII标准 字符值来比较和排序字符串。语言和地区有特定的字符排序惯例,与ASCII标准不同。
例如,在加拿大法语中,最右侧的重音字符决定了当其他字符相同时字符串的排序。考虑以下法语单词: cote,coté,côte,和 côté。
MongoDB使用默认的二进制排序按以下顺序对它们进行排序
cote coté côte côté
MongoDB使用加拿大法语排序按以下顺序对它们进行排序
cote côte coté côté
用法
在创建新的集合或索引时,您可以指定排序规则。您还可以为CRUD操作和聚合指定排序规则。
当您使用排序规则创建新集合时,您定义了在该集合上调用任何支持排序规则的操作的默认排序规则。您可以通过指定不同的排序规则来覆盖操作的排序规则。
注意
目前,您无法在现有集合上创建排序规则。要使用现有集合的排序规则,请创建具有排序规则的索引,并在该索引上的操作中指定相同的排序规则。
当您使用排序规则创建索引时,您指定了使用该索引的操作的排序顺序。为了在索引中使用排序规则,您必须在操作中提供匹配的排序规则,并且操作必须使用该索引。尽管大多数索引类型支持排序规则,但以下类型仅支持二进制比较
排序规则参数
排序规则对象包含以下参数
collation: { locale: <string>, caseLevel: <bool>, caseFirst: <string>, strength: <int>, numericOrdering: <bool>, alternate: <string>, maxVariable: <string>, backwards: <bool> }
您必须指定locale
字段在排序规则中;其他所有字段都是可选的。有关受支持的区域设置和locale
字段的默认值的完整列表,请参阅受支持的语言和区域设置。有关每个字段的说明,请参阅MongoDB手册中的排序规则文档。
比较示例
在集合上设置默认比较规则
以下示例中,我们创建了一个名为 souvenirs
的新集合,并使用 "fr_CA"
区域设置分配了默认比较规则。该比较规则应用于对该集合执行的所有支持比较的操作。
db.createCollection("souvenirs", { collation: { locale: "fr_CA" }, });
支持比较规则的操作会自动应用集合上定义的比较规则。以下查询搜索 souvenirs
集合并应用 "fr_CA"
区域设置的比较规则
myColl.find({type: "photograph"});
您可以在支持比较规则的操作中指定不同的比较规则作为参数。以下查询指定了 "is"
冰岛区域设置以及带有值为 "upper"
的 caseFirst
可选参数
myColl.find({type: "photograph"}, { collation: { locale: "is", caseFirst: "upper" } } );
为索引分配排序规则
以下示例中,我们在一个集合的 title
字段上创建了一个新的索引,并将排序规则设置为 "en_US"
。
myColl.createIndex( { 'title' : 1 }, { 'collation' : { 'locale' : 'en_US' } });
以下查询使用了我们创建的索引
myColl.find({"year": 1980}, {"collation" : {"locale" : "en_US" }}) .sort({"title": -1});
以下查询 没有 使用我们创建的索引。第一个查询没有包含排序规则,第二个查询包含的强度值与索引上的排序规则不同。
myColl.find({"year": 1980}, {"collation" : {"locale" : "en_US", "strength": 2 }}) .sort({"title": -1});
myColl.find({"year": 1980}) .sort({"title": -1});
排序规则查询示例
读取、更新和删除集合中文档的操作可以使用排序规则。本节包括这些操作的示例。有关支持排序规则的操作的完整列表,请参阅 MongoDB 手册:支持排序规则的操作。
find() 和 sort() 示例
以下示例在一个使用默认二进制排序的集合上调用find()
和sort()
。我们通过将locale
参数的值设置为"de"
来使用德语排序。
myColl.find({ city: "New York" }, { collation: { locale: "de" } }) .sort({ name: 1 });
findOneAndUpdate() 示例
以下示例在一个使用默认二进制排序的集合上调用findOneAndUpdate()
操作。该集合包含以下文档
{ "_id" : 1, "first_name" : "Hans" } { "_id" : 2, "first_name" : "Gunter" } { "_id" : 3, "first_name" : "Günter" } { "_id" : 4, "first_name" : "Jürgen" }
考虑以下在集合上进行的findOneAndUpdate()
操作,它没有指定排序
myColl.findOneAndUpdate( { first_name : { $lt: "Gunter" } }, { $set: { verified: true } } );
由于使用二进制排序时,“Gunter”是第一个排序结果,因此没有任何文档在字典上位于其之前并匹配查询文档中的$lt
比较运算符。因此,该操作没有更新任何文档。
考虑使用指定为de@collation=phonebook
的排序以及本地设置为de@collation=phonebook
的相同操作。此区域指定了collation=phonebook
选项,它包含优先排序专有名词的规则,这些专有名词通过首字母的大写来识别。在de@collation=phonebook
区域和选项中,带重音符号的字符排序在相同不带重音符号的字符之前。
myColl.findOneAndUpdate( { first_name: { $lt: "Gunter" } }, { $set: { verified: true } }, { collation: { locale: "de@collation=phonebook" } }, );
由于使用在findOneAndUpdate()
中指定的de@collation=phonebook
排序,“Günter”在字典上位于“Gunter”之前,该操作返回以下更新后的文档
{ lastErrorObject: { updatedExisting: true, n: 1 }, value: { _id: 3, first_name: 'Günter' }, ok: 1 }
findOneAndDelete() 示例
以下示例在一个使用默认二进制排序并包含以下文档的集合上调用findOneAndDelete()
操作
{ "_id" : 1, "a" : "16" } { "_id" : 2, "a" : "84" } { "_id" : 3, "a" : "179" }
在此示例中,我们将numericOrdering
排序参数设置为true
,以根据数字顺序而不是字典顺序对数字字符串进行排序。
myColl.findOneAndDelete( { a: { $gt: "100" } }, { collation: { locale: "en", numericOrdering: true } }, );
运行上述操作后,集合包含以下文档
{ "_id" : 1, "a" : "16" } { "_id" : 2, "a" : "84" }
如果您在对三个文档原始集合进行相同操作而不进行排序时,它会根据字符串的词汇值("16"
,"84"
和 "179"
)匹配文档,并删除第一个符合查询条件的文档。
await myColl.findOneAndDelete({ a: { $gt: "100" } });
由于所有文档在 a
字段中包含的词汇值都符合条件(大于 "100"
的词汇值),该操作删除了第一个结果。执行上述操作后,集合包含以下文档
{ "_id" : 2, "a" : "84" } { "_id" : 3, "a" : "179" }
聚合示例
要在 aggregate 操作中使用排序,请在管道阶段数组之后传递排序文档到选项字段。
以下示例显示了一个在默认二进制排序上运行的集合的聚合管道。该聚合按 first_name
字段分组,计算每个组的总结果数,并按德语电话簿("de@collation=phonebook"
区域)排序。
注意
在聚合中只能指定一个排序。
myColl.aggregate( [ { $group: { "_id": "$first_name", "nameCount": { "$sum": 1 } } }, { $sort: { "_id": 1 } }, ], { collation: { locale: "de@collation=phonebook" } }, );