如何使用 Spring Data Elasticsearch 在条件查询中实现“或”条件

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

我正在使用 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() 方法的方式可能存在缺陷。谁能帮助我纠正这个实现以满足预期的行为?

非常感谢!

spring-boot elasticsearch spring-data-elasticsearch criteriaquery
1个回答
0
投票

你能尝试一下这个吗?

scopesQuery.or(Criteria(ActionEntity::scopes.name).contains(scope.lowercase()))

而不是

scopesQuery.or(scopesQuery.contains(scope.lowercase()))

您当前正在为每个范围创建多个 Criteria 实例,并使用逻辑 AND 条件添加它们。相反,您应该为“范围”字段创建一个 Criteria 实例,并使用逻辑 OR 条件将它们组合起来。因此,对于提供的集合中的每个范围,您可以添加一个条件,使用逻辑或条件检查“范围”字段是否包含该范围。

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