MongoDB $aggregate $push multiple fields in Java Spring Data
You can directly pass the BasicDbObject to any of the aggregation pipeline stage.
Aggregation agg = newAggregation( group("title"). push(new BasicDBObject ("version", "$version").append ("author", "$author").append ("dateAdded", "$dateAdded")).as("versions"));
Note: MongoDB versiĆ³n 2.6 - 3.0 (compatible Java driver)
I've extended org.springframework.data.mongodb.core.aggregation.AggregationOperation
class to implement custom toDBObject
method:
public class GenericAggregationOperation implements AggregationOperation { private String operator; private DBObject query; /** * Default constructor. * * @param operator MongoDB operator ($group, $sort, $project, etc..) * @param query MongoDB aggregation query step string */ public GenericAggregationOperation(String operator, String query) { this(operator, (DBObject) JSON.parse(query)); } /** * Default constructor. * * @param operator MongoDB operator ($group, $sort, $project, etc..) * @param query MongoDB aggregation query step DBObject */ public GenericAggregationOperation(String operator, DBObject query) { this.operator = operator; this.query = query; } @Override public DBObject toDBObject(AggregationOperationContext context) { return new BasicDBObject(operator, query); }}
In your case, it would be:
List<AggregationOperation> list = new ArrayList<AggregationOperation>();lista.add(new GenericAggregationOperation("$group", "{ \"_id\": \"$title\", \"versions\": { \"$push\": { \"version\":\"$version\", \"author\":\"$author\", \"dateAdded\":\"$dateAdded\"}} }"));TypedAggregation<EpisodeIndexDto> agg = Aggregation.newAggregation( YourClassTitleVersion.class, list);mongoOperations.aggregate(agg, YourClassTitleVersion.class, YourClassTitleVersion.class).getMappedResults();
Hope this helps you.
In the new version spring-data-mongodb:2.x.x the AggregationOperation need to return Document instead of DBObject, so the updated class will be:
import com.mongodb.BasicDBObject;import com.mongodb.DBObject;import org.bson.Document;import org.springframework.data.mongodb.core.aggregation.AggregationOperation;import org.springframework.data.mongodb.core.aggregation.AggregationOperationContext;public class GenericAggregationOperation implements AggregationOperation { private String operator; private DBObject query; public GenericAggregationOperation(String operator, DBObject query) { this.operator = operator; this.query = query; } public GenericAggregationOperation(String operator, String query) { this(operator, BasicDBObject.parse(query)); } @Override public Document toDocument(AggregationOperationContext context) { return new Document(operator, query); }}
Plus to make it easier to be used I'll add utility interface (java 8+, for java 7 or lower you can convert this to class utils instead):
import com.mongodb.DBObject;import org.springframework.data.mongodb.core.aggregation.AggregationOperation;public interface GenericAggregationUtils { static AggregationOperation aggregate(String operation, String query) { return new GenericAggregationOperation(operation, query); } static AggregationOperation aggregate(String operation, DBObject query) { return new GenericAggregationOperation(operation, query); }}
And then we can static import the interface into our class:
import static com.example.mongodb.aggregation.GenericAggregationUtils.*;
And use it in the aggregation pipeline together with other spring data AggregationOperation like this:
Aggregation aggregation = newAggregation(YourDocCollection.class, aggregate("$group", "{ \"_id\": \"$title\", \"versions\": { \"$push\": { \"version\":\"$version\", \"author\":\"$author\", \"dateAdded\":\"$dateAdded\"}} }"), sort(Sort.Direction.ASC, "title"), ...);