8

有一个集合:

{"name": "a"},
{"name": "B"},    
{"name": "b"},    
{"name": "c"},    
{"name": "á"},    
{"name": "A"}

前任。如何在不区分大小写的西班牙文中对其进行排序?

我试过这个:

var abc = [{"name": "a"}, {"name": "B"}, {"name": "b"}, {"name": "c"}, {"name": "á"}, {"name": "A"}];
for (i in abc) db.abc.save(abc[i]);

db.abc.find({},{"_id":0}).sort({"name":1});

输出是:

[
    { "name" : "A" },
    { "name" : "B" },
    { "name" : "a" },
    { "name" : "b" },
    { "name" : "c" },
    { "name" : "á" },
]

期望的结果:

[
    { "name" : "a" },
    { "name" : "á" },
    { "name" : "A" },
    { "name" : "b" },
    { "name" : "B" },
    { "name" : "c" }
]
4

5 回答 5

11

尽管此处的其他答案对于 MongoDB 3.2.x 及更早版本是正确的,但从 3.4.0 开始,您可以“为集合或视图、索引或支持排序规则的特定操作指定排序规则”。

该功能的完整文档在这里

于 2016-11-03T16:15:12.967 回答
10

我知道这是一个旧线程,但我认为无论如何回答都会很有用。

您绝对不想在您的应用程序中进行排序,因为这意味着您必须将集合中的所有文档放入内存中才能对它们进行排序并返回您想要的窗口。如果您的收藏量很大,那么这是非常低效的。数据库应该进行排序并将窗口返回给您。

但是,您说,MongoDB 不支持对区域设置敏感的排序。你如何解决问题?神奇的是“排序键”的概念。

基本上,假设您有从“a”到“z”的常规英语/拉丁字母。您要做的是创建一个从“a”到“01”以及从“b”到“02”等到“z”到“26”的排序键映射。也就是说,将每个字母按该语言的排序顺序映射到一个数字,然后将该数字编码为字符串。然后,将要排序的字符串映射到这种类型的排序键。例如,“abc”将变为“010203”。然后使用属性的排序键将属性添加到文档中,并将属性名称附加到区域设置的名称中:

{
    name: "abc",
    name_en: "010203"
}

现在,您可以通过对属性“name_en”进行索引来以语言“en”进行排序,并对选择器和范围使用简单的、基于英语的 MongoDB 排序而不是“name”属性。

现在,假设您有另一种疯狂的语言“xx”,其中字母的顺序是“acb”而不是“abc”。(是的,有些语言会以这种方式混淆拉丁字母的顺序!)排序键是这样的:

{
    name: "abc",
    name_en: "010203",
    name_xx: "010302"
}

现在,您所要做的就是在 name_en 和 name_xx 上创建索引并使用常规 MongoDB 排序以便在这些语言环境上正确排序。基本上,额外的属性是在不同语言环境中排序的代理。

那么你从哪里得到这些映射,你问?毕竟,你不是全球化专家,对吧?

好吧,如果您使用的是 Java、C 或 C++,那么有现成的类可以为您执行此映射。在 Java 中,使用标准的 Collat​​or 类,或者使用 icu4j Collat​​or 类。如果您使用 C/C++,请使用 ICU Collat​​or 函数/类的 C/C++ 版本。对于其他语言,除非你能找到一个已经这样做的库,否则你有点不走运。

这里有一些链接可以帮助您找到它们:

标准 Java 库 Collat​​or:http://docs.oracle.com/javase/7/docs/api/java/text/Collat​​or.html#getCollat​​ionKey(java.lang.String)

C++ 整理器类:http ://icu-project.org/apiref/icu4c/classicu_1_1Collat​​or.html#ae0bc68d37c4a88d1cb731adaa5a85e95

您还可以创建不同的排序键,允许您对每个区域设置不区分大小写(是的,大小写映射是区域设置敏感的!)和不区分重音,不区分 Unicode 变体,或以上任意组合。唯一的问题是,现在您有许多与每个可排序属性平行的属性,当您更新基本“名称”属性时,您必须使它们保持同步。这对于你知道什么是一种痛苦,但它仍然比在你的应用程序或业务逻辑层中进行排序要好。

还要小心带有范围的游标。例如,在英语中,我们只是忽略了字符上的重音。因此,“Ö”的排序方式与“O”相同,它将出现在“M”到“Z”的范围内。但是,在瑞典语中,重音字符排在“Z”之后。因此,如果您使用“M”-“Z”范围,您将包含一堆以“Ö”开头的记录,这些记录应该在英语中出现,而不是在瑞典语中。

如果您在文档的文本属性上进行拆分,这也会对分片产生影响。请注意哪些范围进入哪个分片。最好对不区分区域设置的事物(例如哈希)进行分片。

于 2014-06-04T05:55:45.850 回答
3

目前,MongoDB 没有实现排序规则。

实施Unicode 排序标准是解决这个问题的最佳方法。

但这会使排序变慢,索引变大。所以现在,最好在你的应用程序中进行排序。

于 2012-09-19T15:52:49.267 回答
2

一个简单的解决方法是创建一个将文本转换为纯 ascii 字符的新字段。

{ "name": "Ánfora", "name_sort": "anfora" }
{ "name": "Óscar", "name_sort": "oscar" }
{ "name": "Barça", "name_sort": "barc~a" }
{ "name": "Niño", "name_sort": "nin~o" }
{ "name": "¡Hola!", "name_sort": "hola!" }
{ "name": "¿qué?", "name_sort": "que?" }

然后只需按“name_sort”排序

于 2013-08-01T20:12:40.493 回答
1

不幸的是,您还不能进行不区分大小写的排序,现在按“索引”顺序排序返回。有一张开票:

https://jira.mongodb.org/browse/SERVER-90

您可能会考虑跳过 mongo 中的排序,并在您的应用程序中执行此操作。

于 2012-09-19T15:47:47.327 回答