我正在使用 Spring Data Elasticsearch 在 Spring Boot 中开发一个查询构建器,并面临着在设计用于根据多个可能的“范围”过滤操作的方法中实现 or 条件的问题。下面是我的 QueryBuilder 类和 ActionQueryService 类的相关部分的完整代码:
科特林
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.query.Criteria;
import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
import java.time.ZonedDateTime;
class QueryBuilder private constructor() {
companion object {
private var criteria: Criteria = Criteria()
fun init(): Companion {
criteria = Criteria()
return this
}
fun addActorOidCriterion(actorOid: String?): Companion {
criteria = criteria.and(actorOid?.let { Criteria(ActionEntity::actorOid.name).`is`(it) })
return this
}
fun addActionTypeCriteria(actionTypes: Set<ActionType>?): Companion {
if (!actionTypes.isNullOrEmpty()) {
criteria = criteria.and(ActionEntity::actionType.name).`in`(actionTypes)
}
return this
}
//FIXME: This function doesn't work as expected, it should generate a query to search for any Actions where the field 'scopes' contains any of the scope in the list. Instead of that, it generates a query to search for any Actions where the field 'scopes' contains all the scopes in the list.
fun addScopesCriteria(scopes: Set<String>?): Companion {
if (!scopes.isNullOrEmpty()) {
val scopesQuery = Criteria()
scopes.forEach { scope ->
scopesQuery.or(scopesQuery.contains(scope.lowercase()))
}
criteria = criteria.and(scopesQuery)
}
return this
}
fun addCommitteesCriterion(committees: Set<String>?): Companion {
if (!committees.isNullOrEmpty()) {
criteria = criteria.and(Criteria(ActionEntity::committee.name).`in`(committees))
}
return this
}
fun addAcknowledgeCriterion(acknowledged: Boolean?): Companion {
acknowledged?.let {
criteria = criteria.and(Criteria(ActionEntity::acknowledged.name).`is`(acknowledged))
}
return this
}
fun addExpectedTargetDateBeforeCriterion(expectedTargetDateBefore: ZonedDateTime?): Companion {
expectedTargetDateBefore?.let {
criteria = criteria.and(Criteria(ActionEntity::expectedTargetDate.name).lessThan(it))
}
return this
}
fun addExpectedTargetDateAfterCriterion(expectedTargetDateAfter: ZonedDateTime?): Companion {
expectedTargetDateAfter?.let {
criteria = criteria.and(Criteria(ActionEntity::expectedTargetDate.name).greaterThan(it))
}
return this
}
fun build(pageable: Pageable): CriteriaQuery {
return CriteriaQuery(criteria).setPageable(pageable)
}
}
}
class ActionQueryService {
fun createCriteriaQuery(filterCriteria: ActionCriteria, pageable: Pageable): CriteriaQuery {
return ActionQueryBuilder
.init()
.addActorOidCriterion(filterCriteria.actorOid)
.addActionTypeCriteria(filterCriteria.actionTypes?.map { ActionType.valueOf(it) }?.toSet())
.addScopesCriteria(filterCriteria.scopes)
.addCommitteesCriterion(filterCriteria.committees)
.addAcknowledgeCriterion(filterCriteria.acknowledged)
.addExpectedTargetDateBeforeCriterion(filterCriteria.expectedTargetDateBefore)
.addExpectedTargetDateAfterCriterion(filterCriteria.expectedTargetDateAfter)
.build(pageable)
}
}
fun findByFilterOptions(
filterCriteria: ActionCriteria,
pageable: Pageable
): DocumentPage {
filterCriteria.actorOid = filterCriteria.actorOid ?: userProfileService.getByCurrentUser().id.toString()
val criteriaQuery = actionQueryService.createCriteriaQuery(filterCriteria, pageable)
val searchHits = elasticOperation.search(criteriaQuery, ActionEntity::class.java)
val streamable = searchHits.map { it.content }
return DocumentPage(map(streamable.toList()), searchHits.totalHits)
}
@Document(indexName = "actions")
data class Action(
@Id
var id: UUID,
@Field(type = FieldType.Keyword)
var actorOid: String,
@Field(type = FieldType.Text)
var scopes: Set<String>?,
@Field(type = FieldType.Keyword)
var group: String?,
@Field(type = FieldType.Date)
var expectedTargetDate: ZonedDateTime? = null,
@Field(type = FieldType.Keyword)
var actionType: ActionType,
@Field(type = FieldType.Boolean)
var acknowledged: Boolean = false,
)
问题: addScopesCriteria 函数应生成一个查询来搜索“范围”字段包含列表中任何范围的任何操作。但是,它会生成一个查询来搜索“范围”字段包含列表中所有范围的操作。
设置:
Spring Boot版本:3.1.5 Spring Data Elasticsearch 版本:5.1.5 预期行为: 该方法应在范围内应用 OR 条件,从而允许检索包含任何指定范围的操作。
实际行为: 它错误地应用了看似 AND 条件,要求所有指定的范围都存在。 它生成以下 json 查询
{
"from": 0,
"query": {
"bool": {
"must": [
{
"query_string": {
"default_operator": "and",
"fields": [
"actorOid"
],
"query": "xxxxx"
}
},
{
"bool": {
"must": [
{
"query_string": {
"analyze_wildcard": true,
"fields": [
"scopes"
],
"query": "*aaa*"
}
},
{
"query_string": {
"analyze_wildcard": true,
"fields": [
"scopes"
],
"query": "*bbb*"
}
}
]
}
}
]
}
},
"size": 20,
"sort": [
{
"expectedTargetDate": {
"mode": "min",
"order": "asc"
}
}
],
"track_scores": false,
"version": true
}
我想要“应该”的地方就有“必须”。 我怀疑我链接或使用 .or() 方法的方式可能存在缺陷。谁能帮助我纠正这个实现以满足预期的行为?
非常感谢!
你能尝试一下这个吗?
scopesQuery.or(Criteria(ActionEntity::scopes.name).contains(scope.lowercase()))
而不是
scopesQuery.or(scopesQuery.contains(scope.lowercase()))
您当前正在为每个范围创建多个 Criteria 实例,并使用逻辑 AND 条件添加它们。相反,您应该为“范围”字段创建一个 Criteria 实例,并使用逻辑 OR 条件将它们组合起来。因此,对于提供的集合中的每个范围,您可以添加一个条件,使用逻辑或条件检查“范围”字段是否包含该范围。