mongotemplate aggregation with condition

后端 未结 2 704
孤街浪徒
孤街浪徒 2021-01-19 07:02

I have a collection where the documents look like this:

{
  _id: \"545b9fa0dd5318a4285f7ce7\",
  owner: \"admin\",  
  messages: [
    {
      id: \"100\",
          


        
相关标签:
2条回答
  • 2021-01-19 07:20

    You can use following aggregation :

    db.collection.aggregate(
        { $match : { "_id" : ObjectId("545b9fa0dd5318a4285f7ce7") } },
        { $unwind : "$messages" },
        { $group : { "_id" : "$messages.status", "count" : { $sum : 1} } }
    )
    

    it will give you the count of status for which message are available, all other status count should be consider 0.

    0 讨论(0)
  • 2021-01-19 07:25

    To do this sort of thing you need the $cond operator in the aggregation framework. Spring Data MongoDB does not do that yet, and there are many things lacking from common $group operations that are even only implemented under $project.

    The issue for tracking implementation of any $cond support is here:

    https://jira.spring.io/browse/DATAMONGO-861

    For the rest of the world it looks like this:

    db.collection.aggregate([
        { "$match": { "_id": ObjectId("545b9fa0dd5318a4285f7ce7") } },
        { "$unwind": "$messages" },
        { "$group": {
            "_id": "$_id",
            "sent": {
                "$sum": {
                    "$cond": [
                        { "$eq": [ "$mesages.status", "sent" ] },
                        1,
                        0
                    ]
                }
            },
            "pending": {
                "$sum": {
                    "$cond": [
                        { "$eq": [ "$messages.status", "pending" ] },
                        1,
                        0
                    ]
                }
            },
            "done": {
                "$sum": {
                    "$cond": [
                        { "$eq": [ "$messages.status", "done" ] },
                        1,
                        0
                    ]
                }
            }
        }}
    ])
    

    To get that sort of thing to work under a mongotemplate aggregation you need a class extending aggregation operations that can be built from a DBObject:

    public class CustomGroupOperation implements AggregationOperation {
        private DBObject operation;
    
        public CustomGroupOperation (DBObject operation) {
            this.operation = operation;
        }
    
        @Override
        public DBObject toDBObject(AggregationOperationContext context) {
            return context.getMappedObject(operation);
        }
    }
    

    Then you can define the "$group" as a DBObject and implement in the aggregation pipeline:

       DBObject myGroup = (DBObject)new BasicDBObject(
            "$group", new BasicDBObject(
                "_id","$_id"
            ).append(
                "sent", new BasicDBObject(
                    "$sum", new BasicDBObject(
                        "$cond", new Object[]{
                            new BasicDBObject(
                                "$eq", new Object[]{ "$messages.status", "sent"}
                            ),
                            1,
                            0
                        }
                    )
                )
            ).append(
                "pending", new BasicDBObject(
                    "$sum", new BasicDBObject(
                        "$cond", new Object[]{
                            new BasicDBObject(
                                "$eq", new Object[]{ "$messages.status", "pending"}
                            ),
                            1,
                            0
                        }
                    )
                 )
            ).append(
                "done", new BasicDBObject(
                    "$sum", new BasicDBObject(
                        "$cond", new Object[]{
                             new BasicDBObject(
                                "$eq", new Object[]{ "$messages.status", "done"}
                             ),
                             1,
                             0
                        }
                     )
                )
            )
         );
    
    
       ObjectId myId = new ObjectId("545b9fa0dd5318a4285f7ce7");
    
       Aggregation aggregation = newAggregation(
               match(Criteria.where("_id").is(myId)),
               unwind("messges"),
               new CustomGroupOperation(myGroup)
       );
    

    That allows you to come up with a pipeline that is basically the same as the shell representation above.

    So it would seem that for now, where certain operations and sequences are not supported the best case is to implement a class on the AgggregationOperation interface that can be fed a DBObject or otherwise internally construct one from your own custom methods.

    0 讨论(0)
提交回复
热议问题