我正在使用bool
搜索来匹配多个字段。这些字段已在索引时使用多个过滤器进行了分析,但主要使用edge_ngram
.
我遇到的问题是得分似乎悬而未决。我希望我的搜索与我的第一个领域相savvas
匹配,但它们的得分要晚得多。例如,按分数顺序搜索返回:first_name
Savvas
savvas
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
我已将字段中搜索词的边缘 n-gram 以外的字符替换为-
并修改了电子邮件的长度以保护身份。
ssssssssssssssss
尽管在我的数据中不存在,但事实上搜索会返回其中s
字符数最多的项目。我不希望发生的事情,因为我没有对我的搜索进行任何手动 ngram。
当我尝试搜索电话号码时也会出现此问题,78
当搜索782
具有782
精确 ngram 的电话号码时,我匹配包含字符的任何电子邮件。
似乎elasticsearch也在我的搜索查询上执行ngram,而不仅仅是字段并将两者进行比较,并且以某种方式支持更短的匹配到更大的匹配。
这是我的查询:
{
'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
}
}
以及与之配套的索引设置(为冗长而道歉,但我不想排除任何可能重要的内容):
{
"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"
}
}
}
}
}
我已经使用 Kopf 插件的分析器测试了索引设置,它似乎创建了正确的令牌。
理想情况下,我只会与我的索引创建的标记完全匹配,并在我的一个 bool 的 should 查询中优先考虑更精确的匹配,而不是优先考虑多个 bool should 匹配。
但是,如果至少它只匹配确切的标记,我会很高兴。我不能使用term
搜索,因为我的搜索字符串本身需要被标记化,而无需对其应用任何 ngram。
总结一下我的要求:
- 在任何单个字段中通过最大可能匹配得分。
- 然后按任何单个字段中可能匹配的最低偏移量进行评分。
- 然后按匹配的字段数评分,优先考虑较低的偏移匹配
- - 更新: - -
我使用 a 得到了更好的结果dis_max
,它似乎成功地匹配了多个 ngram 匹配的更大 ngram 匹配,但phone
仍然难以查询的字段除外。这是新的查询:
{
'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
}
]
}
}
}