ElasticSearch 5.xは、Suggester API( Documentation )にいくつかの(互換性のない)変更を導入しました。最も注目すべき変更は次のとおりです。
補完候補はドキュメント指向です
提案は、それらが属するドキュメントを認識しています。現在、関連ドキュメント(
_source
)が補完候補の一部として返されます。
つまり、すべての完了クエリは、一致したwordsではなく、一致したすべてのdocumentsを返します。そしてここに問題があります-オートコンプリートされた単語が複数のドキュメントで発生した場合の重複。
この簡単なマッピングがあるとしましょう:
{
"my-index": {
"mappings": {
"users": {
"properties": {
"firstName": {
"type": "text"
},
"lastName": {
"type": "text"
},
"suggest": {
"type": "completion",
"analyzer": "simple"
}
}
}
}
}
}
いくつかのテストドキュメント:
{
"_index": "my-index",
"_type": "users",
"_id": "1",
"_source": {
"firstName": "John",
"lastName": "Doe",
"suggest": [
{
"input": [
"John",
"Doe"
]
}
]
}
},
{
"_index": "my-index",
"_type": "users",
"_id": "2",
"_source": {
"firstName": "John",
"lastName": "Smith",
"suggest": [
{
"input": [
"John",
"Smith"
]
}
]
}
}
そして、本によるクエリ:
POST /my-index/_suggest?pretty
{
"my-suggest" : {
"text" : "joh",
"completion" : {
"field" : "suggest"
}
}
}
次の結果が得られます。
{
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"my-suggest": [
{
"text": "joh",
"offset": 0,
"length": 3,
"options": [
{
"text": "John",
"_index": "my-index",
"_type": "users",
"_id": "1",
"_score": 1,
"_source": {
"firstName": "John",
"lastName": "Doe",
"suggest": [
{
"input": [
"John",
"Doe"
]
}
]
}
},
{
"text": "John",
"_index": "my-index",
"_type": "users",
"_id": "2",
"_score": 1,
"_source": {
"firstName": "John",
"lastName": "Smith",
"suggest": [
{
"input": [
"John",
"Smith"
]
}
]
}
}
]
}
]
}
つまり、テキスト "joh"の補完候補として、2つのdocumentsが返されました。両方のJohnとtext
プロパティの値が同じでした。
ただし、(1)Wordを1つ受け取りたい。このような単純なもの:
{
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"my-suggest": [
{
"text": "joh",
"offset": 0,
"length": 3,
"options": [
"John"
]
}
]
}
質問:Wordベースの補完候補を実装する方法。この時点では必要ないため、ドキュメント関連のデータを返す必要はありません。
「Completion Suggester」は私のシナリオにも適していますか?それとも、まったく異なるアプローチを使用する必要がありますか?
[〜#〜] edit [〜#〜]:多くの人が指摘したように、補完のみのインデックスを追加することは、実行可能なソリューションになります。ただし、このアプローチには複数の問題が見られます。
"John", "Doe", "David", "Smith"
。 "John D"
を照会すると、不完全なWordの結果は"Doe"
ではなく"Doe", "David"
になります。2番目のポイントを克服するには、単一の単語にインデックスを付けるだけでは不十分です。自動補完する後続の単語を適切に絞り込むために、すべての単語をドキュメントにマップする必要があるためです。これにより、実際には元のインデックスのクエリと同じ問題が発生します。したがって、追加のインデックスはもう意味がありません。
コメントで示唆したように、重複したドキュメントを取得せずにこれを達成する別の方法は、フィールドのngramを含むfirstname
フィールドのサブフィールドを作成することです。まず、次のようにマッピングを定義します。
PUT my-index
{
"settings": {
"analysis": {
"analyzer": {
"completion_analyzer": {
"type": "custom",
"filter": [
"lowercase",
"completion_filter"
],
"tokenizer": "keyword"
}
},
"filter": {
"completion_filter": {
"type": "Edge_ngram",
"min_gram": 1,
"max_gram": 24
}
}
}
},
"mappings": {
"users": {
"properties": {
"autocomplete": {
"type": "text",
"fields": {
"raw": {
"type": "keyword"
},
"completion": {
"type": "text",
"analyzer": "completion_analyzer",
"search_analyzer": "standard"
}
}
},
"firstName": {
"type": "text"
},
"lastName": {
"type": "text"
}
}
}
}
}
次に、いくつかのドキュメントにインデックスを付けます。
POST my-index/users/_bulk
{"index":{}}
{ "firstName": "John", "lastName": "Doe", "autocomplete": "John Doe"}
{"index":{}}
{ "firstName": "John", "lastName": "Deere", "autocomplete": "John Deere" }
{"index":{}}
{ "firstName": "Johnny", "lastName": "Cash", "autocomplete": "Johnny Cash" }
次に、joh
に対してクエリを実行し、John
に対する1つの結果とJohnny
に対する別の結果を取得できます。
{
"size": 0,
"query": {
"term": {
"autocomplete.completion": "john d"
}
},
"aggs": {
"suggestions": {
"terms": {
"field": "autocomplete.raw"
}
}
}
}
結果:
{
"aggregations": {
"suggestions": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "John Doe",
"doc_count": 1
},
{
"key": "John Deere",
"doc_count": 1
}
]
}
}
}
UPDATE(2019年6月25日):
ES 7.2は、この種の動作をネイティブに可能にするsearch_as_you_type
と呼ばれる新しいデータ型を導入しました。続きを読む: https://www.elastic.co/guide/en/elasticsearch/reference/7.2/search-as-you-type.html
追加フィールドskip_duplicatesは、次のリリース6.xで追加される予定です。
POST music/_search?pretty
{
"suggest": {
"song-suggest" : {
"prefix" : "nor",
"completion" : {
"field" : "suggest",
"skip_duplicates": true
}
}
}
}
私たちはまったく同じ問題に直面しています。 Elasticsearch 2.4では、あなたのようなアプローチは以前はうまく機能していましたが、あなたが言うように、提案者はドキュメントベースになりましたが、あなたのように、ドキュメントではなく一意の単語にのみ関心があります。
これまでに考えられる唯一の「解決策」は、提案クエリを実行する単語に対してのみ別のインデックスを作成することです。この別のインデックスで、同一の単語が一度だけインデックス付けされるようにします。次に、この個別のインデックスに対して提案クエリを実行できます。これは、このインデックスが、他のクエリに必要な他のインデックスと常に同期していることを確認する必要がある場合に限り、理想からはほど遠いものです。