Spring Boot+MongoDB如何获取指定分组的去重爱好名称
解决方案
第一步:明确文档结构
假设你的Person文档结构大致如下:
{ "_id": ObjectId("60d21b4667d0d8992e610c85"), "name": "Alice", "hobbies": [ { "name": "A", "hobbyGroup": "group1" }, { "name": "AB", "hobbyGroup": "group1" }, { "name": "ABC", "hobbyGroup": "group1" }, { "name": "A", "hobbyGroup": "group1" }, { "name": "Hiking", "hobbyGroup": "group2" } ] }
第二步:MongoDB原生聚合查询
要实现指定hobbyGroup的去重爱好名称列表,正确的聚合流程是:
- 先过滤包含目标分组的文档(减少后续处理的数据量)
- 拆分嵌套的hobbies数组,将每个爱好转为独立文档
- 过滤出目标分组的单个爱好
- 去重并收集所有爱好名称
对应的原生查询语句:
db.person.aggregate([ // 过滤存在目标hobbyGroup的Person文档 { $match: { "hobbies.hobbyGroup": "group1" } }, // 拆分hobbies数组 { $unwind: "$hobbies" }, // 过滤出属于group1的爱好 { $match: { "hobbies.hobbyGroup": "group1" } }, // 按爱好名称去重并收集为数组 { $group: { _id: null, uniqueHobbies: { $addToSet: "$hobbies.name" } } }, // 只保留结果数组字段 { $project: { _id: 0, uniqueHobbies: 1 } } ])
执行后会返回:
{ "uniqueHobbies": ["A", "AB", "ABC"] }
第三步:Spring Boot中用MongoTemplate实现
在Spring Boot里,你可以通过MongoTemplate来构建这个聚合操作:
首先定义Hobby的实体类:
public class Hobby { private String name; private String hobbyGroup; // 构造器、getter、setter省略 }
Person实体类:
import org.springframework.data.annotation.Id; import org.springframework.data.mongodb.core.mapping.Document; import java.util.List; @Document(collection = "person") public class Person { @Id private String id; private String name; private List<Hobby> hobbies; // 构造器、getter、setter省略 }
然后编写聚合逻辑:
import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.aggregation.Aggregation; import org.springframework.data.mongodb.core.aggregation.AggregationResults; import org.springframework.stereotype.Service; import java.util.List; @Service public class PersonService { private final MongoTemplate mongoTemplate; public PersonService(MongoTemplate mongoTemplate) { this.mongoTemplate = mongoTemplate; } public List<String> getUniqueHobbiesByGroup(String hobbyGroup) { Aggregation aggregation = Aggregation.newAggregation( // 过滤包含目标分组的Person文档 Aggregation.match(Criteria.where("hobbies.hobbyGroup").is(hobbyGroup)), // 拆分hobbies数组 Aggregation.unwind("hobbies"), // 过滤出目标分组的单个爱好 Aggregation.match(Criteria.where("hobbies.hobbyGroup").is(hobbyGroup)), // 去重收集爱好名称 Aggregation.group().addToSet("hobbies.name").as("uniqueHobbies"), // 只返回结果数组 Aggregation.project("uniqueHobbies").andExclude("_id") ); AggregationResults<HobbyResult> results = mongoTemplate.aggregate( aggregation, "person", HobbyResult.class ); return results.getUniqueMappedResult().getUniqueHobbies(); } // 用于接收聚合结果的DTO static class HobbyResult { private List<String> uniqueHobbies; // getter、setter省略 } }
关键说明
- 两次
$match的作用:第一次先过滤掉完全不包含目标分组的Person文档,减少$unwind处理的数据量;第二次确保拆分后的每个爱好都属于目标分组。 - 使用
$addToSet而不是$push,因为$addToSet会自动去重,正好满足你的需求。 - 如果不需要先过滤整个文档,也可以直接跳过第一个
$match,但保留的话性能会更好,尤其是数据量较大时。
内容的提问来源于stack exchange,提问作者Alan
相关产品推荐
相关产品推荐

