2

I am new to Mongodb and spring-data, and I referred to this stackoverflow link grouping quarterly wise and this link using $cond operator in spring data and framed this code snippet below, for retrieving quarterwise sales report in mongodb:

String pipeline =   "{$project:{_id:1,'unitsSold':1,'dateSold':1,'results': 1 ,
  'productName': 1, 'year':{$year:['$dateSold']},    "+
       "'quarter':{$cond:[{$lte:[{$month:'$dateSold'},3]},"+
                         "'first'," +
                         "{$cond:[{$lte:[{$month:'$dateSold'},6]},"+
                                 "'second',"+
                                 "{$cond:[{$lte[{$month:'$dateSold'},9]},"+"'third',"+
                                         "'fourth']}]}]}}},"+
"{$group:{'_id':{ 'year':'$year', 'quarter':'$quarter'},  
'unitsSold': { $sum: '$unitsSold' },'results':{$push:'$$ROOT'}}}";



 DBObject operation = (DBObject)JSON.parse (pipeline);

TypedAggregation<SampleReport> aggregation =newAggregation(SampleReport.class,
new DBObjectAggregationOperation(operation)
);

AggregationResults<SampleReport> result =mongoTemplate.aggregate(aggregation, SampleReport.class);
List<SampleReport> list = result.getMappedResults();
for(SampleReport r : list)
            {
                System.out.println (r.getProductName() + " : " + r.getUnitsSold() + " : " + r.getQuarter() +":: "+r.getYear());
            }

The problem is NOT summing up the units sold. Please lemme know where I am going wrong with spring data. But this query gets the required results using robomongo.

Regards

Kris

Community
  • 1
  • 1
chiku
  • 485
  • 2
  • 8
  • 23

1 Answers1

1

If you say it works in another client then likely something got lost in the transation. There are things you can certainly clean up here to make it more simplified.

Can I generally suggest a much more efficient "math" approach to determining the current quarter rather than the current nested conditional statements, as if nothing else it does make things a lot cleaner. In addition of "efficiency" you should not be using $project just preceeding a $group where it makes logical sense to simply combine everything into that one stage:

[
    { "$group": {
        "_id": {
            "year": { "$year": "$dateSold" },
            "quarter": {
                "$add": [
                    { "$subtract": [
                        { "$divide": [{ "$subtract": [{ "$month": "$dateSold" },1]},3]},
                        { "$mod": [
                            { "$divide": [{ "$subtract": [{ "$month": "$dateSold" },1]},3]},
                            1
                        ]}
                    ]},
                    1
                ]
            }
        },
        "unitsSold": { "$sum": "$unitsSold" }
    }}
]

By all means add in your "$push": "$$ROOT" if you really must, but cutting down the involved logic and putting everything into a single pipeline stage where it is logical to do so is largely the point here.

The next phase is that I strongly suggest you code this up natively. Whilst it may be tempting to think that you have a JSON notation that you can use, you will find that in time this is neither flexible nor does it provide very good readability to place in long strings and rely on parsing them. Moreover you are generally going to want to interpolate local variables at some stage

Aggregation aggregation = newAggregation(
    new CustomGroupOperation(
        new BasicDBObject("$group",
            new BasicDBObject("_id",
                new BasicDBObject("year",new BasicDBObject("$year","$dateSold"))
                    .append("quarter",new BasicDBObject(
                        "$add",Arrays.asList(
                            new BasicDBObject("$subtract",Arrays.asList(
                                new BasicDBObject("$divide",Arrays.asList(
                                    new BasicDBObject("$subtract",Arrays.asList(
                                        new BasicDBObject("$month","$dateSold"),
                                        1
                                    )),
                                    3
                                )),
                                new BasicDBObject("$mod",Arrays.asList(
                                    new BasicDBObject("$divide", Arrays.asList(
                                        new BasicDBObject("$subtract",Arrays.asList(
                                            new BasicDBObject("$month", "$dateSold"),
                                            1
                                        )),
                                        3
                                    )),
                                    1
                                ))
                            )),
                            1
                        )
                    ))
            )
            .append("unitsSold", new BasicDBObject("$sum", "$unitsSold"))
        )
    )
);

You also seem to have abstracted some other code, but I personally prefer to implement the CustomGroupOperation in a way that is not going to conflict with using other spring-mongo aggregation helpers within the newAggregation contruction:

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);
    }
}

But as originally stated, if you are getting a 0 result then it's field naming or "type" where the field is differently named or in fact a string. But the identical statement should then fail in any other client in a similar way, and the only remedy is to fix the naming or "type" as appropriate.

This is certainly a "cleaner" approach to what you are doing. The "math" is sound for an indexed quarter and can even be adapted to other "financial quarters" as is appropriate via a simple mapping. As does the consolidation of pipeline stages here provide significant performance gains in line with the overall size of the data as a $project means an un-necessary pass through the data just to pre-adjust the fields, which you don't want.

Fix up the definition and execution and then check your fields and data to see that everything is correctly named and typed.

Blakes Seven
  • 49,422
  • 14
  • 129
  • 135
  • thanks for your answer, I am getting an exception: A pipeline stage specification object must contain exactly one field. Please lemme know where I am wrong. – chiku Sep 29 '15 at 17:00
  • @chiku You need to use the code as provided as you are clearly doing something different to get that specific error. Use it as is at first as this answers the question you asked. If you want to change things and run into problems then [ask another question](http://stackoverflow.com/questions/ask) instead of commenting here since we cannot see what you are doing.. – Blakes Seven Sep 29 '15 at 23:11
  • @user1503117 Who are you? This is not your question or are you the same user using two accounts? If you are the same person then please respond using the account you posted with as it is confusing if you do not. Whatever the case, simply saying "an exception" with no other details does not tell me anything. There should be nothing wrong with the listing given above. – Blakes Seven Sep 30 '15 at 06:00
  • Blakes, the user is my developer@ my office, he has tried your solution, it did not work, when run the aggregate query in robomongo it works excellent when running with spring data its giving this exception as mentioned.Please provide a solution. kris – chiku Sep 30 '15 at 14:32
  • 1
    @chiku Yet again I am going to ask "what exception?". You need to edit the question to show the detail of what is being run and what exception you are having. I can pretty much guarantee it is no fault of the listing here and in fact another problem with the way it is being implemented. – Blakes Seven Sep 30 '15 at 22:00
  • @chiku Of course it was. Don't forget to [accept the answer](http://stackoverflow.com/help/accepted-answer) and perhaps you can also clean up the negative noise here that was claiming otherwise. – Blakes Seven Oct 02 '15 at 08:40
  • blakes, can u please help me in solving the pivot problem using spring data mongodb, which I have posted in the link : http://stackoverflow.com/questions/32902502/getting-summed-row-to-column-data-using-spring-data-mongodb – chiku Oct 02 '15 at 11:42