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

基于Spring Data MongoDB 2.7.3实现指定城市匹配的聚合查询

问题描述

现有MongoDB文档结构

country{id="1", name="France", cities=[{detail="new_city"}, {from="Paris"}, {from="Lyon"} ]}
country{id="2", name="UK", cities=[{detail="new_city"}, {from="Paris"}, {from="London"}, {from="Liverpool"} ]}

需求场景

  1. 当搜索from="Paris"时,若cities数组中存在该元素,返回对应文档并将cities字段过滤为该元素,输出如下:
country{id="1", name="France", cities={from="Paris"}}
country{id="2", name="UK", cities={from="Paris"}}
  1. 当搜索from="Los Angeles"时,若cities数组中不存在该元素,返回对应文档并将cities字段过滤为含detail="new_city"的元素,输出如下:
country{id="1", name="France", cities={detail="new_city"}}
country{id="2", name="UK", cities={detail="new_city"}}

使用Spring Boot版本为spring-boot-starter-data-mongodb 2.7.3,如何通过聚合(aggregation)实现上述需求?


实现方案

核心逻辑

先通过数组筛选分别提取目标from元素和new_city元素,再根据目标元素是否存在做条件判断,最终替换原cities字段为符合要求的单个元素。

原生MongoDB聚合管道示例

先看原生聚合语句,便于理解逻辑:

db.country.aggregate([
  // 筛选出匹配目标from的元素、以及new_city元素
  {
    $addFields: {
      matchedCities: {
        $filter: {
          input: "$cities",
          cond: { $eq: ["$$this.from", "Paris"] } // 替换为用户搜索参数
        }
      },
      newCity: {
        $filter: {
          input: "$cities",
          cond: { $eq: ["$$this.detail", "new_city"] }
        }
      }
    }
  },
  // 根据matchedCities是否为空,选择最终的cities值
  {
    $project: {
      id: 1,
      name: 1,
      cities: {
        $cond: {
          if: { $gt: [{ $size: "$matchedCities" }, 0] },
          then: { $arrayElemAt: ["$matchedCities", 0] },
          else: { $arrayElemAt: ["$newCity", 0] }
        }
      }
    }
  }
])

Spring Boot Data MongoDB 代码实现

结合spring-boot-starter-data-mongodb 2.7.3的API,用Java代码构建聚合管道:

import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
import org.springframework.data.mongodb.core.aggregation.ConditionalOperators;
import org.springframework.data.mongodb.core.aggregation.ProjectionOperation;
import org.springframework.data.mongodb.core.aggregation.ArrayOperators;
import org.springframework.data.mongodb.core.query.Criteria;
import java.util.List;

// 注入MongoTemplate
private final MongoTemplate mongoTemplate;

public List<Country> getFilteredCountries(String targetFrom) {
    // 1. 筛选出匹配目标from的cities元素
    AggregationOperation addMatchedCities = Aggregation.addFields()
            .field("matchedCities")
            .withValue(ArrayOperators.Filter.filter("cities")
                    .as("city")
                    .by(Criteria.where("city.from").is(targetFrom)))
            .build();

    // 2. 筛选出detail为new_city的cities元素
    AggregationOperation addNewCity = Aggregation.addFields()
            .field("newCity")
            .withValue(ArrayOperators.Filter.filter("cities")
                    .as("city")
                    .by(Criteria.where("city.detail").is("new_city")))
            .build();

    // 3. 投影操作:根据matchedCities是否为空,选择最终的cities值
    ProjectionOperation projection = Aggregation.project("id", "name")
            .and(ConditionalOperators.Cond.when(ArrayOperators.Size.lengthOfArray("matchedCities").greaterThanValue(0))
                    .then(ArrayOperators.ArrayElemAt.arrayElementAt("matchedCities", 0))
                    .otherwise(ArrayOperators.ArrayElemAt.arrayElementAt("newCity", 0)))
            .as("cities");

    // 组装并执行聚合
    Aggregation aggregation = Aggregation.newAggregation(addMatchedCities, addNewCity, projection);
    return mongoTemplate.aggregate(aggregation, "country", Country.class).getMappedResults();
}

// 对应的实体类示例
class Country {
    private String id;
    private String name;
    private City cities; // 对应需求的单个对象输出,若需数组可改为List<City>

    // getter、setter、构造方法省略
}

class City {
    private String detail;
    private String from;

    // getter、setter、构造方法省略
}

代码说明

  • 用ArrayOperators.Filter实现数组元素的精准筛选,分别得到匹配目标from的元素列表和new_city元素列表
  • 通过ConditionalOperators.Cond做分支判断:如果匹配到目标from元素,就取第一个匹配项;否则取new_city元素
  • 实体类Country的cities字段定义为单个City对象,与需求输出格式一致,若需要保留数组格式可修改为List<City>

内容的提问来源于stack exchange,提问作者Adam D

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 19:40:57