【问题标题】:Sort by collation按排序规则排序
【发布时间】:2012-09-11 23:10:03
【问题描述】:

收藏:

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

【问题讨论】:

    标签: mongodb sorting collation


    【解决方案1】:

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

    Full documentation for the feature is here.

    【讨论】:

    • 这将是现在的正确答案。因为 MongoDB 允许在创建集合时定义排序规则或创建具有所需排序规则的视图。请看看这个:docs.mongodb.com/manual/reference/method/db.createCollection/…
    • 我肯定会放弃 MongoDB,因为我认为使用我的母语葡萄牙语(来自巴西)没有简单的方法。但这似乎是一个很好的解决方案。
    【解决方案2】:

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

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

    但是,您说,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++ 整理器类:http://icu-project.org/apiref/icu4c/classicu_1_1Collator.html#ae0bc68d37c4a88d1cb731adaa5a85e95

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

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

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

    【讨论】:

    • 那么在这个例子中,你会在 5 或 6 个字符后停止并在较短的单词上填充 0 吗?
    • 不,较短的字符串总是“赢得”比较。 “abc”排在“abcdef”之前,即使它们有相同的前缀,所以“010203”应该排在“010203040506”之前。排序键属性应作为字符串进行比较,而不是数字。
    • 哦,Java 和 C++ 归类器返回的排序键看起来与我在此处给出的示例非常不同。我只使用了“01”、“02”等,因为它们很容易理解。例如,在 Java 中,getCollat​​ionKey() 方法返回一个整数数组,其中包含位压缩的排序规则元素。我建议将此数组转换为十六进制数字字符串,以便 MongoDB 可以将它们作为字符串与默认的英语比较规则进行比较,这些规则在十六进制上可以正常工作。
    【解决方案3】:

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

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

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

    【讨论】:

      【解决方案4】:

      一个简单的解决方法是创建一个将文本转换为纯 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'排序

      【讨论】:

      • 这样的事情会起作用。正如其他人所建议的那样,在应用程序中进行排序不是一种可行的替代方案,因为您有数百万行,因此必须创建一个可排序字段才能真正解决问题。
      【解决方案5】:

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

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

      您可以考虑跳过 mongo 中的排序,并在您的应用中进行。

      【讨论】:

        猜你喜欢
        • 2016-10-03
        • 1970-01-01
        • 1970-01-01
        • 2012-01-19
        • 2012-12-13
        • 2018-12-19
        • 2023-04-11
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多