1

現在、Symfony2 でFOSElasticaBundleを使用していますが、最長のプレフィックスに一致する検索を作成するのに苦労しています。

これを使用してオートコンプリートのような検索を実行するためにインターネット上にある 100 の例を認識しています。しかし、私の問題は少し異なります。

オートコンプリート タイプの検索では、データベースは最長の英数字文字列 (文字数) を保持し、ユーザーは最短部分のみを提供します。たとえば、ユーザーが「jho」と入力すると、Elasticsearch は「Jhon、Jhonny、Jhonas」を簡単に提供できるとします。

私の問題は逆です。最も長い英数字の文字列を提供したいのですが、Elasticsearch にデータベース内の最大の一致を提供してもらいたいのです。

例: "123456789" を指定すると、データベースは [12,123,14,156,16,7,1234,1,67,8,9,123456,0] を持つことができます。ユーザーが提供した番号は「123456」です。

私は Elasticsearch を使い始めたばかりなので、作業設定などにあまり詳しくありません。

不明な情報や不足している情報がある場合はお知らせください。詳細をお知らせします。

更新 1 (ヴァルの 2 回目の更新を使用)

インデックス: 1800 以上のインデックスをダウンロード

Settings:

curl -XPUT localhost:9200/tests -d '{
  "settings": {
    "analysis": {
      "analyzer": {
        "edge_ngram_analyzer": {
          "tokenizer": "edge_ngram_tokenizer",
          "filter": [ "lowercase" ]
        }
      },
      "tokenizer": {
        "edge_ngram_tokenizer": {
          "type": "edgeNGram",
          "min_gram": "2",
          "max_gram": "25"
        }
      }
    }
  },
  "mappings": {
    "test": {
      "properties": {
        "my_string": {
          "type": "string",
          "fields": {
            "prefix": {
              "type": "string",
              "analyzer": "edge_ngram_analyzer"
            }
          }
        }
      }
    }
  }
}'


Query:

curl -XPOST localhost:9200/tests/test/_search?pretty=true -d '{
  "size": 1,
  "sort": {
    "_script": {
      "script": "doc.my_string.value.length()",
      "type": "number",
      "order": "desc"
    },
    "_score": "desc" 
  },
  "query": {
    "filtered": {
      "query": {
        "match": {
          "my_string.prefix": "8092232423"
        }
      },
      "filter": {
        "script": {
          "script": "doc.my_string.value.length() <= maxlength",
          "params": {
            "maxlength": 10
          }
        }
      }
    }
  }
}'

With this configuration the query returns the following results:

  {
  "took" : 61,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "failed" : 0
  },
  "hits" : {
    "total" : 1754,
    "max_score" : null,
    "hits" : [ {
      "_index" : "tests",
      "_type" : "test",
      "_id" : "AU8LqQo4FbTZPxBtq3-Q",
      "_score" : 0.13441172,
      "_source":{"my_string":"80928870"},
      "sort" : [ 8.0, 0.13441172 ]
    } ]
  }
}

ボーナス質問

その検索に数値の配列を提供し、毎回クエリを実行することなく効率的な方法でそれぞれに一致するプレフィックスを取得したいと思います

4

1 に答える 1

1

これが私の見解です。

基本的に、私たちがしなければならないことは、トークナイザー(my_string後述) を使用してインデックス作成時にフィールド (後述) を細かく分割することです。このようにして、のような文字列が、、、、、にトークン化され、すべてのトークンがインデックス化されて検索可能になります。edgeNGramedge_ngram_tokenizer12345678912123123412345123456123456712345678123456789

testsそれでは、インデックス、analyzer というカスタム アナライザー、edge_ngram_analyzerおよびtestという単一の文字列フィールドを含むマッピングを作成しましょうmy_string。このフィールドは、トークン化されたすべてのプレフィックスを含むサブmy_stringフィールドを宣言する複数フィールドであることに注意してください。prefixes

curl -XPUT localhost:9200/tests -d '{
  "settings": {
    "analysis": {
      "analyzer": {
        "edge_ngram_analyzer": {
          "tokenizer": "edge_ngram_tokenizer",
          "filter": [ "lowercase" ]
        }
      },
      "tokenizer": {
        "edge_ngram_tokenizer": {
          "type": "edgeNGram",
          "min_gram": "2",
          "max_gram": "25"
        }
      }
    }
  },
  "mappings": {
    "test": {
      "properties": {
        "my_string": {
          "type": "string",
          "fields": {
            "prefixes": {
              "type": "string",
              "index_analyzer": "edge_ngram_analyzer"
            }
          }
        }
      }
    }
  }
}

次に、 APItestを使用していくつかのドキュメントにインデックスを付けましょう。_bulk

