使用Mongodb,按一个字段分组并按另一个字段排序选择所有字段。

9
我们有一个名为'message'的集合,其中包含以下字段。
_id |   messageId |  chainId | createOn

1   |       1     |    A     | 155
2   |       2     |    A     | 185
3   |       3     |    A     | 225
4   |       4     |    B     | 226
5   |       5     |    C     | 228
6   |       6     |    B     | 300

我们希望按照以下条件选择文档的所有字段:
  1. 按'chainId'字段去重
  2. 按'desc'顺序对'createdOn'进行排序
因此,期望的结果是:
_id |   messageId |  chainId | createOn

3   |       3     |    A     | 225
5   |       5     |    C     | 228
6   |       6     |    B     | 300

我们在Java应用程序中使用spring-data。我尝试了不同的方法,但目前为止都没有帮助到我。
是否有可能通过单个查询实现上述目标?

4个回答

13

你想要的是可以使用聚合框架来实现的。其中一种有用于他人的基本形式是:

db.collection.aggregate([

    // Group by the grouping key, but keep the valid values
    { "$group": {
        "_id": "$chainId",
        "docId": { "$first": "$_id" },
        "messageId": { "$first": "$messageId" },
        "createOn": { "$first": "$createdOn" }
    }},

    // Then sort
    { "$sort": { "createOn": -1 } }

])

要在不改变其他字段的情况下,在“messageId”的不同值的“组”上获取边界值使用$first。或者,如果您想要最大值,则使用$last,但无论是按行最小还是最大值,可能都需要首先使用$sort,否则,如果整行不重要,则只需使用$min$max

有关更多用法信息,请参见MongoDB aggregate()文档,以及驱动程序JavaDocs和SpringData Mongo连接器文档以获取更多有关聚合方法的使用方式和可能的辅助工具。


4
以下是使用MongoDB Java驱动程序的解决方案:
    final MongoClient mongoClient = new MongoClient();
    final DB db = mongoClient.getDB("mstreettest");
    final DBCollection collection = db.getCollection("message");

    final BasicDBObject groupFields = new BasicDBObject("_id", "$chainId");
    groupFields.put("docId", new BasicDBObject("$first", "$_id"));
    groupFields.put("messageId", new BasicDBObject("$first", "$messageId"));
    groupFields.put("createOn", new BasicDBObject("$first", "$createdOn"));

    final DBObject group = new BasicDBObject("$group", groupFields);

    final DBObject sortFields = new BasicDBObject("createOn", -1);
    final DBObject sort = new BasicDBObject("$sort", sortFields);

    final DBObject projectFields = new BasicDBObject("_id", 0);
    projectFields.put("_id", "$docId");
    projectFields.put("messageId", "$messageId");
    projectFields.put("chainId", "$_id");
    projectFields.put("createOn", "$createOn");
    final DBObject project = new BasicDBObject("$project", projectFields);

    final AggregationOutput aggregate = collection.aggregate(group, sort, project);

最终结果将会是:

{ "_id" : 5 , "messageId" : 5 , "createOn" : { "$date" : "2014-04-23T04:45:45.173Z"} , "chainId" : "C"}
{ "_id" : 4 , "messageId" : 4 , "createOn" : { "$date" : "2014-04-23T04:12:25.173Z"} , "chainId" : "B"}
{ "_id" : 1 , "messageId" : 1 , "createOn" : { "$date" : "2014-04-22T08:29:05.173Z"} , "chainId" : "A"}

我尝试使用SpringData Mongo,但当我按chainId分组时它无法工作(抛出了java.lang.NumberFormatException: For input string: "C"的异常)。


0

这是使用springframework.data.mongodb的解决方案:

Aggregation aggregation = Aggregation.newAggregation(
                Aggregation.group("chainId"),
                Aggregation.sort(new Sort(Sort.Direction.ASC, "createdOn"))
              );
AggregationResults<XxxBean> results = mongoTemplate.aggregate(aggregation, "collection_name", XxxBean.class);

0

替换这行:

final DBObject group = new BasicDBObject("$group", groupFields);

使用这个:

final DBObject group = new BasicDBObject("_id", groupFields);

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