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支持大多数增删改查操作和聚合的排序规则。有关支持的操作的完整列表,请参阅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手册中的使用索引对查询结果进行排序。
以下示例展示了具有以下特征的查询操作:
查询指定冰岛语 (
"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 | 指定是否首先从字符串末尾考虑变音符号。 |
区分大小写 | 指定是否将大小写(大写或小写)视为不同的值。 |
替代方案 | 指定是否考虑空格和标点符号。 |
caseFirst | 指定是先考虑大写字母,还是先考虑小写字母。 |
最大变量 | 指定是忽略空格还是同时空格和标点符号。此设置仅在备用设置为“shift”时才有效。 |
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"}
字符串 "179" 的数值大于 100,因此前面的文档是唯一匹配项。如果没有数字排序,二进制排序规则会将“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 文档: