原因是我们有一个 API,可以从客户端接收查询参数并构建 Elasticsearch 查询。然而,根据用户的类型(无论是财务顾问还是投资者等),我们必须应用更多条件来限制搜索。不幸的是,我们无法对索引的结构进行任何更改(即添加额外的列),这是因为索引不是由我们管理的,并且除了可配置的列名称之外,我们的 API 没有有关索引的信息。
所以这是一个例子。收到基于“investorDateOfBirth
”和“financialAdviserId
”的搜索请求,并且由于搜索来自顾问,因此我们以编程方式添加此条件:
financialAdviserId must be '123' (the id of the current user)
因此最终查询变为:
{
"bool" : {
"must" : [
{
"term" : {
"financialAdviserId" : {
"value" : "123",
"boost" : 1.0
}
}
}
],
"should" : [
{
"term" : {
"investorDateOfBirth" : {
"value" : "1987-11-12",
"boost" : 1.0
}
}
},
{
"term" : {
"financialAdviserId" : {
"value" : "123",
"boost" : 1.0
}
}
}
],
"disable_coord" : false,
"adjust_pure_negative" : true,
"boost" : 1.0
}
}
如您所见,有 2 个“financialAdviserId”,一个是根据请求查询参数以编程方式构建的,另一个(“必须”)是根据当前用户添加的,但如您所知,这将返回那些具有指定的 investorDateOfBirth
以及顾问 id 为 123 的所有其他项目(包括那些没有相同出生日期的项目)
假设索引中有 3 条记录:
| investorDateOfBirth | financialAdviserId | investorId |
| "1987-11-12" | 123 | 111 |
| "1900-11-12" | 123 | 222 |
| "1900-11-12" | 123 | 333 |
对于上面的查询,结果是全部 3 行,这不是我们想要的结果,但是,对于以下查询,它返回仅第一行,这是期望的:
{
"bool" : {
"must" : [
{
"term" : {
"financialAdviserId" : {
"value" : "123",
"boost" : 1.0
}
}
}
],
"should" : [
{
"term" : {
"investorDateOfBirth" : {
"value" : "1987-11-12",
"boost" : 1.0
}
}
}
],
"disable_coord" : false,
"adjust_pure_negative" : true,
"boost" : 1.0
}
}
如何解决这个问题?我们如何更改第一个查询以获得与第二个查询(返回第一行)相同的结果。
只是想让您知道,我们无法使 financialAdviserId
不可搜索,因为还有其他实体可以搜索这些列?有没有办法创建一个子集(在我们的例子中,financialAdviserId 为 123 的子集),然后针对该子集执行客户端请求的查询?
我们在 Java 8
中使用 Elasticsearch v5.5.3
最佳答案
你就快到了。为了获得预期的行为,您可以将一个 bool
嵌套到另一个中:
{
"bool": {
"must": [
{
"term": {
"financialAdviserId": {
"value": "123"
}
}
},
{
"bool": {
"should": [
{
"term": {
"investorDateOfBirth": {
"value": "1987-11-12"
}
}
},
{
"term": {
"financialAdviserId": {
"value": "123"
}
}
}
]
}
}
]
}
(我删除了 boost
和其他细节以使想法更加清晰。)
为什么问题中的第一个查询不起作用
现在让我解释为什么初始查询不起作用。
您在 bool
的同一个实例中使用了 must
和 should
询问。本例中记录的行为如下:
should
If the
bool
query is in a query context and has amust
orfilter
clause then a document will match the bool query even if none of the should queries match.
(这也是为什么使用 Federico 中的 filter
的建议无法解决问题的原因。)
所以实际上您应用的查询具有以下逻辑含义:
query_restricting_set_of_docs AND (user_query or True)
而您正在寻找这个:
query_restricting_set_of_docs AND user_query
在您的情况下user_query
看起来像这样:
query_restricting_set_of_docs OR some_other_query
这给我们带来了最终的表达式:
query_restricting_set_of_docs AND (
query_restricting_set_of_docs OR some_other_query
)
转换为 ES bool
查询,如下所示:
{
"bool": {
"must": [
{
...query_restricting_set_of_docs
},
{
"bool": {
"should": [
{
...query_restricting_set_of_docs
},
{
...other_query
}
]
}
}
]
}
}
注意 query and filter context
过滤器和查询上下文之间的主要区别是:
- 查询上下文计算相关性分数,并且结果不会缓存
- 过滤器上下文不计算分数,但结果会被缓存
缓存部分将使搜索速度更快,但如果没有相关性分数,您将无法首先显示更多相关文档。在您的情况下,您可能希望将 query_restricting_set_of_docs
放入过滤器上下文中。
为此,您可以使用以下查询:
{
"bool": {
"must": [
{
"bool": {
"filter": [
{
"term": {
"financialAdviserId": {
"value": "123"
}
}
}
]
}
},
{
"bool": {
"should": [
{
"term": {
"investorDateOfBirth": {
"value": "1987-11-12"
}
}
},
{
"term": {
"financialAdviserId": {
"value": "123"
}
}
}
]
}
}
]
}
}
这里我们用 filter
将 query_restricting_set_of_docs
包装到另一个 bool
中,从而实现过滤部分的过滤器上下文。
如果您可以控制索引并且您想要限制索引的几个不同子集,则可以使用 Filtered Aliases ,这基本上会将指定的过滤器
添加到针对该别名执行的所有查询中。
希望有帮助!
关于java - 如何创建文档子集并在 Elasticsearch 中对该子集执行查询?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48372929/