该示例基于 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}}} // ] } }