You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Spring Boot中实现MongoDB Aggregation的Facet操作

问题:Spring Boot中无法复现Mongo Compass的聚合查询结果

我能在Mongo Compass中执行以下聚合查询得到正确结果,但在Spring Boot应用里始终无法复现相同效果。

Compass中的聚合查询:

[
  {
    $match: {
      $and: [
        {
          channelId: {
            $in: [1001],
          },
        },
      ],
    },
  },
  {
    $facet: {
      operationTypes: [
        {
          $group: {
            _id: "$operationType",
            count: { $sum: 1 },
          },
        },
        {
          $project: {
            _id: 0,
            operationType: "$_id",
            count: 1,
          },
        },
      ],
      states: [
        {
          $group: {
            _id: "$state",
            count: { $sum: 1 },
          },
        },
        {
          $project: {
            _id: 0,
            state: "$_id",
            count: 1,
          },
        },
      ],
      results: [
        {
          $group: {
            _id: "$result",
            count: { $sum: 1 },
          },
        },
        {
          $project: {
            _id: 0,
            result: "$_id",
            count: 1,
          },
        },
      ],
    },
  },
]

尝试过的无效方案

方案1:自定义Aggregation调用方法

我尝试创建AggregationOperation列表并调用以下方法,但没有效果:

public List<AggregationResultTest> getAggregation(Aggregation aggregation) {
    AggregationResults<AggregationResultTest> results = mongoTemplate.aggregate(aggregation, COLLECTION_NAME,
            AggregationResultTest.class);
    return results.getMappedResults();
}

方案2:Compass导出的代码

我也尝试使用Mongo Compass的EXPORT TO LANGUAGE功能生成代码,但同样无效,生成的代码如下:

import java.util.Arrays;
import org.bson.Document;

Arrays.asList(new Document("$match", 
    new Document("channelId", 1001L)), 
    new Document("$facet", 
    new Document("operationTypes", Arrays.asList(new Document("$group", 
                new Document("_id", "$operationType")
                        .append("count", 
                new Document("$sum", 1L))), 
                new Document("$project", 
                new Document("_id", 0L)
                        .append("operationType", "$_id")
                        .append("count", 1L))))
            .append("states", Arrays.asList(new Document("$group", 
                new Document("_id", "$state")
                        .append("count", 
                new Document("$sum", 1L))), 
                new Document("$project", 
                new Document("_id", 0L)
                        .append("state", "$_id")
                        .append("count", 1L))))
            .append("results", Arrays.asList(new Document("$group", 
                new Document("_id", "$result")
                        .append("count", 
                new Document("$sum", 1L))), 
                new Document("$project", 
                new Document("_id", 0L)
                        .append("result", "$_id")
                        .append("count", 1L))))))

需求

需要在如下结构的Repository中实现该聚合逻辑:

@Repository
public class OperationHistoryRepository {

    private final MongoTemplate mongoTemplate;

    // 聚合方法需要写在这里
}

正确实现方案

1. 定义结果映射实体类

首先创建对应$facet输出结果的实体类:

// 单个统计项的实体
public class CountItem {
    private String operationType;
    private String state;
    private String result;
    private Integer count;

    // 生成getter、setter和无参构造方法
}

// 聚合结果的根实体
public class AggregationResult {
    private List<CountItem> operationTypes;
    private List<CountItem> states;
    private List<CountItem> results;

    // 生成getter、setter和无参构造方法
}

2. 在Repository中实现聚合方法

@Repository
public class OperationHistoryRepository {

    private static final String COLLECTION_NAME = "your_collection_name"; // 替换为实际集合名
    private final MongoTemplate mongoTemplate;

    // 构造方法注入MongoTemplate
    public OperationHistoryRepository(MongoTemplate mongoTemplate) {
        this.mongoTemplate = mongoTemplate;
    }

    public AggregationResult getAggregationResult(Long channelId) {
        // 1. 构建$match阶段
        MatchOperation matchOperation = Aggregation.match(Criteria.where("channelId").in(channelId));

        // 2. 构建operationTypes的子聚合
        Aggregation operationTypeAgg = Aggregation.newAggregation(
                Aggregation.group("operationType").count().as("count"),
                Aggregation.project("count").and("_id").as("operationType").andExclude("_id")
        );

        // 3. 构建states的子聚合
        Aggregation stateAgg = Aggregation.newAggregation(
                Aggregation.group("state").count().as("count"),
                Aggregation.project("count").and("_id").as("state").andExclude("_id")
        );

        // 4. 构建results的子聚合
        Aggregation resultAgg = Aggregation.newAggregation(
                Aggregation.group("result").count().as("count"),
                Aggregation.project("count").and("_id").as("result").andExclude("_id")
        );

        // 5. 构建$facet阶段
        FacetOperation facetOperation = Aggregation.facet(operationTypeAgg, stateAgg, resultAgg)
                .as("operationTypes", "states", "results");

        // 6. 组装完整聚合
        Aggregation aggregation = Aggregation.newAggregation(matchOperation, facetOperation);

        // 7. 执行聚合并映射结果
        AggregationResults<AggregationResult> results = mongoTemplate.aggregate(
                aggregation, COLLECTION_NAME, AggregationResult.class
        );

        return results.getUniqueMappedResult();
    }
}

说明

  • 使用Spring Data MongoDB提供的Aggregation工具类构建聚合阶段,比直接用Document更易维护且类型安全
  • 因为$facet返回的是单个包含三个列表的文档,所以用getUniqueMappedResult()获取结果,而不是getMappedResults()
  • 确保实体类的字段名和聚合输出的键名完全一致,Spring Data会自动映射

内容的提问来源于stack exchange,提问作者Serdar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 01:41:07