1

I want to use conditional query.

Here is my query

db.projects.aggregate([
{
    "$group": {
        "_id": "$iecode",
        "treatmentArms": { "$first": "$evaluationDTOList" }
    }
},
{ "$unwind": "$treatmentArms" },
{
    "$group": {
        "_id": null,
        "Package": { 
            "$sum": { 
               "$cond": [ 
                   { "$eq": [ "$treatmentArms.mechanismOrPkg", "Package" ] }, 
                   1, 0
                ] 
            }
        },
        "Constraint-relaxing mechanisms": { 
            "$sum": { 
               "$cond": [ 
                    { 
                        "$and": [
                            { "$eq": [ "$treatmentArms.mechanismOrPkg", "Mechanism" ] },
                            { "$eq": [ "$treatmentArms.mechanismTested1", "Constraint-relaxing mechanisms" ] }
                        ]
                    }, 
                    1, 
                    0 ]
            }
        },
        "Delivery mechanisms": { 
            "$sum": { 
               "$cond": [ 
                    { 
                        "$and": [
                            { "$eq": [ "$treatmentArms.mechanismOrPkg", "Mechanism" ] },
                            { "$eq": [ "$treatmentArms.mechanismTested1", "Delivery mechanisms" ] }
                        ]
                    }, 
                    1, 
                    0 ]
            }
        },
        "Other": { 
            "$sum": { 
               "$cond": [ 
                    { 
                        "$and": [
                            { "$eq": [ "$treatmentArms.mechanismOrPkg", "Mechanism" ] },
                            { "$eq": [ "$treatmentArms.mechanismTested1", "Other" ] }
                        ]
                    }, 
                    1, 
                    0 ]
            }
        }
    }
}
])

Here is my java code

DBObject groupByIECode = new BasicDBObject("$group",
                new BasicDBObject("_id", new BasicDBObject("iecode","$iecode")).append("treatmentArms",new BasicDBObject("$first","$evaluationDTOList")));
        System.out.println("groupByIECode: "+groupByIECode.toString());

        DBObject unwind = new BasicDBObject("$unwind","$treatmentArms");
        System.out.println("unwind: "+unwind.toString());


        DBObject finalCalculation = new BasicDBObject("$group",new BasicDBObject("_id",null))
                                    .append(
                                            "Package", new BasicDBObject(
                                                "$sum", new BasicDBObject(
                                                    "$cond", new Object[]{
                                                        new BasicDBObject(
                                                            "$eq", new Object[]{ "$treatmentArms.mechanismOrPkg", "Package"}
                                                        ),
                                                        1,
                                                        0
                                                    }
                                                )
                                            )
                                        );

        System.out.println("finalCalculation: "+finalCalculation);
        final AggregationOutput output = projects.aggregate(match,groupByIECode,unwind,finalCalculation);

It gives me MongoException$DuplicateKey

Later I found out that $cond operator is not supported in spring mongotemplate. So how do I implememt this conditional query with spring mongotemplate.

This link has some explanation but it do not shows full implementation

Neil Lunn
  • 148,042
  • 36
  • 346
  • 317
Half Blood Prince
  • 951
  • 2
  • 13
  • 25

1 Answers1

1

From the documentation, a canonical example for using the Spring Data MongoDB support for the MongoDB Aggregation Framework looks as follows:

import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;

Aggregation agg = newAggregation(
    pipelineOP1(),
    pipelineOP2(),
    pipelineOPn()
);

AggregationResults<OutputType> results = mongoTemplate.aggregate(agg,
    "INPUT_COLLECTION_NAME", OutputType.class);
List<OutputType> mappedResult = results.getMappedResults();

Note that if you provide an input class as the first parameter to the newAggregation method the MongoTemplate will derive the name of the input collection from this class. Otherwise if you don’t specify an input class you must provide the name of the input collection explicitly. If an input-class and an input-collection is provided the latter takes precedence.


For your query, create a workaround that implements the AggregationOperation interface to take in a DBObject that represents a single group operation in an aggregation pipeline with the $cond operator:

public class GroupAggregationOperation implements AggregationOperation {
    private DBObject operation;

    public GroupAggregationOperation (DBObject operation) {
        this.operation = operation;
    }

    @Override
    public DBObject toDBObject(AggregationOperationContext context) {
        return context.getMappedObject(operation);
    }
}

Then implement the $group operation as a DBObject in the aggregation pipeline that is the same as the one you have:

DBObject operation = (DBObject) new BasicDBObject("$group", new BasicDBObject("_id", null))
    .append(
        "Package", new BasicDBObject(
            "$sum", new BasicDBObject(
                "$cond", new Object[]{
                    new BasicDBObject(
                        "$eq", new Object[]{ "$treatmentArms.mechanismOrPkg", "Package"}
                    ),
                    1,
                    0
                }
            )
        )
    );

which you can then use as:

import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;

GroupAggregationOperation groupOp = new GroupAggregationOperation(operation);
Aggregation agg = newAggregation(
    group("iecode").first("treatmentArms").as("treatmentArms"),
    unwind("treatmentArms"),
    groupOp 
);
AggregationResults<Entity> results = mongoTemplate.aggregate(agg, Entity.class); 
List<Entity> entities = results.getMappedResults();
chridam
  • 100,957
  • 23
  • 236
  • 235
  • can I get `DBObject` from the `Aggregation` object. Actually I do not have been class(Entity). – Half Blood Prince Aug 03 '16 at 07:08
  • I mean where do I specify the collection name on which I want to fire my query. – Half Blood Prince Aug 03 '16 at 07:09
  • 1
    @HalfBloodPrince I've updated my answer with the canonical example from the [documentation](http://docs.spring.io/spring-data/mongodb/docs/current/reference/html/#mongo.aggregation.basic-concepts) which I would encourage you to go through. – chridam Aug 03 '16 at 07:30
  • How do I specify more than one condition? LIke `"$treatmentArms.mechanismOrPkg == Package"` and `"$treatmentArms.mechanism == Mechanism1"`. – Half Blood Prince Aug 03 '16 at 10:25
  • 1
    Append another DBObject with the conditions – chridam Aug 03 '16 at 10:26
  • Won't I have to use `$and`? – Half Blood Prince Aug 03 '16 at 10:31
  • 1
    No. It's only a matter of appending the other `$group` object keys on the existing `DBObject` i.e. `DBObject operation = (DBObject) new BasicDBObject("$group", new BasicDBObject("_id", null)).append("Package", new ...).append("Constraint-relaxing mechanisms", new ...).append("Delivery mechanisms", new ...).append(...` – chridam Aug 03 '16 at 11:27
  • Okay. What I did is I made a list of `BasicDBObject` with name `filters`, added all filters in that and used it like `new BasicDBObject("$and",filters)`. – Half Blood Prince Aug 03 '16 at 12:08