Word-ориентированное завершение suggester (ElasticSearch 5.икс)

ElasticSearch 5.x ввел некоторые (разрывные) изменения в API Suggester (документация). Наиболее заметным изменением является следующее:

завершение suggester ориентирован на документ

предложения осведомлены о документ, к которому они принадлежат. Теперь, связанные документы (_source) являются возвращен как часть предложений по завершению.

короче говоря, все запросы завершения возвращают все совпадения документы вместо того, чтобы просто совпали слова. И в этом кроется проблема - дублирование завершаются автоматически слова, если они возникают более чем в одном документе.

предположим, у нас есть это простое отображение:

{
   "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) документы были возвращены - оба Джона и оба имели одинаковое значение text собственность.

тем не менее, я хотел бы получить один (1) слово. Что-то простое, вроде этого:

{
   "_shards": {
      "total": 5,
      "successful": 5,
      "failed": 0
   },
   "my-suggest": [
      {
         "text": "joh",
         "offset": 0,
         "length": 3,
         "options": [
          "John"
         ]
      }
   ]
}

вопрос: как реализовать текстовое завершение suggester. Нет необходимости возвращать какие-либо данные, связанные с документом, поскольку на данный момент они мне не нужны.

является ли" завершение Suggester " даже уместным для моего сценария? Или мне следует использовать совершенно другой подход?


редактировать: Как указывали многие из вас, жизнеспособным решением был бы дополнительный индекс только завершения. Тем не менее, я вижу несколько проблем с этим подходом:

  1. синхронизация нового индекса.
  2. автозаполнение последующих слов, вероятно, будет глобальным, а не суженным. Например, скажем, у вас есть следующие слова в дополнительных индекс: "John", "Doe", "David", "Smith". При запросе "John D", результат для неполного слова должен быть "Doe", а не "Doe", "David".

чтобы преодолеть второй момент, недостаточно только индексировать отдельные слова, так как вам также нужно будет сопоставить все слова с документами, чтобы правильно сузить автоматическое заполнение последующих слов. И с этим у вас фактически есть та же проблема, что и запрос исходного индекса. Поэтому дополнительный индекс больше не имеет смысла.

3 ответов


как указано в комментарии, еще один способ достичь этого без получения дубликатов документов-создать подполе для firstname поле, содержащее ngrams поля. Сначала вы определяете свое отображение следующим образом:

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 и 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
        }
      ]
    }
  }
}

в следующем выпуске 6 будет добавлено дополнительное поле skip_duplicates.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
            }
        }
    }
}

мы сталкиваемся с точно такой же проблемой. В Elasticsearch 2.4 подход, как вы описываете, раньше работал хорошо для нас, но теперь, как вы говорите, suggester стал документальным, в то время как, как вы, мы заинтересованы только в уникальных словах, а не в документах.

единственное "решение", которое мы могли бы придумать до сих пор, - это создать отдельный индекс только для слов, по которым мы хотим выполнить запросы предложений, и в этом отдельном индексе убедитесь, что идентичные слова только после индексации. Затем вы можете выполнить запросы предложений по этому отдельному индексу. Это далеко от идеала, хотя бы потому, что тогда нам нужно будет убедиться, что этот индекс остается синхронизированным с другим индексом, который нам нужен для наших других запросов.