curl -XPOST localhost:9200/tests/test/_bulk -d '
{"index":{}}
{"my_string":"12"}
{"index":{}}
{"my_string":"1234"}
{"index":{}}
{"my_string":"1234567890"}
{"index":{}}
{"my_string":"abcd"}
{"index":{}}
{"my_string":"abcdefgh"}
{"index":{}}
{"my_string":"123456789abcd"}
{"index":{}}
{"my_string":"abcd123456789"}
'

特に難しいと感じたのは、マッチング結果が入力文字列よりも長いか短いかのどちらかになる可能性があることです。これを実現するには、2 つのクエリを組み合わせる必要があります。1 つは短い一致を探し、もう 1 つは長い一致を探します。したがって、matchクエリは入力に一致する短い「プレフィックス」を持つドキュメントを検索し、query_stringクエリは (入力文字列にedge_ngram_analyzer適用された!) 入力文字列より長い「プレフィックス」を検索します。a で囲まれ、bool/should文字列の長さが減少する (つまり、最も長いものが最初になる) ように並べ替えると、うまくいきます。

いくつかのクエリを実行して、何が展開するか見てみましょう。

このクエリは、「123456789」、つまり「123456789abcd」に最も長く一致する 1 つのドキュメントを返します。この場合、結果は入力より長くなります。

curl -XPOST localhost:9200/tests/test/_search -d '{
  "size": 1,
  "sort": {
    "_script": {
      "script": "doc.my_string.value.length()",
      "type": "number",
      "order": "desc"
    }
  },
  "query": {
    "bool": {
      "should": [
        {
          "match": {
            "my_string.prefixes": "123456789"
          }
        },
        {
          "query_string": {
            "query": "123456789",
            "default_field": "my_string.prefixes",
            "analyzer": "edge_ngram_analyzer"
          }
        }
      ]
    }
  }
}'

2 番目のクエリは、「123456789abcdef」、つまり「123456789abcd」に最も長く一致する 1 つのドキュメントを返します。この場合、結果は入力よりも短くなります。

curl -XPOST localhost:9200/tests/test/_search -d '{
  "size": 1,
  "sort": {
    "_script": {
      "script": "doc.my_string.value.length()",
      "type": "number",
      "order": "desc"
    }
  },
  "query": {
    "bool": {
      "should": [
        {
          "match": {
            "my_string.prefixes": "123456789abcdef"
          }
        },
        {
          "query_string": {
            "query": "123456789abcdef",
            "default_field": "my_string.prefixes",
            "analyzer": "edge_ngram_analyzer"
          }
        }
      ]
    }
  }
}'

それがそれをカバーすることを願っています。そうでない場合はお知らせください。

_msearchおまけの質問ですが、 APIを使用してすべてのクエリを一度に送信することをお勧めします。

更新elasticsearch.yml: 最後に、次を使用して、ファイルでスクリプトが有効になっていることを確認します。

 # if you have ES <1.6
 script.disable_dynamic: false

 # if you have ES >=1.6
 script.inline: on

UPDATE 2ユースケースが他の人のニーズに合う可能性があるため、上記を残しています。ここで、「短い」接頭辞のみが必要なので (理にかなっています!!)、マッピングとクエリを少し変更する必要があります。

マッピングは次のようになります。

{
  "settings": {
    "analysis": {
      "analyzer": {
        "edge_ngram_analyzer": {
          "tokenizer": "edge_ngram_tokenizer",
          "filter": [
            "lowercase"
          ]
        }
      },
      "tokenizer": {
        "edge_ngram_tokenizer": {
          "type": "edgeNGram",
          "min_gram": "2",
          "max_gram": "25"
        }
      }
    }
  },
  "mappings": {
    "test": {
      "properties": {
        "my_string": {
          "type": "string",
          "fields": {
            "prefixes": {
              "type": "string",
              "analyzer": "edge_ngram_analyzer"  <--- only change
            }
          }
        }
      }
    }
  }
}

また、クエリは少し異なりますが、常に最長のプレフィックスのみを返しますが、入力文字列に対してより短いか等しい長さです。ぜひお試しください。すべてが適切に設定されていることを確認するために、データのインデックスを再作成することをお勧めします。

{
  "size": 1,
  "sort": {
    "_script": {
      "script": "doc.my_string.value.length()",
      "type": "number",
      "order": "desc"
    },
    "_score": "desc"           <----- also add this line
  },
  "query": {
    "filtered": {
      "query": {
        "match": {
          "my_string.prefixes": "123"  <--- input string
        }
      },
      "filter": {
        "script": {
          "script": "doc.my_string.value.length() <= maxlength",
          "params": {
            "maxlength": 3      <---- this needs to be set to the length of the input string
          }
        }
      }
    }
  }
}
于 2015-08-07T05:55:08.150 に答える