How to do a Mongo aggregation query in Spring Data?

We Are Going To Discuss About How to do a Mongo aggregation query in Spring Data?. So lets Start this Java Article.

How to do a Mongo aggregation query in Spring Data?

  1. How to do a Mongo aggregation query in Spring Data?

    Although this is old thread, but I hope whoever found this thread can now safely for doing multi stage/pipeline aggregation(not quite sure what it's call) in MongoRepository.
    As I'm also struggling looking for clue and example of aggregation in mongo repository without mongo template.

  2. Mongo aggregation query in Spring Dat

    Although this is old thread, but I hope whoever found this thread can now safely for doing multi stage/pipeline aggregation(not quite sure what it's call) in MongoRepository.
    As I'm also struggling looking for clue and example of aggregation in mongo repository without mongo template.

Solution 1

Although this is old thread, but I hope whoever found this thread can now safely for doing multi stage/pipeline aggregation(not quite sure what it’s call) in MongoRepository.
As I’m also struggling looking for clue and example of aggregation in mongo repository without mongo template.

But now, I’m able to do the Aggregation pipeline as per spring doc said in here

My aggregation looks like this in mongoshell:

db.getCollection('SalesPo').aggregate([
    {$project: {
        month: {$month: '$poDate'},
        year: {$year: '$poDate'},
        amount: 1,
        poDate: 1
     }},
      {$match: {$and : [{year:2020} , {month:7}] 
     }}
      ,
      {$group: { 
          '_id': {
            month: {$month: '$poDate'},
            year: {$year: '$poDate'} 
          },
          totalPrice: {$sum: {$toDecimal:'$amount'}},
          }
      },
    {$project: {
        _id: 0,
        totalPrice: {$toString: '$totalPrice'}
     }}
 ])

While I transform it into @Aggregation annotation in MongoRepository become like this below (I’m removing the aposthrephe and also replace with method params):

@Repository
public interface SalesPoRepository extends MongoRepository<SalesPo, String> {

@Aggregation(pipeline = {"{$project: {\n" +
        "        month: {$month: $poDate},\n" +
        "        year: {$year: $poDate},\n" +
        "        amount: 1,\n" +
        "        poDate: 1\n" +
        "     }}"
        ,"{$match: {$and : [{year:?0} , {month:?1}] \n" +
        "     }}"
        ,"{$group: { \n" +
        "          '_id': {\n" +
        "            month: {$month: $poDate},\n" +
        "            year: {$year: $poDate} \n" +
        "          },\n" +
        "          totalPrice: {$sum: {$toDecimal:$amount}},\n" +
        "          }\n" +
        "      }"
    ,"{$project: {\n" +
        "        _id: 0,\n" +
        "        totalPrice: {$toString: $totalPrice}\n" +
        "     }}"})
    AggregationResults<SumPrice> sumPriceThisYearMonth(Integer year, Integer month);

My Document looks like this:

@Document(collection = "SalesPo")
@Data
public class SalesPo {
  @Id
  private String id;
  @JsonSerialize(using = LocalDateSerializer.class)
  private LocalDate poDate;
  private BigDecimal amount;
}

And the SumPrice class for hold the projections:

@Data
public class SumPrice {
  private BigDecimal totalPrice;
}

I hope this answer can help whoever try to do aggregation in mongorepository without using mongotemplate.

Original Author Of This Content

Solution 2

You can implement the AggregationOperation and write the custom aggregation operation query and then use MongoTemplate to execute any mongo shell query you have executed in your mongo shell as below:

Custom Aggregation Operation

import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
import org.springframework.data.mongodb.core.aggregation.AggregationOperationContext;

public class CustomAggregationOperation implements AggregationOperation {

  private String jsonOperation;

  public CustomAggregationOperation(String jsonOperation) {
    this.jsonOperation = jsonOperation;
  }

  @Override
  public org.bson.Document toDocument(AggregationOperationContext aggregationOperationContext) {
    return aggregationOperationContext.getMappedObject(org.bson.Document.parse(jsonOperation));
  }
}

Any Mongo Shell Aggregation query executor

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.aggregation.TypedAggregation;
import org.springframework.stereotype.Service;
import sample.data.mongo.models.Course;

@Service
public class LookupAggregation {

  @Autowired
  MongoTemplate mongoTemplate;

  public void LookupAggregationExample() {

    AggregationOperation unwind = Aggregation.unwind("studentIds");

    String query1 = "{$lookup: {from: 'student', let: { stuId: { $toObjectId: '$studentIds' } },"
        + "pipeline: [{$match: {$expr: { $eq: [ '$_id', '$stuId' ] },},}, "
        + "{$project: {isSendTemplate: 1,openId: 1,stu_name: '$name',stu_id: '$_id',},},], "
        + "as: 'student',}, }";

    TypedAggregation<Course> aggregation = Aggregation.newAggregation(
        Course.class,
        unwind,
        new CustomAggregationOperation(query1)
    );

    AggregationResults<Course> results =
        mongoTemplate.aggregate(aggregation, Course.class);
    System.out.println(results.getMappedResults());
  }
}

For more details, Have a look at the Github repository classes: CustomAggregationOperation & LookupAggregation

Other approaches also using MongoTemplate:

#1. Define an interface for your custom code for Model Post:

interface CustomPostRepository {
     List<Post> yourCustomMethod();
}

#2. Add implementation for this class and follow the naming convention to make sure we can find the class.

class CustomPostRepositoryImpl implements CustomPostRepository {

    @Autowired
    private MongoOperations mongoOperations;

    public List<Post> yourCustomMethod() {

      // custom match queries here
      MatchOperation match = null;
      // Group by , Lookup others stuff goes here
      // For details: https://docs.spring.io/spring-data/mongodb/docs/current/api/org/springframework/data/mongodb/core/aggregation/Aggregation.html

      Aggregation aggregate = Aggregation.newAggregation(match);

      AggregationResults<Post> orderAggregate = mongoOperations.aggregate(aggregate,
                      Post.class, Post.class);
      return orderAggregate.getMappedResults();

    }
}

#3. Now let your base repository interface extend the custom one and the infrastructure will automatically use your custom implementation:

interface PostRepository extends CrudRepository<Post, Long>, CustomPostRepository {

}

Original Author Of This Content

Conclusion

So This is all About This Tutorial. Hope This Tutorial Helped You. Thank You.

Also Read,

Siddharth

I am an Information Technology Engineer. I have Completed my MCA And I have 4 Year Plus Experience, I am a web developer with knowledge of multiple back-end platforms Like PHP, Node.js, Python and frontend JavaScript frameworks Like Angular, React, and Vue.

Leave a Comment