java - 如何创建文档子集并在 Elasticsearch 中对该子集执行查询?

标签 java elasticsearch elasticsearch-5

原因是我们有一个 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 的同一个实例中使用了 mustshould询问。本例中记录的行为如下:

should

If the bool query is in a query context and has a must or filter 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"
                }
              }
            }
          ]
        }
      }
    ]
  }
}

这里我们用 filterquery_restricting_set_of_docs 包装到另一个 bool 中,从而实现过滤部分的过滤器上下文。

如果您可以控制索引并且您想要限制索引的几个不同子集,则可以使用 Filtered Aliases ,这基本上会将指定的过滤器添加到针对该别名执行的所有查询中。

希望有帮助!

关于java - 如何创建文档子集并在 Elasticsearch 中对该子集执行查询?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48372929/

相关文章:

Java Sprite 掉落?

elasticsearch - 如何在elasticsearch的uri请求搜索中找到日期范围

java - 组合与聚合在代码上有何不同?

Java:带有长类型参数的 String.substring()

java - 在 Spark 结构化流中使用 Kafka 接收器时是否必须设置检查点?

用于对文档进行分类的 Elasticsearch 插件

elasticsearch - 使用 Elasticsearch 计算文档数量

apache-spark - 从spark写入elasticsearch非常慢

c# - ElasticSearch 查询仅当所有字段都匹配我的过滤器时才检索结果?

elasticsearch - ElasticSearch获取字段,即使它们为空或为空