我已经在https://github.com/VonC/countrysearch岁时建立了一个项目
它包括src/main/java/com/example/countrysearch/service/CountryService.java
,使用Aggregation
具有以下步骤的管道:
Filtered Cities Addition:这将根据城市的from
字段是否与searchTerm
字段匹配来过滤城市.然后,过滤后的数组存储在一个名为filteredCities
的新字段中.
Conditional Logic for Final Cities:管道判断filteredCities
是否为空(大小为零).如果为空,则管道根据设置为new_city
的detail
字段过滤城市时会发生回退.结果数组存储在另一个名为finalCities
的新字段中.
Output Formatting:管道中的最终操作投影(或 Select )最终输出中所需的字段,如id
、name
和finalCities
.finalCities
在最终输出中被重命名为cities
.
+----------------------+
| Input Document |
+----------------------+
|
|
v
+------------------------+
| Add 'filteredCities' | ---> Filter cities based on the 'from' field
+------------------------+
|
|
v
+------------------------+
| Conditional Logic | ---> Check if 'filteredCities' is empty; fallback to 'detail="new_city"'
+------------------------+
|
|
v
+------------------------+
| Output Formatting | ---> Project required fields ('id', 'name', 'finalCities')
+------------------------+
|
|
v
+------------------------+
| Final Output |
+------------------------+
Full CountryService.java
Code
package com.example.countrysearch.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.*;
import org.springframework.stereotype.Service;
import static org.springframework.data.mongodb.core.aggregation.Aggregation.project;
import com.example.countrysearch.model.Country;
import java.util.List;
@Service
public class CountryService {
@Autowired
private MongoTemplate mongoTemplate;
public List<Country> findCountriesByCity(String searchTerm) {
// AggregationExpression for 'filteredCities'
AggregationExpression filterCitiesExpression = ArrayOperators.Filter.filter("cities")
.as("city")
.by(ComparisonOperators.Eq.valueOf("city.from").equalToValue(searchTerm));
// First addFields operation to add 'filteredCities'
AggregationOperation addFieldsFilteredCities = project("id", "name", "cities")
.and(filterCitiesExpression)
.as("filteredCities");
// AggregationExpression for conditional logic for 'finalCities'
AggregationExpression conditionalFinalCities = ConditionalOperators.when(
ComparisonOperators.Eq.valueOf(
ArrayOperators.Size.lengthOfArray("filteredCities")
).equalToValue(0)
)
.then(
ArrayOperators.Filter.filter("cities")
.as("city")
.by(
ComparisonOperators.Eq.valueOf("city.detail").equalToValue("new_city")
)
)
.otherwise("$filteredCities");
// Second addFields operation to add 'finalCities'
AggregationOperation addFieldsFinalCities = project("id", "name")
.and(conditionalFinalCities).as("finalCities");
// Final project operation to shape the output
AggregationOperation projectFinal = project("id", "name")
.and("finalCities").as("cities");
// Build the aggregation pipeline
Aggregation aggregation = Aggregation.newAggregation(
addFieldsFilteredCities,
addFieldsFinalCities,
projectFinal
);
// Execute the aggregation
AggregationResults<Country> result = mongoTemplate.aggregate(aggregation, "country", Country.class);
return result.getMappedResults();
}
}
在filterCitiesExpression
中,如果我想添加另一个比较,例如:ComparisonOperators.Eq... AND ComparisonOperators.Ne...;
,.by()
方法中的语法是什么?
要在.by()
方法中添加多个比较,可以使用BooleanOperators.And.and
组合多个条件.语法应如下所示:
AggregationExpression filterCitiesExpression = ArrayOperators.Filter.filter("cities")
.as("city")
.by(
BooleanOperators.And.and(
ComparisonOperators.Eq.valueOf("city.from").equalToValue(searchTerm),
ComparisonOperators.Ne.valueOf("city.someOtherField").notEqualToValue(someOtherValue)
)
);
此示例将过滤包含对象的数组,其中city.from
等于searchTerm
,city.someOtherField
不等于someOtherValue
.
在需要向Cities数组添加筛选器的情况下,例如:我希望优先使用找到的对象填充城市:
- A.
city.from="Paris" and city.population>1M
a.
- B.否则,只有对象
city.from="Paris"
- C.最后,如果以上都不是,
"city.detail"="new_city"
目前实施的是条件b和条件c.
避免:如果在cities
数组中,一个文档包含"city.population" < 1000000
,而另一个文档包含"city.from" = "Paris"
而不包含"city.population"
的信息,则新形成的数组将包含条件a)和b),而此时应该只存在条件b).
要优先使用指定的条件填充Cities数组,可以实现一系列条件($cond
)语句链.这将需要更新conditionalFinalCities
变量.
要实现上述行为(a>;b>;c),必须重构管道以顺序判断每个条件,只有在前一个条件没有产生任何结果时才应用下一个条件.
由于条件"a"和条件"b"的过滤逻辑不是互斥的(即,如果cities
数组中的一个文档匹配条件"b"("city.from" = "Paris"
)但缺少"city.population"
字段,而另一个文档满足条件"a"("city.from" = "Paris" and "city.population" > 1000000
),则两者都将被包括在最终数组中),这将违反仅具有最高优先级的条件应占优的约束.
解决这个问题的一种方法是在聚合管道中使用其他阶段来隔离严格满足条件"a"的文档,而忽略那些满足条件"b"但不满足"a"的文档.
package com.example.countrysearch.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.*;
import org.springframework.stereotype.Service;
import static org.springframework.data.mongodb.core.aggregation.Aggregation.project;
import com.example.countrysearch.model.Country;
import java.util.List;
@Service
public class CountryService {
@Autowired
private MongoTemplate mongoTemplate;
public List<Country> findCountriesByCity(String searchTerm) {
// Filtering for condition "a"
AggregationOperation filterForA = project("id", "name", "cities")
.and(ArrayOperators.Filter.filter("cities")
.as("city")
.by(BooleanOperators.And.and(
ComparisonOperators.Eq.valueOf("city.from").equalToValue(searchTerm),
ComparisonOperators.Gt.valueOf("city.population").greaterThanValue(1000000)
)))
.as("filteredCitiesForA");
// Conditional logic to isolate condition "a"
AggregationExpression conditionalForA = ConditionalOperators.when(
ComparisonOperators.Eq.valueOf(ArrayOperators.Size.lengthOfArray("filteredCitiesForA")).equalToValue(0)
)
.then(
ConditionalOperators.when(
ComparisonOperators.Eq.valueOf(ArrayOperators.Size.lengthOfArray(
ArrayOperators.Filter.filter("cities")
.as("city")
.by(ComparisonOperators.Eq.valueOf("city.from").equalToValue(searchTerm))
)).equalToValue(0)
)
.then(
ArrayOperators.Filter.filter("cities")
.as("city")
.by(ComparisonOperators.Eq.valueOf("city.detail").equalToValue("new_city"))
)
.otherwise(
ArrayOperators.Filter.filter("cities")
.as("city")
.by(ComparisonOperators.Eq.valueOf("city.from").equalToValue(searchTerm))
)
)
.otherwise("$filteredCitiesForA");
// Project 'finalCities'
AggregationOperation projectFinal = project("id", "name")
.and(conditionalForA).as("finalCities");
// Build the aggregation pipeline
Aggregation aggregation = Aggregation.newAggregation(
filterForA,
projectFinal
);
// Execute the aggregation
AggregationResults<Country> result = mongoTemplate.aggregate(aggregation, "country", Country.class);
return result.getMappedResults();
}
}
其使用附加级filterForA
来预过滤条件"a",并将结果存储在filteredCitiesForA
中.随后的条件逻辑(conditionalForA
)判断filteredCitiesForA
是否为空.如果为空,则继续判断条件"b"和"c".否则,它使用条件"a"的过滤结果.这确保了最终数组(finalCities
)包含满足最高优先级条件的文档.
Remember to include this new conditionalForA
expression in your pipeline to replace the old one.
I have done so in CountryService.java
, which compiles just fine.