排序规则(Collations)
在此页面上
Overview
在本指南中,您可以了解如何在 MongoDB 中使用排序规则,按字符串值对查询或聚合操作结果进行排序。 排序规则是一组适用于特定语言和区域设置的字符排序和匹配规则。
您可以在本指南的以下部分了解有关排序规则的更多信息:
示例样本数据
本页上的示例使用 MongoDB 集合,其中包含以下文档:
{ "_id" : 1, "firstName" : "Klara" } { "_id" : 2, "firstName" : "Gunter" } { "_id" : 3, "firstName" : "Günter" } { "_id" : 4, "firstName" : "Jürgen" } { "_id" : 5, "firstName" : "Hannah" }
这些文档由以下数据类表示:
data class FirstName( val id: Int, val firstName: String, val verified: Boolean = false )
MongoDB 中的排序规则
MongoDB 默认使用二进制排序规则对字符串进行排序。 二进制排序规则使用 ASCII 标准 用于比较和排序字符串的字符值。某些语言和区域设置具有与 ASCII 字符值不同的特定字符排序约定。
例如,在加拿大法语中,当前面的所有字符都相同时,最右边的重音字符(变音符号)决定字符串的顺序。 考虑以下加拿大法语单词:
cote
coté
côte
côté
使用二进制排序规则时,MongoDB 按以下顺序对它们进行排序:
cote coté côte côté
当使用加拿大法语排序规则时,MongoDB 按不同的顺序对其进行排序,如下所示:
cote côte coté côté
如何指定排序规则
MongoDB支持大多数 CRUD操作和聚合的排序规则。 有关支持的操作的完整列表,请参阅《支持排序规则的操作》服务器手册页面。
您可以使用以下字符串格式指定区域设置代码和可选变体:
"<locale code>@collation=<variant code>"
以下示例指定了 "de" 区域设置代码和 "phonebook" 变体代码:
"de@collation=phonebook"
如果不需要指定变体,请省略区域设置代码之后的所有内容,如下所示:
"de"
有关支持的区域设置的完整列表,请参阅有关支持的语言和区域设置的服务器手册页面。
以下部分向您展示在 MongoDB 中应用排序规则的不同方法:
Collection
您可以在创建collection时设置默认排序规则。使用指定排序规则创建集合时,扫描该集合的所有受支持操作都会应用该排序规则。
只有在创建集合时,您才能将默认排序规则分配给该集合。但是,您可以在现有集合的新索引中指定排序规则。更多信息,请参阅本指南的索引部分。
以下代码片段展示了在创建名为 items
的新collection时,如何指定“en_US”区域设置排序规则:
database.createCollection( "names", CreateCollectionOptions().collation( Collation.builder().locale("en_US").build() ) )
要检查是否成功创建排序规则,请检索该collection上的索引列表,如下所示:
val collection = database.getCollection<FirstName>("names") val indexInformation = collection.listIndexes().first() println(indexInformation.toJson())
{ // ... "collation": { "locale": "en_US", // ... }, // ... }
Index
在collection上创建新索引时,可以指定排序规则。索引会将文档的有序表示形式存储在collection中,因此您的操作无需在内存中执行排序。要使用索引,您的操作必须满足以下条件:
该操作使用与索引中指定的排序规则相同的排序规则。
该操作由包含排序规则的索引涵盖。
以下代码片段展示了如何在使用“en_US”区域设置排序规则的“firstName”字段上按升序创建索引:
val collection = database.getCollection<FirstName>("names") val idxOptions = IndexOptions().collation(Collation.builder().locale("en_US").build()) collection.createIndex(Indexes.ascending(FirstName::firstName.name), idxOptions)
要检查是否成功创建排序规则,请检索该collection上的索引列表,如下所示:
val collection = database.getCollection<FirstName>("names") val indexInformation = collection.listIndexes().first() println(indexInformation.toJson())
{ // ... "collation": { "locale": "en_US", // ... }, // ... }
以下代码片段显示了一个示例操作,该操作指定相同的排序规则,并由我们在前面的代码片段中创建的索引覆盖:
val resultsFlow = collection.find() .collation(Collation.builder().locale("en_US").build()) .sort(Sorts.ascending(FirstName::firstName.name));
操作
您可以通过将新排序规则作为参数传递给支持的操作之一来覆盖集合上的默认排序规则。 但是,由于该操作不使用索引,因此该操作的性能可能不如使用索引的操作。 有关索引未涵盖的排序操作缺点的更多信息,请参阅有关使用索引对查询结果进行排序的服务器手册页面。
以下代码片段显示了具有以下特征的示例查询操作:
引用的集合包含默认排序规则“en_US”,其类似于集合部分中指定的排序规则。
查询指定了冰岛语 ("is") 排序规则,但集合的默认排序规则索引未涵盖该排序规则。
由于索引未涵盖指定的排序规则,因此排序操作在内存中执行。
val findFlow = collection.find() .collation(Collation.builder().locale("is").build()) .sort(Sorts.ascending(FirstName::firstName.name))
不支持排序规则的索引类型
虽然大多数 MongoDB 索引类型都支持排序规则,但以下类型仅支持二进制比较:
排序规则选项
本节介绍各种排序规则选项以及如何指定它们以进一步完善排序和匹配行为。
排序规则选项 | 说明 |
---|---|
locale | Required. The ICU locale code for language and variant. locale() API Documentation |
backwards | Whether to consider diacritics from the end of the string first. backwards() API Documentation |
区分大小写 | Whether to consider case (upper or lower) as different values. caseLevel() API Documentation |
替代方案 | Whether to consider spaces and punctuation. collationAlternate() API Documentation |
caseFirst | Whether to consider uppercase or lowercase first. collationCaseFirst() API Documentation |
最大变量 | Whether to ignore whitespace or both whitespace and punctuation. This setting is only valid when the alternate setting is "shifted". collationMaxVariable() API Documentation |
strength | ICU level of comparison. The default value is "tertiary". For more information on each level, see the ICU Comparison Levels. collationStrength() API Documentation |
normalization | Whether to perform unicode normalization on the text as needed. For more information on unicode normalization, see Unicode Normalization Forms. normalization() API Documentation |
numericOrdering | Whether to order numbers according to numeric value rather than collation order. numericOrdering() API Documentation |
您可以使用Collation.Builder
类为上述排序规则选项指定值。 您可以调用build()
方法来构造Collation
对象,如以下代码片段所示:
Collation.builder() .caseLevel(true) .collationAlternate(CollationAlternate.SHIFTED) .collationCaseFirst(CollationCaseFirst.UPPER) .collationMaxVariable(CollationMaxVariable.SPACE) .collationStrength(CollationStrength.SECONDARY) .locale("en_US") .normalization(false) .numericOrdering(true) .build()
有关相应方法和它们采用的参数的更多信息,请参阅 Collation.Builder 的 API 文档。
排序规则示例
本节包含的示例演示如何使用一系列支持排序规则的 MongoDB 操作。
在以下示例中,我们将指定"de@collation=phonebook"
区域设置和变体排序规则。 排序规则的“de”部分指定德语区域设置,“collation=phonebook”部分指定变体。 “de”区域设置排序规则包含对专有名词进行优先排序的规则,通过首字母大写进行标识。 在“collation=phonebook”变体中,按升序排序,带变音符号的字符排在不带变音符号的相同字符之前。
find() 和 sort() 示例
以下示例演示了如何在从collection中检索排序结果时应用排序规则。要执行此操作,请对示例collection调用find()
,并链接collation()
和sort()
方法以指定接收结果的顺序。
当我们对示例集合执行此操作时,输出应如下所示:
val resultsFlow = collection.find() .collation(Collation.builder().locale("de@collation=phonebook").build()) .sort(Sorts.ascending(FirstName::firstName.name)) resultsFlow.collect { println(it) }
FirstName(id=3, firstName=Günter, verified=false) FirstName(id=2, firstName=Gunter, verified=false) FirstName(id=5, firstName=Hannah, verified=false) FirstName(id=4, firstName=Jürgen, verified=false) FirstName(id=1, firstName=Klara, verified=false)
有关本节中提到的方法和类的详情,请参阅以下 API 文档:
findOneAndUpdate() 示例
本部分演示如何在更新查询中第一个匹配项的操作中指定排序规则。要指定此操作的排序规则,请实例化一个 FindOneAndUpdateOptions
对象,为其设置排序规则,然后将其作为参数传递给对 findOneAndUpdate()
方法的调用。
在此示例中,我们将演示以下内容:
按升序检索示例集合中“Gunter”之前的第一个文档。
设置操作选项,包括
"de@collation=phonebook"
排序规则。添加值为“true”的新字段“verified”。
检索并打印更新后的文档。
注意
为方便起见,以下代码示例使用了来自import com.mongodb.client.model
包的导入。
由于使用de@collation=phonebook
排序规则按升序排列时,“Günter”在词法上位于“Gunter”之前,因此以下操作在结果中在“Gunter”之前返回“Günter”:
val result = collection.findOneAndUpdate( Filters.lt(FirstName::firstName.name, "Gunter"), Updates.set("verified", true), FindOneAndUpdateOptions() .collation(Collation.builder().locale("de@collation=phonebook").build()) .sort(Sorts.ascending(FirstName::firstName.name)) .returnDocument(ReturnDocument.AFTER) ) println(result)
FirstName(id=3, firstName=Günter, verified=true)
有关本节中提到的方法和类的详情,请参阅以下 API 文档:
findOneAndDelete() 示例
本部分演示如何在删除查询中第一个匹配项的操作中指定排序规则中字符串的数字顺序。 要为此操作指定排序规则,请实例化一个FindOneAndDeleteOptions
对象,为其设置数字排序规则,并将其作为参数传递给findOneAndDelete()
方法。
此示例对包含以下文档的collection调用findOneAndDelete()
操作:
{ "_id" : 1, "a" : "16 apples" } { "_id" : 2, "a" : "84 oranges" } { "_id" : 3, "a" : "179 bananas" }
这些文档由以下数据类表示:
data class CollationExample( val id: Int, val a: String)
在排序规则中,我们将locale
选项设置为“en”,将numericOrdering
选项设置为“true”,以便根据字符串的数字顺序对字符串进行排序。
注意
为方便起见,以下代码示例使用了来自import com.mongodb.client.model
包的导入。
val result = collection.findOneAndDelete( Filters.gt(CollationExample::a.name, "100"), FindOneAndDeleteOptions() .collation(Collation.builder().locale("en").numericOrdering(true).build()) .sort(Sorts.ascending(CollationExample::a.name)) ) println(result)
CollationExample(id=3, a=179 bananas)
字符串“179”的数值大于数字 100,因此前面的文档是唯一匹配项。
如果我们对包含三个文档的原始集合执行相同的操作而不使用数字排序规则,则过滤器会匹配所有文档,因为按照二进制排序规则排序时,“100”在“16”、“84”、“179”之前。
有关本节中提到的方法和类的详情,请参阅以下 API 文档:
聚合示例
本节演示如何在聚合操作中指定排序规则。 在聚合操作中,您可以指定一系列聚合阶段,这些阶段统称为聚合管道。 要执行聚合,请对MongoCollection
对象调用aggregate()
方法。
要为聚合操作指定排序规则,请对聚合操作返回的AggregateFlow
调用collation()
方法。 确保在聚合管道中指定要应用排序规则的排序聚合阶段。
以下示例展示了如何在示例集合上构建聚合管道,并通过指定以下内容来应用排序规则:
群组聚合阶段,使用
Aggregates.group()
firstName
辅助程序通过文档的字段识别每个文档,并将该值用作结果的_id
。群组聚合阶段的累加器,用于对
firstName
字段中匹配值的实例数求和。对先前聚合阶段的输出文档的
_id
字段应用升序排序。构造排序规则对象,指定德语区域设置以及忽略重音和变音符号的排序规则强度。
data class Result( val id: String, val nameCount: Int) val groupStage = Aggregates.group( "\$${FirstName::firstName.name}", Accumulators.sum("nameCount", 1) ) val sortStage = Aggregates.sort(Sorts.ascending("_id")) val resultsFlow = collection.aggregate<Result>(listOf(groupStage, sortStage)) .collation( Collation.builder().locale("de") .collationStrength(CollationStrength.PRIMARY) .build() ) resultsFlow.collect { println(it) }
Result(id=Gunter, nameCount=2) Result(id=Hannah, nameCount=1) Result(id=Jürgen, nameCount=1) Result(id=Klara, nameCount=1)
有关本节中提到的方法和类的详情,请参阅以下 API 文档: