问:我的目标是什么?
A:需要一种方法来跨多行搜索最佳匹配列。
问:可以详细解释一下吗?
A:假设我有以下数据:
身份证 | A键 | B键 | C键 | 瓦尔A | 瓦尔B | 瓦尔C |
---|---|---|---|---|---|---|
1 | abc | b* | c* | va0 | 空 | vc0 |
2 | 一个* | bcd | c* | 空 | vb1 | vc1 |
在我的情况下,我有一个评分函数可以计算最佳匹配值。对于给定的条件键:
Key A = abc
、Key B = bcd
和Key C = cde
,那么我需要查询最佳匹配值:
Val A = va0
、Val B = vb1
和Val C = vc1
(有点特别如下)。
作为结论,假设我们的得分函数是:
2*i
(i
是从1
开始的位置索引)1+i
使用
Key A = abc
、Key B = bcd
和 Key C = cde
,对于第 1 行 (ID=1
):
Key A
是完全匹配,Key B
是模式匹配,Key C
也是模式匹配,但较长索引较大的匹配优先,权重为weight1 = 2*1 + (1+2) + 1+3 = 9
。
使用相同的算法,对于第 2 行(
ID=2
),权重为 weight2 = (1+1) + 2(1+2) + 1+3 = 12
。
对于
Val A
,第2行为NULL,然后我们从第1行得到va0
,对于Val B
,第1行为NULL,然后我们从第2行得到vb1
。但是对于Val C
,因为weight2 > weight1
然后我们得到了vc1
。
这就是为什么我们得到
Val A = va0
、Val B = vb1
和 Val C = vc1
。
更进一步,对于整行来说,找到最高分行是很清楚的(对于给定的数据和查询条件,第 2 行是预期的),但是对于列级最高分匹配就会出现问题。
也许我们可以一一搜索列,但如果超过 100 列,这个解决方案就会失效,这是不可接受的。顺便说一句,任何版本的 ElasticSearch 都可以。我们正在使用最新的
8.12.2
用于测试目的。我们正处于 POC 阶段,任何解决方案都会适用,我们很高兴对此进行任何测试。
bulk query
API 可能可以工作,但我们的列可能约为 5k,它无法按预期工作。我重新整理你的数据:
NULL
值替换为新值。你可以编程wildcard
匹配。所以我用 startsWith
检查来代替它映射
PUT /key_matches
{
"mappings": {
"properties": {
"key": {
"type": "keyword"
},
"weight": {
"type": "integer"
},
"pattern_values": {
"type": "nested",
"properties": {
"id": {
"type": "integer"
},
"pattern": {
"type": "keyword"
},
"value": {
"type": "keyword"
},
"score": {
"type": "double"
}
}
}
}
}
}
文件
PUT /key_matches/_bulk
{"create":{"_id":1}}
{"key":"A","weight":1,"pattern_values":[{"id":1,"pattern":"abc","value":"va0","score":0},{"id":2,"pattern":"a","value":"va0","score":0}]}
{"create":{"_id":2}}
{"key":"B","weight":2,"pattern_values":[{"id":1,"pattern":"b","value":"vb1","score":0},{"id":2,"pattern":"bcd","value":"vb1","score":0}]}
{"create":{"_id":3}}
{"key":"C","weight":3,"pattern_values":[{"id":1,"pattern":"c","value":"vc0","score":0},{"id":2,"pattern":"c","value":"vc1","score":0}]}
update_by_query
计算每个嵌套 pattern_values
对象的分数。源值在脚本 params
中设置。您可以reindex
代替更新
POST /key_matches/_update_by_query
{
"query": {
"match_all": {
}
},
"script": {
"source": """
def patternValues = ctx._source.pattern_values;
List strings = params.strings;
// calculate score
String key = ctx._source.key;
String string = "";
for (str in strings) {
if (str.containsKey(key)) {
string = str[key];
}
}
int weigth = ctx._source.weight;
for (patternValue in patternValues) {
String pattern = patternValue.pattern;
if (string.equals(pattern)) {
patternValue.score = 2 * weigth;
} else if (string.startsWith(pattern)) {
patternValue.score = 1 + weigth;
} else {
patternValue.score = 0;
}
}
""",
"params": {
"strings": [{"A": "abc"}, {"B": "bcd"}, {"C": "cde"}]
}
}
}
聚合查询
GET /key_matches/_search?filter_path=aggregations
{
"aggs": {
"inside_pattern_values": {
"nested": {
"path": "pattern_values"
},
"aggs": {
"by_id": {
"terms": {
"field": "pattern_values.id"
},
"aggs": {
"total_score": {
"sum": {
"field": "pattern_values.score"
}
}
}
}
}
}
}
}
回应
{
"aggregations" : {
"inside_pattern_values" : {
"doc_count" : 6,
"by_id" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : 1,
"doc_count" : 3,
"total_score" : {
"value" : 9.0
}
},
{
"key" : 2,
"doc_count" : 3,
"total_score" : {
"value" : 10.0
}
}
]
}
}
}
}
id 2 的总分最高。您的计算错误。请检查一下
在以下查询中使用 id 2(
value
参数)
GET /key_matches/_search?filter_path=hits.hits.inner_hits.pattern_values.hits.hits._source
{
"query": {
"nested": {
"path": "pattern_values",
"query": {
"term": {
"pattern_values.id": {
"value": "2"
}
}
},
"inner_hits": {}
}
}
}
响应包含预期值
{
"hits" : {
"hits" : [
{
"inner_hits" : {
"pattern_values" : {
"hits" : {
"hits" : [
{
"_source" : {
"score" : 2,
"pattern" : "a",
"id" : 2,
"value" : "va0"
}
}
]
}
}
}
},
{
"inner_hits" : {
"pattern_values" : {
"hits" : {
"hits" : [
{
"_source" : {
"score" : 4,
"pattern" : "bcd",
"id" : 2,
"value" : "vb1"
}
}
]
}
}
}
},
{
"inner_hits" : {
"pattern_values" : {
"hits" : {
"hits" : [
{
"_source" : {
"score" : 4,
"pattern" : "c",
"id" : 2,
"value" : "vc1"
}
}
]
}
}
}
}
]
}
}