bool
複数のフィールドと照合するために検索を使用しています。フィールドはインデックス時に複数のフィルターを使用して分析されましたが、主にedge_ngram
.
私が抱えている問題は、得点が宙に浮いているように見えることです. 私の検索が最初のフィールドのsavvas
1 つと一致することを期待しますが、それらはずっと後でスコアリングされます。たとえば、スコアの順にリターンを検索すると、次のようになります。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 以外の文字を に置き換え-
、電子メールの長さを変更して ID を保護しました。
ssssssssssssssss
実際、データに存在しないのに検索すると、文字数が最も多いアイテムが返さs
れます。検索に対して手動の ngram を実行していないため、これは予期しないことです。
この問題は、電話番号を検索しようとしたときにも発生します。正確な ngramを持つ電話番号を78
検索すると、その文字を含むすべてのメールが一致します。782
782
Elasticsearch は、フィールドだけでなく、私の検索クエリでも ngrams を実行し、2 つを比較して、どういうわけか短い一致をより大きなものに優先しているようです。
これが私のクエリです:
{
'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 クエリの 1 つでより正確な一致を優先します。
ただし、少なくとも正確なトークンのみに一致する場合は幸いです。term
ngram を適用せずに、検索文字列自体をトークン化する必要があるため、検索を使用できません。
私の要件を要約するには:
- 任意の 1 つのフィールドで可能な限り最大の試合で最初に得点します。
- 次に、任意の 1 つのフィールドで可能な一致の最小オフセットによってスコアを付けます。
- 次に、一致したフィールドの数でスコアを付け、より低いオフセットの一致を優先します
- - アップデート: - -
を使用すると、はるかに優れた結果が得られます。まだクエリが難しいフィールドを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
}
]
}
}
}