OpenSearch предоставляет различные инструменты, помогающие пользователям избежать орфографических ошибок. Помимо широко известного нечеткого поиска, можно использовать еще одну функцию - " Sugester ". Суггесторы работают по-другому и используют другой синтаксис, чем обычные запросы OpenSearch.
Типы суггесторов: термин, фраза, завершение
OpenSearch предлагает три типа суггесторов:
- Предложения термина
- Предложения фраз
- Предложения завершения (автозаполнение)
Предложитель терминов
Предложитель терминов может быть запущен одновременно с запросом и использоваться для предложения альтернатив "Вы имели в виду ...?", особенно в случае, если пользователь неправильно написал слово.
В самом простом варианте программа предлагает поиск в поле для заданного набора слов. Если он найдет в индексе очень похожие по написанию слова, которые встречаются чаще, чем термины, использованные в запросе, то он предложит заменить эти термины альтернативными.
Фраза предложение
Предсказатель фраз похож на предсказатель терминов, но является более сложным. Он рассматривает расположение слов в тексте и может попытаться предложить улучшенную фразу, которая с большей вероятностью даст релевантные результаты. Для поиска релевантных результатов фразовому предложению необходимо использовать специальный анализатор (анализатор триграмм).
Предложитель завершения
Предложения завершения отличаются от предложений термина и фразы. Вместо того чтобы предлагать улучшенный запрос, он предоставляет результаты "поиска по мере ввода". Предложения завершения оптимизированы для скорости.
Как реализовать поисковые подсказки в OpenSearch
Как реализовать систему предложений терминов
В самом простом варианте, предлагатель терминов будет работать без специального маппинга на любом поле "text".
1 2 3 4 5 6 7 8 9 10 11 12 13 | POST content_programmes_v4/_search { "suggest": { "my-suggestion" : { "text" : "opensearch powered search engyne", "term" : { "field" : "content.body", "suggest_mode" : "missing", } } } } |
Результат будет таким, как показано ниже. Текст показывает исходный текст, а для некоторых слов будут предложены варианты.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | "suggest" : { "my-suggestion" : [ { "text" : "opensearch", "offset" : 0, "length" : 13, "options" : [ ] }, { "text" : "search", "offset" : 14, "length" : 6, "options" : [ ] }, { "text" : "engyne", "offset" : 21, "length" : 6, "options" : [ { "text" : "engine", "score" : 0.6, "freq" : 12 }, { "text" : "enzyme", "score" : 0.5, "freq" : 36 } ] } ] } |
Поведение предлагателя можно изменить, в частности, с помощью параметра suggest mode.
При использовании параметра "missing" будут предлагаться только те термины, которые имеют нулевые вхождения в поле в индексе, в то время как "popular" будет предлагать термины для всех терминов, для которых найден разумный вариант, более распространенный, чем термин в запросе. "missing" - хороший вариант для большинства случаев, но если ваш набор данных, скорее всего, уже содержит значительное количество неправильно написанных слов, то вам, вероятно, следует использовать "popular".
Ниже приведена таблица с основными параметрами, которые можно использовать с термином suggester:
Field | Поле, в котором суггестор должен искать предложения |
Analyzer | Определяет поисковый анализатор, который будет применяться к текстовым значениям поля по умолчанию |
Gram_size | Должно быть установлено в 1 или опущено, если поле не является полем ngram или shingle |
Text | Входной текст для поиска |
Highlight | Определяет маркер для вывода |
Collate | Определяет запрос для повторной проверки всех предложений (см. раздел Использование Collate ниже) |
Size | Определяет общее количество предложений, которые будут извлечены (лучше всего использовать 5 или меньше, чтобы избежать нерелевантных предложений) |
Shard_size | Определяет максимальное количество предложений для каждого шарда |
Как реализовать фразовые предложения
для работы с фразовыми предложениями требуется маппинг триграмм.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | PUT my-index { "settings": { "index": { "number_of_shards": 1, "analysis": { "analyzer": { "trigram": { "type": "custom", "tokenizer": "standard", "filter": ["lowercase","shingle"] } }, "filter": { "shingle": { "type": "shingle", "min_shingle_size": 2, "max_shingle_size": 3 } } } } }, "mappings": { "properties": { "content": { "type": "text", "fields": { "trigram": { "type": "text", "analyzer": "trigram" }} } } } } |
Затем можно выполнить следующий запрос:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | POST my-index/_search { "suggest": { "text": "Scarlett Johanssen", "simple_phrase": { "phrase": { "field": "content.trigram", "size": 1, "gram_size": 3, "direct_generator": [ { "field": "content.trigram", "suggest_mode": "always" } ], "highlight": { "pre_tag": "<em>", "post_tag": "</em>" } } } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | "suggest" : { "simple_phrase" : [ { "text" : "Scarlett Johanssen", "offset" : 0, "length" : 18, "options" : [ { "text" : "scarlett johannsson", "highlighted" : "scarlett <em>johannsson</em>", "score" : 0.19398963 } ] } |
В этом случае, хотя в нашем тексте может быть несколько ошибочных вариантов "johannsson", OpenSearch будет использовать контекст "scarlett", чтобы извлечь версию johannsson, наиболее часто встречающуюся вместе со словом Scarlett.
Фраза suggest требует от нас определения списка direct_generators (1 или более), которые отвечают за генерацию предложений для каждого термина во фразе suggestion. Каждый генератор требует параметр field как минимум, но вы можете настроить его с помощью следующих опций:
Field | Поле в индексе, используемое для поиска предложений |
Max_edits | 0,1,2 - максимальное количество правок, необходимое для достижения предложения из ввода |
Min_doc_freq | По умолчанию отключено. Минимальное количество документов, в которых должно встречаться наше слово, чтобы оно появилось в качестве предложения |
Min_word_length | По умолчанию равно 4, поскольку орфографические ошибки нечасто встречаются в коротких словах. Увеличение этого числа повышает производительность, но при этом исключает возможные предложения. |
Max_term_freq | Если исходный термин встречается в индексе более x раз, то не ищите предложения. Это улучшит производительность за счет того, что вы не будете искать часто встречающиеся слова (предполагается, что они написаны правильно), рискуя исключить возможные предложения. |
Prefix_length | По умолчанию равно 1, поскольку обычно орфографические ошибки не встречаются в начале слов. Увеличение этого числа повышает производительность, рискуя исключить возможные предложения. |
Pre_filter | Это позволяет применить фильтр анализа текста к входному тексту перед поиском предложений. Это может быть использовано для предложения синонимов, например, анализ продаж предлагает анализ доходов |
Post_filter | Это позволяет применить фильтр анализа текста к выходным предложениям. |
Использование collate
Collate позволяет точно настроить поисковые предложения, перепроверив их с помощью запроса.
Например:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | POST actors/_search { "suggest": { "text" : "scarlett johannson", "simple_phrase" : { "phrase" : { "field" : "actors.trigram", "size" : 1, "direct_generator" : [ { "field" : "actors.trigram", "suggest_mode" : "always", "min_word_length" : 1 } ], "collate": { "query": { "source" : { "match": { "{{field_name}}" : "{{suggestion}}" } } }, "params": {"field_name" : "actors"}, "prune": true } } } } } |
Этот запрос проверяет, действительно ли предложение дает результат, если его выполнить по полю, не связанному с триграммой (actors vs actor.trigram). Если запрос collate не дает результатов, предложение не будет включено.
Как реализовать предложения завершения
Чтобы создать автозаполняемый тип предложения, необходимо создать специальный маппинг с типом "completion".
1 2 3 4 5 6 7 8 9 10 11 12 13 | PUT actors { "mappings": { "properties": { "suggest": { "type": "completion" }, "name": { "type": "keyword" } } } } |
В приведенном выше примере мы создали поле "suggest", содержащее данные для поиска.
Для поиска по вышеуказанному индексу мы можем выполнить такой запрос, как:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | POST actors/_search { "_source": "suggest", "suggest": { "my-suggest": { "prefix": "sca", "completion": { "field": "suggest", "size": 5 , "skip_duplicates": true } } } } |
Для ускорения ответа "_source" ограничивается полем suggester.
Параметр "skip_duplicates" может быть true или false в зависимости от необходимости.
Размер" указывает, сколько предложений должно быть возвращено.
Нечеткий поиск в автозаполненных предложениях
Вы также можете разрешить орфографические ошибки в запросе на заполнение, добавив объект "fuzzy" в поле.
1 2 3 4 5 | "field": "suggest", "fuzzy": { "fuzziness": 2, "min_length": 4 } |
Значение нечеткости указывает на количество орфографических ошибок, допустимых в запросе. Однако имейте в виду, что добавление нечеткости может дать неожиданные результаты, особенно в начале, когда количество букв в поисковом запросе невелико. Поэтому рекомендуется задавать достаточно высокое значение "min_length" - минимальной длины поискового запроса перед применением нечеткости.
Ответ автокомплектовщика
Ответ будет выглядеть примерно так:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | "suggest": { "my-suggest": [ { "text": "sca", "offset": 0, "length": 3, "options": [ { "text": "scarlett johannssen", "_index": "music", "_id": "1", "_score": 1.0, "_source": { "suggest": [ "scarlett johannssen" ] } } ] } ] } |
Контекстная фильтрация автозаполнения
Обычно автозаполнитель работает со всеми документами в индексе, и отфильтровать его по другим полям документа невозможно. Однако можно добавить поле "контекст", чтобы автозаполнение можно было фильтровать по категориям или местоположению. Таким образом, можно будет предлагать товары в интернет-магазине, отфильтрованные по определенной категории, или предлагать отели, отфильтрованные по определенной географической зоне. Для этого необходимо добавить контекст в каждый документ в момент индексации.
Добавление контекста в маппинг предложений
Следующий маппинг определяет, что у предложения завершения есть два контекста: один типа category, который использует теги, найденные в поле "category_tags", а другой типа geo, найденный в поле "loc", которое должно быть типа geo_point.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | PUT hotels { "mappings": { "properties": { "suggest": { "type": "completion", "contexts": [ { "name": "hotel_type", "type": "category", "path": "category_tags" }, { "name": "location", "type": "geo", "precision": 4, "path": "loc" } ] }, "loc": { "type": "geo_point" }, "category_tags": { "type": "keyword" } } } } |
Для каждого документа нам нужно добавить соответствующие данные в контекстные поля, определенные в маппинге.
1 2 3 4 5 6 7 | PUT hotels/_doc/1 { "suggest": ["Gran Hotel"], "category_tags": ["family_run", "3 star"] , "loc": "41.56,-71.27" } |
При выполнении запроса suggest мы должны передать контекст или контексты, которые мы хотим использовать для фильтрации.
В приведенном ниже запросе мы передаем два контекста: один для местоположения, а другой для типа отеля. Обратите внимание, что если передано более одного контекста, то предложения должны соответствовать только одному контексту, и нет никакой гарантии, что предложения, соответствующие большему количеству контекстов, будут возвращены выше в списке.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | POST hotels/_search { "suggest": { "hotel_suggestion": { "prefix": "gra", "completion": { "field": "suggest", "size": 10, "contexts": { "location": { "lat": 41.56, "lon": 71.27 }, "hotel_type": "family_run" } } } } } |
Примечания и полезные советы
В случае с предложениями терминов и фраз следует помнить, что предложения терминов и фраз могут выполняться одновременно с основным запросом, так что вы можете получать предложения одновременно с основным запросом, избавляя себя от необходимости выполнять два запроса.
Однако использование подсказок может значительно замедлить выполнение запросов, поэтому лучше отправлять запрос с подсказкой только в том случае, если исходный запрос дает мало результатов.
Если вы рассматриваете возможность использования предложения завершения, помните, что существуют и другие альтернативы с аналогичной функциональностью, такие как префиксные запросы или "поиск по мере ввода", или API Terms Enum. В целом, преимущество предложений завершения в том, что они работают быстро и легко устраняют дубликаты. Однако сложнее точно настроить приоритет возврата предложений.