Elasticsearch看似随机得分和匹配

时间:2015-07-24 09:16:10

标签: elasticsearch scoring booleanquery

我使用bool搜索来匹配多个字段。这些字段已在索引时使用多个过滤器进行分析,但主要使用edge_ngram

我遇到的问题是得分似乎在空中。我希望我的搜索savvas首先匹配first_name Savvassavvas字段之一,但它们的得分要晚得多。例如,按照得分的顺序搜索First name | Last name | Email ___________|_________________|________________________ ------ | Sav--- | ---@sa-------------.com -----s | Sa---- | sa----------s@-----.com Sa---- | ---- | sa---------@-------.com Sa---- | -------- | sa-------@---------.com sa- | ----- | sa----------@------.com Sa-- | ----s-----s | sa------s-----s@---.com Sa---- | ----------- | sa-----@-----------.com Savvas | -------s | ----------@--------.com Savvas | -------s | --------@----------.com Sa- | ---s----S------ | sa------s-----@----.com

-

我已在ssssssssssssssss字段中替换了搜索字词边缘n-gram以外的字符,并修改了电子邮件的长度以保护身份。

实际上搜索s虽然我的数据中不存在,但会返回其中包含78个字符数最多的项目。我不希望发生的事情,因为我没有对我的搜索进行任何手动ngram。

