1

bool複数のフィールドと照合するために検索を使用しています。フィールドはインデックス時に複数のフィルターを使用して分析されましたが、主にedge_ngram.

私が抱えている問題は、得点が宙に浮いているように見えることです. 私の検索が最初のフィールドのsavvas1 つと一致することを期待しますが、それらはずっと後でスコアリングされます。たとえば、スコアの順にリターンを検索すると、次のようになります。first_nameSavvassavvas

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検索すると、その文字を含むすべてのメールが一致します。782782

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 つでより正確な一致を優先します。

ただし、少なくとも正確なトークンのみに一致する場合は幸いです。termngram を適用せずに、検索文字列自体をトークン化する必要があるため、検索を使用できません。

私の要件を要約するには:

  • 任意の 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
                }
            ]
        }
    }
}
4

1 に答える 1

1

おそらく、検索文字列でオートコンプリート、つまり名前アナライザーを使用したくないでしょう。インデックス作成中のみ、つまりマッピングは次のようにする必要があります。

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

また、マルチマッチでlast_nameよりもfirst_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
                }
            ]
        }
    }
}
于 2015-07-24T11:35:30.317 に答える