MongoDB中的不区分大小写排序

86

我如何按指定字段对MongoDB集合进行不区分大小写的排序?默认情况下,我会得到先A-Z后a-z的排序。

11个回答

84

更新: 截至目前,MongoDB已经具备不区分大小写的索引功能:

Users.find({})
  .collation({locale: "en" })
  .sort({name: 1})
  .exec()
  .then(...)

Shell:

db.getCollection('users')
  .find({})
  .collation({'locale':'en'})
  .sort({'firstName':1})

更新:此答案已过时,3.4版本将具有不区分大小写的索引。欲了解更多信息,请参阅JIRA https://jira.mongodb.org/browse/SERVER-90


遗憾的是,MongoDB目前尚未支持不区分大小写的索引:https://jira.mongodb.org/browse/SERVER-90,相关任务已经被推迟。

这意味着当前唯一能进行不区分大小写排序的方法是创建一个特定的“小写化”字段,将需要排序的字段值复制为其小写形式,并在该字段上进行排序。


所以,除非创建一个仅包含小写或大写值的新字段,否则没有对结果进行不区分大小写排序的选项。对吧? - Varun Kumar
1
@VarunKumar 是的,这是 MongoDB 的一个缺点之一,我个人不会使用下面的答案,没有索引的情况下,MongoDB 将被限制在 32MB 的排序上,导致只允许非常小的结果集。 - Sammaye
1
@VarunKumar 更准确地说,您将被限制在排序2到32,000条记录之间,具体取决于文档的大小,不仅如此,排序将完全在内存中进行,这将是一个致命的问题。 - Sammaye
1
@F.H. 你正在使用字符集排序吗? - Sammaye
1
@F.H. 为了明确,sort(排序)是区分大小写的原因与iOS没有使用排序规则有关,这是因为它使用基于字典顺序的排序而没有使用排序规则。 - Sammaye
显示剩余10条评论

61

MongoDB中的排序不是这样工作的,但您可以使用聚合在运行时执行此操作:

接下来是以下数据:

{ "field" : "BBB" }
{ "field" : "aaa" }
{ "field" : "AAA" }

所以根据以下语句:

db.collection.aggregate([
    { 
        "$project": {
            "field": 1,
            "insensitive": { "$toLower": "$field" }
        }
    },
    { "$sort": { "insensitive": 1 } }
])

会产生如下结果:

{
    "field" : "aaa",
    "insensitive" : "aaa"
},
{
    "field" : "AAA",
    "insensitive" : "aaa"
},
{
    "field" : "BBB",
    "insensitive" : "bbb"
}

当转换后的任何值导致相同的键时,实际插入顺序将保持不变。


是的,这看起来不错,但我想从Java代码中实现相同的功能。如果您能与处理MongoDB和查询对象一起分享如何从Java类中实现这一点,那将更有帮助。 - Varun Kumar
@VarunKumar,你需要构建DBObject条目并将其传递给聚合方法。文档资源中有这个例子。因此,翻译起来应该不难。考虑到这是一个实际的答案,展示如何完成这项任务也不应该太难。 - Neil Lunn
这会很慢吗(即聚合每次都会被评估吗?) - Archimedes Trajano
有人使用spring-data-mongo或Java解决了这个问题吗? - freak007
这很好,如果你不能使用排序。 - FernandoG

32

这在 MongoDB JIRA 上已经是一个相当长时间的问题,但现在已经得到解决。请查看此版本说明以获取详细文档。您应该使用collation

User.find()
    .collation({locale: "en" }) //or whatever collation you want
    .sort({name:1})
    .exec(function(err, users) {
        // use your case insensitive sorted results
    });

15

添加代码 .collation({'locale':'en'}) 帮助解决了我的问题。


但是当我在Mongoose中使用聚合(collation)时,会出现错误MongooseError: 回调必须是一个函数,得到[object Object]。 - Monu Chaudhary

3

目前(mongodb 4)可以执行以下操作:

mongo shell:

db.getCollection('users')
  .find({})
  .collation({'locale':'en'})
  .sort({'firstName':1});

mongoose:

Users.find({})
  .collation({locale: "en" })
  .sort({name: 1})
  .exec()
  .then(...)

以下是 MongoDB 支持的语言和地区信息:

请参考 mongodb官方文档


3
在Mongoose中:
-
Customer.find()
  .collation({locale: "en" })
  .sort({comapany: 1})

1

这是Java版本的。为了增加多样性,我混合使用了不带参数和第一个键值变量的BasicDBObject

        DBCollection coll = db.getCollection("foo");

        List<DBObject> pipe = new ArrayList<DBObject>();

        DBObject prjflds = new BasicDBObject();
        prjflds.put("field", 1);
        prjflds.put("insensitive", new BasicDBObject("$toLower", "$field"));

        DBObject project = new BasicDBObject();
        project.put("$project", prjflds);
        pipe.add(project);

        DBObject sort = new BasicDBObject();
        sort.put("$sort", new BasicDBObject("insensitive", 1));
        pipe.add(sort);

        AggregationOutput agg = coll.aggregate(pipe);

        for (DBObject result : agg.results()) {
            System.out.println(result);
        }

1
如果您想对文档中的所有数据进行排序并返回,可以添加document: "$$ROOT"
db.collection.aggregate([
  { 
    $project: {
      field: 1,
      insensitive: { $toLower: "$field" },
      document: "$$ROOT"
    }
  },
  { $sort: { insensitive: 1 } }
]).toArray()

使用 $addFields,您不再需要添加 $$ROOT - am.rez

0
尝试了以上所有方法和答案,正在整合结果。
答案1:
db.collection.aggregate([
    { "$project": {
       "field": 1,
       "insensitive": { "$toLower": "$field" }
    }},
    { "$sort": { "insensitive": 1 } } ])

聚合查询将字段转换为小写,因此对于大数据性能较低。

答案2:

db.collection.find({}).collation({locale: "en"}).sort({"name":1})

默认情况下,MongoDB 遵循 utf-8 编码规则(Z 的优先级高于 a),因此需要使用特定语言的规则进行覆盖。 相比上述查询,它更快速。 请查看官方文档以自定义规则。

https://docs.mongodb.com/manual/reference/collation/


0

MongoDB与优雅提供

public <T extends Entity> ReactivePanacheQuery<T> withCollation(Collation collation); 

ReactivePanacheQuery类中的方法。 使用此方法可以实现对数据进行不区分大小写的排序,具体操作如下。

repo.find(query, sort).withCollation(Collation.builder().locale("en").build())

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接