You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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的去重爱好名称列表,正确的聚合流程是:

  1. 先过滤包含目标分组的文档(减少后续处理的数据量)
  2. 拆分嵌套的hobbies数组,将每个爱好转为独立文档
  3. 过滤出目标分组的单个爱好
  4. 去重并收集所有爱好名称

对应的原生查询语句:

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 10:15:47