Автозаполнение в наше время является обязательным элементом. OpenSearch выпустил множество функций, позволяющих сделать автозаполнение более гибким и мощным, а также более адаптируемым к требованиям каждого конкретного случая использования.
API перечисления терминов
API перечисления терминов ищет сходства в индексе на основе частичных совпадений. Такой подход может помочь нам выполнять поиск в наших полях с низкой задержкой.
Как следует из названия "terms enum", этот API работает с терминами, или, другими словами, с полями типа "keyword".
По умолчанию API terms enum сопоставляет термины с учетом регистра, чтобы сделать запрос как можно более легким. Также важно отметить, что terms_enum всегда будет соответствовать с начала значения поля.
У Terms enum API есть свойство "timeout", которое остановит запрос по истечении заданного времени. По истечении времени запрос может вернуть частичные или пустые результаты. Мы также можем ограничить количество терминов с помощью свойства "size".
В API перечисления Terms есть еще два параметра, которые являются более продвинутыми: index_filter и search_after.
Index_filter позволит нам вызывать API Terms enum API по многим индексам, используя подстановочный знак (см. подробное объяснение ниже). Search_after позволит нам перечислять термины постранично, если мы определим, по какому термину вы хотите начать поиск, аналогично параметру "from" (см. пример ниже).
Как использовать API перечисления терминов
Давайте попробуем проиндексировать несколько документов в новый индекс.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | POST _bulk { "index" : { "_index" : "test_terms_enum"} } {"name": "Star wars"} { "index" : { "_index" : "test_terms_enum"} } {"name": "Star trek"} { "index" : { "_index" : "test_terms_enum"} } {"name": "Shrek"} { "index" : { "_index" : "test_terms_enum"} } {"name": "Heaven is full of stars"} { "index" : { "_index" : "test_terms_enum"} } {"name": "Starman"} { "index" : { "_index" : "test_terms_enum"} } {"name": "The last status"} { "index" : { "_index" : "test_terms_enum"} } {"name": "starter pack"} |
Теперь попробуем вызвать API _terms_enum:
1 2 3 4 5 | GET test_terms_enum/_terms_enum { "field": "name", "string": "star" } |
Мы должны увидеть хотя бы несколько результатов, связанных со "звездами":
1 2 3 4 5 6 7 8 9 | { "_shards" : { "total" : 1, "successful" : 1, "failed" : 0 }, "terms" : [ ], "complete" : true } |
Однако наши результаты ничего не показывают. Это потому, что, как мы уже говорили, этот API работает с типами полей ключевых слов.
OpenSearch будет генерировать поле .keyword динамически, поэтому давайте попробуем еще раз:
1 2 3 4 5 | GET test_terms_enum/_terms_enum { "field": "name.keyword", "string": "star" } |
Теперь здесь должны появиться "Звездные войны", "Звездный путь", "Старман" и стартовый пакет, верно?
1 2 3 4 5 6 7 8 9 10 11 | { "_shards" : { "total" : 1, "successful" : 1, "failed" : 0 }, "terms" : [ "starter pack" ], "complete" : true } |
И снова не те результаты, которых мы ожидали. Это связано с чувствительностью API к регистру. Мы можем изменить это следующим образом:
1 2 3 4 5 6 7 8 | GET test_terms_enum/_terms_enum { "size":10, "timeout":"1s", "field": "name.keyword", "string": "star", "case_insensitive": true } |
Давайте проверим ответ:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | { "_shards" : { "total" : 1, "successful" : 1, "failed" : 0 }, "terms" : [ "Star trek", "Star wars", "Starman", "starter pack" ], "complete" : true } |
Отлично. Теперь у нас есть ожидаемые результаты. Обратите внимание на свойство "size", которое в этом примере установлено по умолчанию - 10, что позволяет нам ограничить количество терминов.
А как насчет "Небеса полны звезд"? Поскольку API перечисления терминов будет искать с начала значения поля, для того, чтобы оно совпало, строка запроса должна быть "hea".
Также обратите внимание на свойство "тайм-аут", в данном случае одна секунда, которое остановит запрос по истечении заданного времени.
Теперь давайте рассмотрим еще два важных параметра: index_filter и search_after.
index_filter
Вы можете использовать API перечисления терминов для многих индексов, используя подстановочный знак (*).
Например:
1 2 3 4 5 6 7 8 9 10 11 12 13 | GET test_terms_enum*/_terms_enum { "field": "name.keyword", "string": "star", "case_insensitive": true, "index_filter": { "range": { "rating": { "gte": 10 } } } } |
Это запросит все индексы, начиная с test_terms_enum, и пропустит все те, которые не дают результатов (map to match_none) при фильтрации по полю рейтинга, равному или большему 10.
Поскольку в нашем индексе нет такого поля рейтинга, этот фильтр не даст результатов, и индекс будет опущен, так что ни один термин не будет показан, даже если у "звезды" есть совпадения.
Важное замечание
Фильтрация выполняется по принципу best-effort, она использует статистику индексов и сопоставления для переписывания запросов в match_none вместо полного выполнения запроса. Например, запрос диапазона по полю даты может быть переписан в match_none, если все документы в шарде (включая удаленные документы) находятся за пределами заданного диапазона. Однако не все запросы могут быть переписаны в match_none, поэтому данный API может вернуть индекс, даже если предоставленный фильтр не соответствует ни одному документу.
search_after
Этот параметр используется в качестве живого указателя для пагинации.
В этом контексте вы можете использовать search_after для пагинации ваших терминов, вам просто нужно определить, с какого термина вы хотите начать поиск, аналогично параметру "from" в поисковом запросе.
Пагинация важна для того, чтобы сохранить полезную нагрузку легкой и запрашивать только те данные, которые пользователь должен увидеть в определенный момент. Нет смысла запрашивать сразу 1000 результатов, если пользователь может увидеть только 10 на одной странице. Лучше выполнять новый запрос при каждом изменении страницы и запрашивать каждый раз 10 результатов.
1 2 3 4 5 6 7 | GET test_terms_enum/_terms_enum { "field": "name.keyword", "case_insensitive": true, "string": "star", "search_after": "Star wars" } |
Этот запрос начнет поиск после термина "Звездные войны". Идея заключается в том, чтобы динамически устанавливать этот параметр на основе текущего последнего термина.
Ответ без параметра search_after:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | { "_shards" : { "total" : 1, "successful" : 1, "failed" : 0 }, "terms" : [ "Star trek", "Star wars", "Starman", "starter pack" ], "complete" : true } |
Ответ с параметром search_after:
1 2 3 4 5 6 7 8 9 10 11 12 | { "_shards" : { "total" : 1, "successful" : 1, "failed" : 0 }, "terms" : [ "Starman", "starter pack" ], "complete" : true } |
Обратите внимание, что "Star wars" работает как указатель, а второй поиск выполняется после этого термина.
Заключение
Мы узнали, как использовать новый API перечисления терминов. Этот API может быть очень полезен в некоторых случаях, а в сочетании с другими подходами может значительно повысить производительность и релевантность.
За
- Простота использования
- Быстрый
- Не требуется переиндексация или новые поля
- Чувствителен или нечувствителен к регистру
- Поддерживает пагинацию
ПРОТИВ
- Совпадает только с началом термина
- Нечеткость не поддерживается