Elasticsearch 中通过数组元素进行匹配

Ale*_*lov 4 elasticsearch

我必须在 Elasticsearch 中构造一个相当重要的(就像现在看起来的那样)查询。假设我有几个实体,每个实体都有一个由字符串组成的数组元素:

1). ['A', 'B']
2). ['A', 'C']
3). ['A', 'E']
4). ['A']
Run Code Online (Sandbox Code Playgroud)

数组元素的映射如下(使用动态模板):

{
  "my_array_of_strings": {
    "path_match": "stringArray*",
    "mapping": {
      "type": "string",
      "index": "not_analyzed"
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

实体的 Json 表示如下所示:

{
  "stringArray": [
    "A",
    "B"
  ]
}
Run Code Online (Sandbox Code Playgroud)

然后我有用户输入:['A','B','C']。

我想要实现的是找到仅包含输入中指定元素的实体 - 预期结果是: ['A', 'B'], ['A', 'C'], ['A'] 但不是 [' A', 'E'](因为用户输入中不存在 'E')。

这个场景可以用Elasticsearch实现吗?

更新: 除了使用脚本的解决方案 - 应该可以很好地工作,但如果有许多匹配的记录,很可能会大大减慢查询速度 - 我还设计了另一种解决方案。下面我将尝试解释其主要思想,不涉及代码实现。

我没有提到的一个重要条件(这可能给其他用户提供了有价值的提示)是数组由枚举元素组成,即数组中此类元素的数量是有限的。这允许将此类数组展平为实体的单独字段。

假设有 5 个可能的值:“A”、“B”、“C”、“D”、“E”。这些值中的每一个都是一个布尔字段 - 如果为空则为 true(即数组版本将包含此元素),否则为 false。然后每个实体都可以重写如下:

1).
A = true
B = true
C = false
D = false
E = false

2).
A = true
B = false
C = true
D = false
E = false

3).
A = true
B = false
C = false
D = false
E = true

4).
A = true
B = false
C = false
D = false
E = false
Run Code Online (Sandbox Code Playgroud)

用户输入 ['A', 'B', 'C'] 我需要做的就是: a)获取所有可能的值 (['A', 'B', 'C', 'D', 'E']) 并从中减去用户输入 -> 结果将是 ['D', 'E']; b)查找每个结果元素为假的记录,即“D = false AND E = false”。

正如预期的那样,这将给出记录 1、2 和 4。我仍在尝试这种方法的代码实现,但到目前为止它看起来很有希望。它尚未经过测试,但我认为与在查询中使用脚本相比,这可能会执行得更快,并且对资源的需求更少。

为了进一步优化这一点,可能根本不提供“假”字段,并将先前的查询修改为“D = 不存在 AND E = 不存在”——结果应该是相同的。

Chi*_*h25 5

您可以通过脚本来实现这一点,这就是它的样子

{
  "query": {
    "filtered": {
      "filter": {
        "bool": {
          "must": [
            {
              "terms": {
                "name": [
                  "A",
                  "B",
                  "C"
                ]
              }
            },
            {
              "script": {
                "script": "if(user_input.containsAll(doc['name'].values)){return true;}",
                "params": {
                  "user_input": [
                    "A",
                    "B",
                    "C"
                  ]
                }
              }
            }
          ]
        }
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

groovy script是检查列表是否包含除 之外的任何内容,['A', 'B', 'C']如果包含则返回 false,因此它不会返回['A', 'E']。它只是检查子列表匹配。该脚本可能需要几秒钟。您需要启用dynamic scripting,并且语法可能有所不同ES 2.x,如果它不起作用,请告诉我。

编辑1

我只把这两个条件都放在里面了filter。首先,仅返回具有A、B 或 C 的文档,然后仅将脚本应用于这些文档,因此这会比前一个更快。有关过滤器订购的更多信息

希望这可以帮助!!