Spring数据MongoDB(聚合)

问题描述 投票:0回答:1

让我们考虑以下文件:

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"} ]}

如果用户搜索“from”=“Paris”并且城市数组包含“from”=“Paris”,则输出将为:

country{id="1", name="France", cities={from="Paris"}}
country{id="2", name="UK", cities={from="Paris"}}

如果用户搜索“from”=“Los Angeles”并且城市数组不包含“from”=“Los Angeles”,那么我们必须在城市数组中搜索包含“detail”=“new_city”的对象,所以输出将是:

country{id="1", name="France", cities={detail="new_city"}}
country{id="2", name="UK", cities={detail="new_city"}}

如何使用聚合来执行此操作?

SpringBoot版本:spring-boot-starter-data-mongodb 2.7.3

谢谢你

mongodb spring-boot mongodb-query aggregation-framework spring-data-mongodb
1个回答
0
投票

您应该能够使用

$addFields
此处提到)和
$filter
聚合运算符
的组合。

使用

$addFields
函数进行
$filter
操作用于在每个文档中创建一个名为
filteredCities
的新字段。
该新字段包含与字段
"from"
等于搜索词 (
searchTerm
) 的条件相匹配的城市数组。

addField("filteredCities").withValue(
        filter("cities")
                .as("city")
                .by(valueOf("city.from").equalToValue(searchTerm))
)

$filter
循环遍历
cities
数组,并且对于每个对象(别名为
city
),检查
from
字段是否与
searchTerm
匹配。如果是,则该对象将包含在新的
filteredCities
数组中。

使用另一个

$addFields
操作向每个文档添加另一个名为
finalCities
的字段。这是根据您提供的标准保存要显示的城市的最终列表。

addField("finalCities").withValue(
        conditional(
                valueOf("filteredCities").isEmpty(),
                arrayOf(valueOf("cities").filter()
                        .as("city")
                        .by(valueOf("city.detail").equalToValue("new_city"))),
                valueOf("filteredCities")
        )
)

$cond
条件运算符检查
filteredCities
是否为空。
根据结果,它设置
finalCities
的值:

  • 如果

    filteredCities
    为空,则使用
    $filter
    函数创建一个数组,仅包含原始
    "detail" = "new_city"
    数组中具有
    cities
    的城市对象。

  • 如果

    filteredCities
    不为空,则将
    finalCities
    的值设置为
    filteredCities
    ,其中已经包含满足
    "from" = searchTerm
    条件的城市。

最后一步,

$project
,重塑每个文档以仅包含字段
id
name
finalCities
(重命名为
cities
)。

Input Document
---------------
{
  id: "1",
  name: "France",
  cities: [{detail: "new_city"}, {from: "Paris"}, {from: "Lyon"}]
}
      |
      |
      v
$addFields (filteredCities)
---------------------------
{
  id: "1",
  name: "France",
  cities: [{detail: "new_city"}, {from: "Paris"}, {from: "Lyon"}],
  filteredCities: [{from: "Paris"}]   <--- New field based on $filter
}
      |
      |
      v
$addFields (finalCities)
------------------------
{
  id: "1",
  name: "France",
  cities: [{detail: "new_city"}, {from: "Paris"}, {from: "Lyon"}],
  filteredCities: [{from: "Paris"}],
  finalCities: [{from: "Paris"}]       <--- New field based on $cond
}
      |
      |
      v
$project
--------
{
  id: "1",
  name: "France",
  cities: [{from: "Paris"}]            <--- Reshaped document
}

假设您有一个带有

Country
字段的
List<Map<String, String>> cities
类,则用于执行此聚合管道的 Java 代码可能如下所示:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;

import java.util.List;
import java.util.Map;

public class CountryService {

    @Autowired
    private MongoTemplate mongoTemplate;

    public List<Map> getCountriesBasedOnCities(String searchTerm) {
        Aggregation aggregation = newAggregation(
                addFields()
                        .addField("filteredCities").withValue(
                                filter("cities")
                                        .as("city")
                                        .by(valueOf("city.from").equalToValue(searchTerm))
                        ).build(),
                addFields()
                        .addField("finalCities").withValue(
                                conditional(
                                        valueOf("filteredCities").isEmpty(),
                                        arrayOf(valueOf("cities").filter()
                                                .as("city")
                                                .by(valueOf("city.detail").equalToValue("new_city"))),
                                        valueOf("filteredCities")
                                )
                        ).build(),
                project("id", "name")
                        .and("finalCities").as("cities")
        );

        AggregationResults<Map> results = mongoTemplate.aggregate(aggregation, "country", Map.class);

        return results.getMappedResults();
    }
}

"country"
代表 MongoDB 集合:将其名称替换为存储
country
文档的集合中的名称。

© www.soinside.com 2019 - 2024. All rights reserved.