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