按整理排序

时间:2012-09-19 15:42:03

标签: mongodb sorting collation

有一个集合:

{"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" }
]

5 个答案:

答案 0 :(得分:11)

虽然这里的其他答案对于MongoDB版本3.2.x和之前的版本是正确的,但从3.4.0开始,您可以“为集合或视图,索引或支持整理的特定操作指定排序规则”。

Full documentation for the feature is here

答案 1 :(得分: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/Collator.html#getCollationKey(java.lang.String)

C ++ Collat​​or类:http://icu-project.org/apiref/icu4c/classicu_1_1Collator.html#ae0bc68d37c4a88d1cb731adaa5a85e95

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

还要注意带范围的游标。例如,在英语中,我们只是忽略字符的重音。因此,“Ö”以与“O”相同的方式排序,它将出现在“M”到“Z”的范围内。但是,在瑞典语中,重音字符排在“Z”之后。所以,如果你做一个范围“M” - “Z”,你将包括一堆以“Ö”开头的记录,这些记录应该是英文版,但不是瑞典文。

如果您拆分文档的文本属性,这也会对分片产生影响。小心哪个范围进入哪个分片。最好对那些不像语言环境敏感的东西进行分片,比如哈希。

答案 2 :(得分:3)

目前,MongoDB没有实施整理。

实施Unicode collation standard是解决问题的最佳方式。

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

答案 3 :(得分: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'排序

答案 4 :(得分:1)

不幸的是,你不能做一个不区分大小写的排序,现在按“索引”顺序排序返回。有一张门票:

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

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