How to use $cond operation in Spring-MongoDb aggre

2019-01-09 15:49发布

I have an aggregation pipeline which includes a project like this:

$project: {
  start: {
    $cond: {
      if: {
        $eq: ["$start", "EARLY"]
      },
      then: "$deltastart.start",
      else: "$deltastart.end"
    }
  },...
},...

which works fine in mongo shell. How to express this using the Aggregation framework in Spring-Mongodb? I have seen ProjectionOperationBuilder, ExpressionProjectionOperationBuilder types but not an example how to use them... any suggestions?

2条回答
戒情不戒烟
2楼-- · 2019-01-09 15:53

I add the same issue and searched on Google and this was the first result I found so I'd like to add for future readers that this feature is now available since version 1.10 RC1 with the ConditionalOperators.Cond class.

You can read the JavaDoc here.

查看更多
萌系小妹纸
3楼-- · 2019-01-09 16:12

If using the current Spring Data release which has support for the $cond operator via the $project pipeline, then this can be converted to (untested):

import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;
import static org.springframework.data.mongodb.core.aggregation.ConditionalOperators.Cond.*;
import org.springframework.data.mongodb.core.query.Criteria;

Cond condOperation = ConditionalOperators.when(Criteria.where("start").is("EARLY"))
                                    .thenValueOf("deltastart.start")
                                    .otherwise("deltastart.end");

Aggregation agg = newAggregation(project().and(condOperation).as("start"));
AggregationResults<MyClass> results = mongoTemplate.aggregate(agg, MyClass.class); 
List<MyClass> myList = results.getMappedResults();

For Spring-Data MongoDB version which do not have support for the $cond operator in the aggregation operation, there is a workaround which is to implement the AggregationOperation interface to take in a DBObject:

public class CustomProjectAggregationOperation implements AggregationOperation {
    private DBObject operation;

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

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

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

DBObject operation = (DBObject) new BasicDBObject(
    "$project", new BasicDBObject(
         "start", new BasicDBObject(
                "$cond", new Object[]{
                        new BasicDBObject(
                            "$eq", new Object[]{ "$start", "EARLY"}
                        ),
                        "$deltastart.start",
                        "$deltastart.end"
                 }
           )
     )
);

which you can then use in TypeAggregation:

TypedAggregation<CustomClass> aggregation = newAggregation(CustomClass.class,
    new CustomProjectAggregationOperation(operation)
);
AggregationResults<CustomClass> result = mongoTemplate.aggregate(aggregation, CustomClass.class); 
查看更多
登录 后发表回答