Словаристский помощник по завершению (ElasticSearch 5.x)
ElasticSearch 5.x внедрил некоторые (взломанные) изменения в API-интерфейсе Suggestester (Документация). Наиболее заметным изменением является следующее:
Эксперт по завершению документирования
Предложения знают о к которому они принадлежат. Теперь связанные документы (_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"
]
}
]
}
Вопрос: как реализовать основанный на словах помощник по завершению. Нет необходимости возвращать какие-либо документы, связанные с данными, поскольку в данный момент мне это не нужно.
Является ли "Завершающий запрос" даже подходящим для моего сценария? Или я должен использовать совершенно другой подход?
ИЗМЕНИТЬ:
Как многие из вас указали, дополнительный индекс завершения будет эффективным решением. Однако я вижу несколько проблем с этим подходом:
- Синхронизация нового индекса.
- Автозаполнение последующих слов, вероятно, будет глобальным, а не суженным. Например, скажем, что в дополнительном индексе есть следующие слова:
"John", "Doe", "David", "Smith"
. При запросе "John D"
результат для неполного слова должен быть "Doe"
, а не "Doe", "David"
.
Чтобы преодолеть вторую точку, только индексирование отдельных слов будет недостаточно, так как вам также нужно будет сопоставить все слова с документами, чтобы правильно сузить автозаполнение последующих слов. И при этом у вас есть такая же проблема, как запрос исходного индекса. Поэтому дополнительный индекс больше не имеет смысла.
Ответы
Ответ 1
Как указывалось в комментарии, другой способ достижения этой цели без получения дубликатов документов является создание суб-поле для 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
}
]
}
}
}
ОБНОВЛЕНИЕ (25 июня 2019 г.):
В ES 7.2 был введен новый тип данных с именем search_as_you_type
который изначально search_as_you_type
такое поведение. Узнайте больше по адресу: https://www.elastic.co/guide/en/elasticsearch/reference/7.2/search-as-you-type.html.
Ответ 2
В следующем выпуске 6.x добавлено дополнительное поле skip_duplicates.
Из документов в 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
}
}
}
}
Ответ 3
Мы сталкиваемся с той же проблемой. В Elasticsearch 2.4 подход, подобный тому, который вы описали, использовался для нас отлично, но теперь, как вы говорите, инициатор стал документированным, так как вы заинтересованы только в уникальных словах, а не в документах.
Единственное "решение", о котором мы могли подумать, - создать отдельный индекс только для слов, на которых мы хотим выполнить запросы предложений, и в этом отдельном индексе убедитесь, что так или иначе идентичные слова только индексируются один раз. Затем вы можете выполнить запросы предложения по этому отдельному индексу. Это далеко не идеально, хотя бы потому, что нам нужно будет убедиться, что этот индекс остается в синхронизации с другим индексом, который нам нужен для других наших запросов.