web-dev-qa-db-ja.com

ワード指向の補完サジェスタ(ElasticSearch 5.x)

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 [〜#〜]:多くの人が指摘したように、補完のみのインデックスを追加することは、実行可能なソリューションになります。ただし、このアプローチには複数の問題が見られます。

  1. 新しいインデックスの同期を維持します。
  2. 後続の単語のオートコンプリートは、絞り込まずに、おそらくグローバルになります。たとえば、追加インデックスに次の単語があるとします:"John", "Doe", "David", "Smith""John D"を照会すると、不完全なWordの結果は"Doe"ではなく"Doe", "David"になります。

2番目のポイントを克服するには、単一の単語にインデックスを付けるだけでは不十分です。自動補完する後続の単語を適切に絞り込むために、すべての単語をドキュメントにマップする必要があるためです。これにより、実際には元のインデックスのクエリと同じ問題が発生します。したがって、追加のインデックスはもう意味がありません。

21
alesc

コメントで示唆したように、重複したドキュメントを取得せずにこれを達成する別の方法は、フィールドの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

19
Val

追加フィールドskip_duplicatesは、次のリリース6.xで追加される予定です。

https://www.elastic.co/guide/en/elasticsearch/reference/master/search-suggesters-completion.html#skip_duplicates のドキュメントから:

POST music/_search?pretty
{
    "suggest": {
        "song-suggest" : {
            "prefix" : "nor",
            "completion" : {
                "field" : "suggest",
                "skip_duplicates": true
            }
        }
    }
}
2
Dries Cleymans

私たちはまったく同じ問題に直面しています。 Elasticsearch 2.4では、あなたのようなアプローチは以前はうまく機能していましたが、あなたが言うように、提案者はドキュメントベースになりましたが、あなたのように、ドキュメントではなく一意の単語にのみ関心があります。

これまでに考えられる唯一の「解決策」は、提案クエリを実行する単語に対してのみ別のインデックスを作成することです。この別のインデックスで、同一の単語が一度だけインデックス付けされるようにします。次に、この個別のインデックスに対して提案クエリを実行できます。これは、このインデックスが、他のクエリに必要な他のインデックスと常に同期していることを確認する必要がある場合に限り、理想からはほど遠いものです。

1
Edgar Vonk