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

时间:2014-05-06 12:07:23

标签: java mongodb mapreduce aggregation-framework spring-data-mongodb

我们收集了包含以下字段的“消息”

_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”排序(排序)
  3. 所以,预期的结果是

    _id |   messageId |  chainId | createOn
    
    3   |       3     |    A     | 225
    5   |       5     |    C     | 228
    6   |       6     |    B     | 300
    

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

4 个答案:

答案 0 :(得分:8)

您想要的是使用聚合框架可以实现的功能。 (对其他人有用)的基本形式是:

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连接器文档,以了解聚合方法和可能的帮助程序的更多用法。

答案 1 :(得分: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:输入字符串:" C")是异常

答案 2 :(得分:0)

替换此行:

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

这一个:

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

答案 3 :(得分: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);