无法在 ElasticSearch 8.12.2 中跨行查询多个最佳匹配列

问题描述 投票:0回答:1

问:我的目标是什么?

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
  • 当值为NULL时,不匹配

使用

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 阶段,任何解决方案都会适用,我们很高兴对此进行任何测试。

  1. 我们尝试索引整行并使用自定义评分函数查询得分最高的行,它有效并且解决方案很容易知道。
  2. 我们阅读了手册并知道
    bulk query
    API 可能可以工作,但我们的列可能约为 5k,它无法按预期工作。
elasticsearch
1个回答
0
投票

简介

我重新整理你的数据:

  1. 现在文档是一个包含键和值的列
  2. 我手动将
    NULL
    值替换为新值。你可以编程
  3. 我无法实现
    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}]}

通过#1。分数计算

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"}]
        }
    }
}

通过#2。总分计算

聚合查询

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 的总分最高。您的计算错误。请检查一下

通过#3。值提取

在以下查询中使用 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"
                                    }
                                }
                            ]
                        }
                    }
                }
            }
        ]
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.