10

I have a mongo aggregate group query:

db.wizard.aggregate(
{
$group: {
    _id: "$title",
    versions: { $push: {version:"$version", author:"$author", dateAdded:"$dateAdded"}}
    }
})

I need this query in Java Spring-Data-MongoDB, my current solution looks like this:

    Aggregation agg = Aggregation.newAggregation(
            Aggregation.group("title").
                    push("version").as("versions")
    );

Problem is that i don't know how to add more fields to push method (version, author, dateAdded). Is it possible with Spring-Data-MongoDB?

3
  • If you want to put "stringified" mongo-shell query, extend AggregationOperation class and override toDBObject or similar method (depending on drive version). I used to workaround lot of aggregate steps which is not compatible with native Spring Data Commented Sep 8, 2016 at 15:37
  • If need, I can add an example how to do that Commented Sep 8, 2016 at 15:44
  • it would be great if you post an example Commented Sep 9, 2016 at 7:03

3 Answers 3

16

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"));
Sign up to request clarification or add additional context in comments.

Comments

4

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.

Comments

2

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"),
    ...
);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.