Overview
在本指南中,您可以了解如何在 MongoDB 中使用排序规则,按字符串值对查询或聚合操作结果进行排序。 排序规则是一组适用于特定语言和区域设置的字符排序和匹配规则。
您可以在本指南的以下部分中学习有关排序规则的更多信息:
重要
项目 Reactor 库
本指南使用 Project Reactor 库来使用Java Reactive Streams驱动程序方法返回的 Publisher 实例。要学习;了解有关 Project Reactor 库及其使用方法的更多信息,请参阅 Reactor 文档中的入门。要进一步学习;了解如何使用本指南中的 Project Reactor 库方法,请参阅“将数据写入MongoDB”指南。
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 操作和聚合上的排序规则。有关支持操作的完整列表,请参阅 MongoDB Server 手册中的支持排序规则的操作。
您可以使用以下字符串格式指定区域设置代码和可选变体:
"<locale code>@collation=<variant code>"
以下示例指定了 "de" 区域设置代码和 "phonebook" 变体代码:
"de@collation=phonebook"
如果未指定变体,则仅使用区域设置代码。
有关支持的区域设置的完整列表,请参阅 MongoDB Server 手册中的“支持的语言和区域设置”。
以下部分向您展示在 MongoDB 中应用排序规则的不同方法:
Collection
只能在创建过程中为集合设置默认排序规则。但是,您可以在现有集合的新索引中指定排序规则。所有支持的搜索集合的操作都会应用默认排序规则。有关更多信息,请参阅本指南的索引部分。
以下示例展示了在创建名为 items 的新集合时如何指定 "en_US" 区域设置排序规则:
Mono.from(database.createCollection( "items", new CreateCollectionOptions().collation( Collation.builder().locale("en_US").build()))) .block();
要检查是否成功创建排序规则,请检索该collection上的索引列表,如下所示:
List<Document> indexes = Flux.from(itemsCollection.listIndexes()) .collectList().block(); if (indexes != null) { indexes.forEach(idx -> System.out.println(idx.toJson())); }
上述代码的输出应包含以下内容:
{ ... "collation": { "locale": "en_US", ... } ... }
Index
在集合上创建新索引时,可以指定排序规则。索引以指定顺序存储文档,从而无需在查询过程中进行内存排序。要使用索引,操作必须使用与索引中指定的排序规则相同的排序规则,并且覆盖该索引。
以下示例展示了如何使用 "en_US" 区域设置排序规则在“名称”字段上按升序创建索引:
IndexOptions idxOptions = new IndexOptions(); idxOptions.collation(Collation.builder().locale("en_US").build()); Mono.from(itemsCollection.createIndex( Indexes.ascending("name"), idxOptions)).block();
要检查是否成功创建排序规则,请检索该collection上的索引列表,如下所示:
List<Document> indexes = Flux.from(itemsCollection.listIndexes()) .collectList().block(); if (indexes != null) { indexes.forEach(idx -> System.out.println(idx.toJson())); }
上述代码的输出应包含以下内容:
{ ... "collation": { "locale": "en_US", ... } ... }
以下示例显示一个操作,该操作指定相同的排序规则,并由在前面示例中创建的索引覆盖:
FindPublisher<Document> indexPublisher = itemsCollection.find() .collation(Collation.builder().locale("en_US").build()) .sort(Sorts.ascending("name")); Flux.from(indexPublisher) .doOnNext(doc -> System.out.println(doc.toJson())) .blockLast();
操作
您可以通过将新的排序规则传递给支持的操作来覆盖默认排序规则。但是,如果没有索引,您的查询将执行内存排序,这比使用索引排序规则慢。有关索引未覆盖的排序操作的缺点的更多信息,请参阅 MongoDB Server 手册中的使用索引排序查询结果。
以下示例显示了具有以下特征的查询操作:
引用的集合包含默认
"en_US"排序规则索引,其类似于 集合 部分中指定的索引。该查询指定了冰岛语 (
"is") 排序规则。由于这与索引排序规则不同,因此查询不使用索引,而是执行内存排序。
FindPublisher<Document> customPublisher = itemsCollection.find() .collation(Collation.builder().locale("is").build()) .sort(Sorts.ascending("name")); Flux.from(customPublisher) .doOnNext(doc -> System.out.println(doc.toJson())) .blockLast();
不支持排序规则的索引类型
大多数 MongoDB 索引类型都支持排序规则。但是,以下类型仅支持二进制比较,不支持排序规则:
排序规则选项
本节介绍各种排序规则选项以及如何指定它们以进一步完善排序和匹配行为。
排序规则选项 | 说明 |
|---|---|
locale | 必需。语言和变体的 ICU 区域设置代码。 |
backwards | 指定是否首先考虑 string 末尾的变音符。 |
区分大小写 | 指定是否将大小写视为不同的值。 |
替代方案 | 指定是否考虑空格和标点。 |
caseFirst | 指定是否先考虑大写或小写。 |
最大变量 | 指定是否忽略空白字符或忽略空白字符和标点符号。此设置仅在替代设置为 "shifted" 时有效。 |
strength | 指定 ICU 比较级别。默认值为“tertiary”。有关各级别的更多信息,请参阅 ICU 比较级别。 |
normalization | 指定是否根据需要对文本执行 Unicode 规范化。有关 Unicode 规范化的更多信息,请参阅Unicode 规范化形式。 |
numericOrdering | 指定是否根据数值而不是排序规则对数字进行排序。 |
您可以使用 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 操作的使用示例。对于每个示例,假设您从以下文档集合开始:
{ "_id" : 1, "first_name" : "Klara" } { "_id" : 2, "first_name" : "Gunter" } { "_id" : 3, "first_name" : "Günter" } { "_id" : 4, "first_name" : "Jürgen" } { "_id" : 5, "first_name" : "Hannah" }
以下示例使用 "de@collation=phonebook" 区域设置和变体排序规则。排序规则的 "de" 部分指定德语区域设置,"collation=phonebook" 部分指定变体。"de" 区域设置排序规则包含对专有名词进行优先级排序的规则,通过首字母大写来识别。在 "collation=phonebook" 变体中,带变音符号的字符排序在不带变音符号的相同字符之前,以升序排序。
find() 和 sort() 示例
以下示例展示如何在从集合中检索排序结果时应用排序规则。要执行此操作,请对示例集合调用 find(),并链接 collation() 和 sort() 方法以指定接收结果的顺序。
FindPublisher<Document> findPublisher = phonebookCollection.find() .collation(Collation.builder() .locale("de@collation=phonebook").build()) .sort(Sorts.ascending("first_name")); Flux.from(findPublisher) .doOnNext(doc -> System.out.println(doc.toJson())) .blockLast();
当您对示例集合执行此操作时,输出如下所示:
{"_id": 3, "first_name": "Günter"} {"_id": 2, "first_name": "Gunter"} {"_id": 5, "first_name": "Hannah"} {"_id": 4, "first_name": "Jürgen"} {"_id": 1, "first_name": "Klara"}
有关本节中提到的方法和类的详情,请参阅以下 API 文档:
findOneAndUpdate() 示例
以下示例通过实例化 FindOneAndUpdateOptions 对象并将其作为参数传递,在 findOneAndUpdate() 操作中指定排序规则。该示例执行以下操作:
按升序检索示例集合中“Gunter”之前的第一个文档。
设置操作选项,包括
"de@collation=phonebook"排序规则。添加值为“true”的新字段“verified”。
检索并打印更新后的文档。
Document updatedDoc = Mono.from( phonebookCollection.findOneAndUpdate( Filters.lt("first_name", "Gunter"), Updates.set("verified", true), new FindOneAndUpdateOptions() .collation(Collation.builder() .locale("de@collation=phonebook") .build()) .sort(Sorts.ascending("first_name")) .returnDocument(ReturnDocument.AFTER))) .block(); if (updatedDoc != null) { System.out.println("Updated document: " + updatedDoc.toJson()); }
由于使用 de@collation=phonebook 排序规则按升序排列时,“Günter”在词法上位于“Gunter”之前,因此前面的操作返回以下文档:
Updated document: {"_id": 3, "first_name": "Günter", "verified": true}
有关本节中提到的方法和类的详情,请参阅以下 API 文档:
findOneAndDelete() 示例
下面的示例通过实例化 FindOneAndDeleteOptions 对象并将其作为参数传递,在 findOneAndDelete() 操作中指定数值排序规则。该集合包含以下文档:
{ "_id" : 1, "a" : "16 apples" } { "_id" : 2, "a" : "84 oranges" } { "_id" : 3, "a" : "179 bananas" }
此排序规则将 locale 选项设置为 "en",将 numericOrdering 选项设置为“true”,以便根据字符串的数值对字符串进行排序。
Document deletedDoc = Mono.from( numericalCollection.findOneAndDelete( Filters.gt("a", "100"), new FindOneAndDeleteOptions() .collation(Collation.builder() .locale("en") .numericOrdering(true) .build()) .sort(Sorts.ascending("a")))) .block(); if (deletedDoc != null) { System.out.println("Deleted document: " + deletedDoc.toJson()); }
运行上述操作后,输出结果如下:
Deleted document: {"_id": 3, "a": "179 bananas"}
The numeric value of the string "179" is greater than 100, so the preceding 文档 is the only match.在没有数字顺序的情况下,二进制排序规则将“100”排在“16”、“84”和“179”之前,因此过滤器匹配所有文档。
有关本节中提到的方法和类的详情,请参阅以下 API 文档:
聚合示例
以下示例展示了如何在聚合操作中指定排序规则。要执行聚合,请在 MongoCollection 对象上调用 aggregate() 方法。
要为聚合操作指定排序规则,请对聚合操作返回的 AggregatePublisher 调用 collation() 方法。在您的管道中指定排序聚合阶段以应用排序规则。
以下示例在示例集合上构建聚合管道,并通过指定以下内容来应用排序规则:
一个群组聚合阶段,使用
Aggregates.group()通过first_name字段识别每个文档,并将该值用作结果的_id。群组阶段中的累加器,用于对
first_name字段中匹配值的实例数求和。对输出文档的
_id字段进行升序排序。指定德语区域设置以及忽略重音和变音符号的排序规则强度的排序规则对象。
Bson groupStage = Aggregates.group( "$first_name", Accumulators.sum("nameCount", 1)); Bson sortStage = Aggregates.sort(Sorts.ascending("_id")); AggregatePublisher<Document> aggregatePublisher = phonebookCollection .aggregate(Arrays.asList(groupStage, sortStage)) .collation(Collation.builder() .locale("de") .collationStrength(CollationStrength.PRIMARY) .build()); Flux.from(aggregatePublisher) .doOnNext(doc -> System.out.println(doc.toJson())) .blockLast();
上述代码输出以下文档:
{"_id": "Gunter", "nameCount": 2} {"_id": "Hannah", "nameCount": 1} {"_id": "Jürgen", "nameCount": 1} {"_id": "Klara", "nameCount": 1}
有关本节中提到的方法和类的详情,请参阅以下 API 文档: