在这个示例中,我们希望聚合一个标签列表,以从 MongoDB 集合(称为 person)中获得特定标签的出现次数,该集合按出现次数降序排序。此示例演示了分组(group)、排序(sort)、投影(project,选择)和展开(unwind,结果拆分)的用法。
关键代码如下:
(1)TagCount 实体
public class TagCount { private String tag; private int n; }
(2)投影关键代码
// 静态导入 import static org.springframework.data.mongodb.core.aggregation.Aggregation.*; // 关键代码 Aggregation agg = newAggregation( project("tags"), unwind("tags"), group("tags").count().as("n"), project("n").and("tag").previousOperation(), sort(DESC, "n") ); AggregationResults<TagCount> results = mongoTemplate.aggregate(agg, "tags", TagCount.class); List<TagCount> tagCount = results.getMappedResults(); for(TagCount tagCount : tagCountList) { System.out.println(tagCount.getTag() + "=" + tagCount.getN()); }
上面示例将用到如下算法:
1、通过使用 newAggregation() 静态工厂方法创建一个新的聚合(Aggregation),我们向其传递一个聚合操作列表,这些聚合操作定义了我们聚合的聚合管道。
2、使用 project 操作,从输入集合中选择 tags 字段(tags 字段是一个字符串数组)。
3、使用 unwind 操作为 tags 字段数组中的每个标签生成一个新的文档。
4、使用 group 操作为每个 tags 值定义一个组,我们为其聚合出现次数(通过使用 count 聚合运算符并将结果收集到一个名为 n 的新字段中)。
5、选择 n 字段,并为上一个 group 操作(因此调用 previousOperation())生成的 ID 字段创建一个别名,名称为 tag。
6、使用 sort 操作,根据标签的出现次数(即 n 字段),按降序对结果列表进行排序。
7、将我们创建的 Aggregation 作为调用 MongoTemplate 的 aggregate() 方法的参数,让 MongoDB 执行实际的聚合操作。
(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)TagCount.java 实体,存放聚合结果
package com.hxstrive.springdata.mongodb.entity; import lombok.Data; /** * 投影结果实体 * @author hxstrive.com */ @Data public class TagCount { private String tag; private int n; }
(4)Person.java 实体,一个文档集合对象
package com.hxstrive.springdata.mongodb.entity; import lombok.Builder; import lombok.Data; import lombok.ToString; @Data @ToString @Builder public class Person { /** 用户ID */ private int id; /** 用户姓名 */ private String name; /** 年龄 */ private int age; /** 电子邮件 */ private String email; /** 用户标签 */ private String[] tags; }
(5)AggregationFrameworkDemo1.java 客户端类
package com.hxstrive.springdata.mongodb; import com.hxstrive.springdata.mongodb.entity.Person; import com.hxstrive.springdata.mongodb.entity.TagCount; import com.hxstrive.springdata.mongodb.entity.Tags; 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.Aggregation; import org.springframework.data.mongodb.core.aggregation.AggregationResults; import java.util.List; import static org.springframework.data.domain.Sort.Direction.DESC; import static org.springframework.data.mongodb.core.aggregation.Aggregation.*; /** * 聚合框架示例1 * @author hxstrive.com */ @SpringBootTest public class AggregationFrameworkDemo1 { @Autowired private MongoTemplate mongoTemplate; @BeforeEach public void init() { mongoTemplate.dropCollection(Person.class); // 准备数据 // 工程师 engineer // 厨师 chef // 程序员 programmer // 运动员 sportsman mongoTemplate.insert(Person.builder().id(100).name("Tom").age(27).email("Tom@sina.com") .tags(new String[]{ "engineer", "sportsman" }).build()); mongoTemplate.insert(Person.builder().id(200).name("Helen").age(30).email("Helen@outlook.com") .tags(new String[]{ "programmer", "chef" }).build()); mongoTemplate.insert(Person.builder().id(300).name("Bill").age(47).email("bill@gmail.com") .tags(new String[]{ "engineer" }).build()); mongoTemplate.insert(Person.builder().id(400).name("Joe").age(20).email("joe@163.com") .tags(new String[]{ "chef", "sportsman" }).build()); } @Test public void contextLoads() { Aggregation agg = newAggregation( project("tags"), unwind("tags"), group("tags").count().as("n"), project("n").and("tag").previousOperation(), sort(DESC, "n") ); AggregationResults<TagCount> results = mongoTemplate.aggregate(agg, Person.class, TagCount.class); List<TagCount> tagCountList = results.getMappedResults(); for(TagCount tagCount : tagCountList) { System.out.println(tagCount.getTag() + "=" + tagCount.getN()); } // 结果: // sportsman=2 // chef=2 // engineer=2 // programmer=1 // 执行的聚合语句如下: // [ // { "$project" : { "tags" : 1}}, // { "$unwind" : "$tags"}, // { "$group" : { "_id" : "$tags", "n" : { "$sum" : 1}}}, // { "$project" : { "n" : 1, "_id" : 0, "tag" : "$_id"}}, // { "$sort" : { "n" : -1}} // ] } }