SpringData - Mongo - 聚合查询

3

我一直在与MongoDB和Spring Data的聚合框架搏斗,实际上我想知道我想做的事情是否可能。

我有以下Mongo文档:

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

我想创建以下统计数据:
List<ClientStatsEntry> entries;

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

基本上步骤如下:

  1. 按productId进行过滤(匹配)集合
  2. 按clientIds分组剩余所有元素(groupBy)
  3. 检索第一条和最后一条条目的创建日期
  4. 将所有价格加起来并存储在"totalPrice"中
  5. 计算所有购买数量并将其存储在"totalCount"中

我尝试使用这种方法开始,但是我找不到一种方法可以在一个聚合管道中完成所有操作:

Aggregation agg = newAggregation(
            match(Criteria.where("productId").is(productId)),
            group("clientId").sum("price").as("totalPriceSpend"),
            Aggregation.project("totalPriceSpend", "productId").and("productId").previousOperation());
1个回答

3
我相信您正在寻找这个聚合管道(注释表示概述的步骤):
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
            }
        }
    }
])

Spring Data MongoDB 的聚合操作等效方法如下:

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();

1
真是太棒了!非常感谢你的帮助,这确实运行得很好! :) - Fritz
1
仅仅是为了补充之前的回答,match和group方法之间缺少一个"sort()"操作,它按照"createdDate"进行排序...然后它就可以很好地工作了。 - Fritz
@Fritz,我完全忽略了那个关键点。在这种情况下,$min和$max运算符是合适的替代品。感谢您的纠正,稍后会更新我的答案。 - chridam

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接