校对
本页内容
概述
在本指南中,您可以了解如何使用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 中应用不同的排序方式
集合
您可以在创建集合时设置默认排序规则。当您使用指定的排序规则创建集合时,所有支持的扫描该集合的操作都会应用该排序规则的规则。
您只能在创建集合时将其设置为默认排序规则。然而,您可以在现有集合上的新索引中指定排序规则。有关更多信息,请参阅本指南的索引部分。
以下代码片段显示了如何在创建名为items
:
database.createCollection( "names", CreateCollectionOptions().collation( Collation.builder().locale("en_US").build() ) )
的集合时指定 "en_US" 区域设置排序规则
val collection = database.getCollection<FirstName>("names") val indexInformation = collection.listIndexes().first() println(indexInformation.toJson())
{ // ... "collation": { "locale": "en_US", // ... }, // ... }
索引
您可以在创建集合上的新索引时指定排序规则。索引存储了集合中文档的有序表示,因此您的操作不需要在内存中执行排序。要使用索引,您的操作必须满足以下标准
操作使用与索引中指定的排序规则相同的排序规则。
操作被包含排序规则的索引所覆盖。
以下代码片段显示了如何创建一个索引,该索引以升序对 "firstName" 字段使用 "en_US" 区域设置排序规则
val collection = database.getCollection<FirstName>("names") val idxOptions = IndexOptions().collation(Collation.builder().locale("en_US").build()) collection.createIndex(Indexes.ascending(FirstName::firstName.name), idxOptions)
的集合时指定 "en_US" 区域设置排序规则
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索引类型支持排序规则,但以下类型仅支持二进制比较:
校对选项
本节介绍了各种校对选项及其如何指定,以便进一步细化排序和匹配行为。
校对选项 | 描述 |
---|---|
区域设置 | 必填。语言和变体的ICU区域代码。 locale() API 文档 |
反向 | 是否首先考虑字符串末尾的变音符号。 backwards() API 文档 |
大小写敏感 | 是否将大小写(大写或小写)视为不同的值。 caseLevel() API 文档 |
备选 | 是否考虑空格和标点符号。 collationAlternate() API 文档 |
大小写优先 | 是否考虑大写或小写优先。 collationCaseFirst() API 文档 |
最大变量 | 是否忽略空格或空格和标点符号。此设置仅在备选设置为“移位”时有效。 collationMaxVariable() API 文档 |
强度 | ICU 比较级别。默认值是 "三级"。有关每个级别的更多信息,请参阅ICU 比较级别。 collationStrength() API 文档 |
规范化 | 是否根据需要对文本执行 Unicode 规范化。有关 Unicode 规范化的更多信息,请参阅 Unicode 规范化形式。 normalization() API 文档 |
数字排序 | 是否按数字值而不是排序顺序对数字进行排序。 numericOrdering() API 文档 |
您可以使用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”部分指定了一个变体。德语区域排序包含优先考虑专有名词的规则,通过首字母的大写来识别。在“collation=phonebook”变体中,带重音符号的字符在升序排序中排在相同不带重音符号的字符之前。
find() 和 sort() 示例
以下示例演示了如何从集合中检索排序结果时应用排序规则。要执行此操作,请在示例集合上调用 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"
排序规则。添加一个新字段 "verified",其值为 "true"。
检索并打印更新后的文档。
注意
以下代码示例使用了从 import com.mongodb.client.model
包中导入的便捷导入。
由于 "Günter" 使用 de@collation=phonebook
排序规则在升序中位于 "Gunter" 的词法之前,因此以下操作在结果中返回 "Günter" 在 "Gunter" 之前
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()
方法的调用。
此示例在一个包含以下文档的集合上调用 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文档