Spring Data MongoDB 教程

聚合框架示例3

该示例基于 MongoDB 聚合框架查询文档中人口超过 10000 的省。并且我们添加了额外的排序,以使用不同的 MongoDB 版本产生稳定的结果。此示例演示分组、排序和匹配。关键代码如下:

(1)结果类型映射实体

public class StateStats {
    // 省名称
    @Id
    private String state;
    // 人口数
    @Field("totalPop")
    private int totalPopulation;
}

(2)聚合关键代码

// 静态导入
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;

TypedAggregation<ZipInfo> aggregation = newAggregation(ZipInfo.class,
   // 使用 state 分组,求 population 的和,别名为 totalPop
   group("state").sum("population").as("totalPop"),
   // 根据 totalPop 排序
   sort(ASC, previousOperation(), "totalPop"),
   // 匹配 totalPop 大于等于 15000 的省
   match(where("totalPop").gte(15000))
);

AggregationResults<StateStats> result = mongoTemplate.aggregate(aggregation, StateStats.class);
List<StateStats> stateStatsList = result.getMappedResults();
for(StateStats stateStats : stateStatsList) {
   System.out.println(JSONObject.toJSONString(stateStats));
}

上面代码使用了以下算法:

(1)按 state 字段对输入集合进行分组,计算 population 字段的总和,并将结果存储在新字段 “totalPop” 中。

(2)除了 “totalPop” 字段之外,按升序对中间结果进行排序。

(3)使用接受 Criteria 作为参数的 match 操作筛选中间结果。

注意:我们可以从 newAggregation() 方法的第一个参 ZipInfo 类派生输入集合的名称,因此在调用 mongoTemplate.aggregate() 时不需要指定输入文档类型。

完整示例

(1)application.properties 配置

# Log
logging.level.root=debug

# MongoDB
spring.data.mongodb.uri=mongodb://localhost:27017/test

(2)AppConfig.java 配置类

package com.hxstrive.springdata.mongodb.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.MongoDatabaseFactory;
import org.springframework.data.mongodb.core.MongoTemplate;

/**
* 配置 MongoTemplate
* @author hxstrive.com 2022/12/23
*/
@Slf4j
@Configuration
public class AppConfig {

   @Bean
   public MongoTemplate mongoTemplate(MongoDatabaseFactory mongoDatabaseFactory) {
       log.info("mongoTemplate({}, {})", mongoDatabaseFactory);
       return new MongoTemplate(mongoDatabaseFactory);
   }

}

(3)映射给定的输入集合的结构

import lombok.Builder;
import lombok.Data;

/**
* city 集合分组后的中间结果实体类型
* @author hxstrive.com
*/
@Builder
@Data
public class ZipInfo {
   // 州名称
   private String state;
   // 城市名称
   private String city;
   // 人口数量
   private int population;
}

(4)投影结果映射的实体

import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Field;

/**
* 结果映射实体
* @author hxstrive.com
*/
@Data
public class StateStats {
   // 省名称
   @Id
   private String state;
   // 人口数
   @Field("totalPop")
   private int totalPopulation;
}

(5)客户端代码,通过 MongoTemplate 实现分组和投影。

import com.alibaba.fastjson.JSONObject;
import com.hxstrive.springdata.mongodb.entity.demo3.StateStats;
import com.hxstrive.springdata.mongodb.entity.demo3.ZipInfo;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.aggregation.TypedAggregation;
import java.util.List;
import static org.springframework.data.domain.Sort.Direction.ASC;
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;
import static org.springframework.data.mongodb.core.query.Criteria.where;

/**
* 聚合框架示例2
* @author hxstrive.com
*/
@SpringBootTest
public class AggregationFrameworkDemo3 {

   @Autowired
   private MongoTemplate mongoTemplate;

   @BeforeEach
   public void init() {
       mongoTemplate.dropCollection(ZipInfo.class);

       // 准备数据
       mongoTemplate.insert(ZipInfo.builder().state("SiChuan").city("ChengDu").population(10000).build());
       mongoTemplate.insert(ZipInfo.builder().state("SiChuan").city("DaZhou").population(15000).build());
       mongoTemplate.insert(ZipInfo.builder().state("GuangZhou").city("ShangHai").population(11000).build());
       mongoTemplate.insert(ZipInfo.builder().state("GuangZhou").city("ShenZhen").population(8000).build());
       mongoTemplate.insert(ZipInfo.builder().state("JiangSu").city("NanJing").population(10900).build());
   }

   @Test
   public void contextLoads() {
       TypedAggregation<ZipInfo> aggregation = newAggregation(ZipInfo.class,
               // 使用 state 分组,求 population 的和,别名为 totalPop
               group("state").sum("population").as("totalPop"),
               // 根据 totalPop 排序
               sort(ASC, previousOperation(), "totalPop"),
               // 匹配 totalPop 大于等于 15000 的省
               match(where("totalPop").gte(15000))
       );

       AggregationResults<StateStats> result = mongoTemplate.aggregate(aggregation, StateStats.class);
       List<StateStats> stateStatsList = result.getMappedResults();
       for(StateStats stateStats : stateStatsList) {
           System.out.println(JSONObject.toJSONString(stateStats));
       }
       // 结果:
       // {"state":"GuangZhou","totalPopulation":19000}
       // {"state":"SiChuan","totalPopulation":25000}

       // 执行的聚合语句如下:
       // [
       //    { "$group" : { "_id" : "$state", "totalPop" : { "$sum" : "$population"}}},
       //    { "$sort" : { "_id" : 1, "totalPop" : 1}},
       //    { "$match" : { "totalPop" : { "$gte" : 15000}}}
       // ]
   }

}
说说我的看法
全部评论(
没有评论
关于
本网站属于个人的非赢利性网站,转载的文章遵循原作者的版权声明,如果原文没有版权声明,请来信告知:hxstrive@outlook.com
公众号