我必须在 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 = 不存在”——结果应该是相同的。
您可以通过脚本来实现这一点,这就是它的样子
{
"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 的文档,然后仅将脚本应用于这些文档,因此这会比前一个更快。有关过滤器订购的更多信息
希望这可以帮助!!
| 归档时间: |
|
| 查看次数: |
2869 次 |
| 最近记录: |