Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SpringData – Mongo – Aggregation

I am fighting quite a while with the Aggregation Framework of MongoDB and Spring Data and I am actually wondering if the stuff I would like to do is actually possible.

I have the following Mongo documents:

{
  "_id": ObjectId("564520fad4c64dd36fb1f0a4"),
  "_class": "com.sample.Purchase",
  "created": new Date(1447371002645),
  "productId": NumberLong(12),
  "clientId": "c1",
  "price": NumberLong(20)
}

I would like to create the following stats:

List<ClientStatsEntry> entries;

public class ClientStatsEntry  {
   private String clientId;
   private Date firstSeen;
   private Date lastSeen;
   private Long totalPriceSpend;
   private long totalCount;
}

So basically the steps are:

  1. Filter collection by productId (match)
  2. Split all remaining elements by clientIds (groupBy)
  3. Retrieve the created date of the first AND of the last entries
  4. Sum up all prices and store in "totalPrice"
  5. Count all purchases and store it in "totalCount"

I tried to start with that approach, but I can't find a way how to do everything in one aggregation pipepline:

Aggregation agg = newAggregation(
            match(Criteria.where("productId").is(productId)),
            group("clientId").sum("price").as("totalPriceSpend"),
            Aggregation.project("totalPriceSpend", "productId").and("productId").previousOperation());
like image 275
Fritz Avatar asked Aug 24 '26 05:08

Fritz


1 Answers

I believe you are looking for this aggregation pipeline (comments denote the steps outlined):

db.purchase.aggregate([
    /* 1. Filter collection by productId (match) */
    {
        "$match": {
            "productId": productId
        }
    },
    /* 2. Split all remaining elements by clientIds (groupBy) */
    {
        "$group": {
            "_id": "$clientId",
            "firstSeen": { "$min": "$createdDate"}, // 3. a) Retrieve the created date of the first entry
            "lastSeen": { "$max": "$createdDate"}, // 3. b) Retrieve the created date of the last entry
            /* 4. Sum up all prices and store in "totalPrice" */
            "totalPriceSpend": {
                "$sum": "$price"
            },
            /* 5. Count all purchases and store it in "totalCount" */
            "totalCount": {
                "$sum": 1
            }
        }
    }
])

The Spring Data MongoDB aggregation equivalent follows:

Aggregation agg = Aggregation.newAggregation( 
    match(Criteria.where("productId").is(productId)),
    group("clientId")
        .min("createdDate").as("firstSeen")
        .max("createdDate").as("lastSeen")
        .sum("price").as("totalPriceSpend")
        .count().as("totalCount"),
    project("firstSeen", "lastSeen", "totalPriceSpend", "totalCount")
        .and("clientId").previousOperation()
); 
AggregationResults<ClientStatsEntry> result = 
    mongoTemplate.aggregate(agg, ClientStatsEntry.class);
List<ClientStatsEntry> clientStatsList = result.getMappedResults();
like image 88
chridam Avatar answered Aug 26 '26 22:08

chridam