当我尝试搜索电话号码时,系统也会出现此问题,在搜索782的电话号码时,我会搜索包含字符782的所有电子邮件,而这些电话号码的确属于{ 'bool': { 'should': [ // Any one of these matches will return a result { 'match': { 'phone': { 'query': $searchString, 'fuzziness': '0', 'boost': 3 // If phone matches give it precedence } } }, { 'match': { 'email': { 'query': $searchString, 'fuzziness': '0' } } }, { 'multi_match': { 'query': $searchString, 'type': 'cross_fields', // Match if any term is in any of the fields 'fields': ['name.first_name', 'name.last_name'], 'fuzziness': '0' } } ], 'minimum_should_match': 1 } } ngrams

似乎elasticsearch也在我的搜索查询中执行ngrams,而不仅仅是字段,并且比较两者并且不知何故有利于更短的匹配。

这是我的问题:

{
    "settings":{
        "analysis":{
            "char_filter":{
                "trim":{
                    "type":"pattern_replace",
                    "pattern":"^\\s*(.*)\\s*$",
                    "replacement":"$1"
                },
                "tel_strip_chars":{
                    "type":"pattern_replace",
                    "pattern":"^(\\(\\d+\\))|^(\\+)|\\D",
                    "replacement":"$1$2"
                },
                "tel_uk_exit_coded":{
                    "type":"pattern_replace",
                    "pattern":"^00(\\d+)",
                    "replacement":"+$1"
                },
                "tel_parenthesized_country_code":{
                    "type":"pattern_replace",
                    "pattern":"^\\((\\d+)\\)(\\d+)",
                    "replacement":"+$1$2"
                }
            },
            "tokenizer":{
                "intl_tel_country_code": {
                    "type":"pattern",
                    "pattern":"\\+(9[976]\\d|8[987530]\\d|6[987]\\d|5[90]\\d|42\\d|3[875]\\d|2[98654321]\\d|9[8543210]|8[6421]|6[6543210]|5[87654321]|4[987654310]|3[9643210]|2[70]|7|1)(\\d{1,14})$",
                    "group":0
                }
            },
            "filter":{
                "autocomplete":{
                    "type":"edge_ngram",
                    "min_gram":1,
                    "max_gram":50
                },
                "autocomplete_tel":{
                    "type":"ngram",
                    "min_gram":3,
                    "max_gram":20
                },
                "email":{
                    "type":"pattern_capture",
                    "preserve_original":1,
                    "patterns":[
                        "([^@]+)",
                        "(\\p{L}+)",
                        "(\\d+)",
                        "@(.+)",
                        "([^-@]+)"
                    ]
                }
            },
            "analyzer":{
                "name":{
                    "type":"custom",
                    "tokenizer":"standard",
                    "filter":[
                        "trim",
                        "lowercase",
                        "asciifolding",
                        "autocomplete"
                    ]
                },
                "email":{
                    "type":"custom",
                    "tokenizer":"uax_url_email",
                    "filter":[
                        "trim",
                        "lowercase",
                        "email",
                        "unique",
                        "autocomplete"
                    ]
                },
                "phone":{
                    "type":"custom",
                    "tokenizer":"intl_tel_country_code",
                    "char_filter":[
                        "trim",
                        "tel_strip_chars",
                        "tel_uk_exit_coded",
                        "tel_parenthesized_country_code"
                    ],
                    "filter":[
                        "autocomplete_tel"
                    ]
                }
            }
        }
    },
    "mappings":{
        "person":{
            "properties":{
                "address":{
                    "properties":{
                        "country":{
                            "type":"string",
                            "index_name":"country"
                        }
                    }
                },
                "timezone":{
                    "type":"string"
                },
                "name":{
                    "properties":{
                        "first_name":{
                            "type":"string",
                            "analyzer":"name"
                        },
                        "last_name":{
                            "type":"string",
                            "analyzer":"name"
                        }
                    }
                },
                "email":{
                    "type":"string",
                    "analyzer":"email"
                },
                "phone":{
                    "type":"string",
                    "analyzer":"phone"
                },
                "id":{
                    "type":"string"
                }
            }
        }
    }
}

和它一起使用的索引设置(为冗长而道歉,但我不想排除任何可能重要的内容):

term

我已经使用Kopf插件的分析器测试了索引设置,它似乎可以创建正确的令牌。

理想情况下,我只会与我的索引创建的令牌完全匹配,并优先考虑我的一个bool应该查询中的更精确匹配,而不是优先考虑多个bool应该匹配。

但是如果至少它只与精确的令牌匹配,我会很高兴。我无法使用dis_max搜索,因为我的搜索字符串本身需要进行标记化,只是不对其应用任何ngram。

总结我的要求:

  • 在任何一个字段中以最大可能匹配率排名第一。
  • 然后按任意单个字段中可能匹配的最低偏移量进行评分。
  • 然后根据匹配的字段数得分,优先选择较低的偏移匹配

---更新:---

我使用phone得到了更好的结果,它似乎成功地匹配了多个ngram匹配的更多ngram匹配,除了仍然难以查询的{ 'dis_max': { 'tie_breaker': 0.0, 'boost': 1.5, 'queries': [ // Any one of these matches will return a result [ 'match': { 'phone': { 'query': $searchString, 'boost': 1.9 } } ], [ 'match': { 'email': { 'query': $searchString } } ], [ 'multi_match': { 'query': $searchString, 'type': 'cross_fields', // Match if any term is in any of the fields 'fields': ['name.first_name', 'name.last_name'], 'tie_breaker': 0.1, 'boost': 1.5 } ] } } } 字段。这是新查询:

function ThreadFunc(Data: Pointer): Cardinal; stdcall;

1 个答案:

答案 0 :(得分:1)

可能你不想在搜索字符串上使用自动填充,即名称分析器,只有在索引期间,映射应该是:

"first_name": {
    "type":"string",
    "index_analyzer":"name"
}

另外,要在多场比赛中对 first_name 高于 last_name 的比赛进行评分,您可以按如下方式提供字段级提升:

示例:last_name匹配与first_name相关的一半

{
    'dis_max': {
        'tie_breaker': 0.0,
        'boost': 1.5,
        'queries': [ // Any one of these matches will return a result
            [
                'match': {
                    'phone': {
                        'query': $searchString,
                        'boost': 1.9
                    }
                }
            ],
            [
                'match': {
                    'email': {
                        'query': $searchString
                    }
                }
            ],
            [
                'multi_match': {
                    'query': $searchString,
                    'type': 'cross_fields', // Match if any term is in any of the fields
                    'fields': ['name.first_name', 'name.last_name^0.5'],
                    'tie_breaker': 0.1,
                    'boost': 1.5
                }
            ]
        }
    }
}