В этой статье мы расскажем о том, как избежать критических ошибок в производительности, почему стандартное решение OpenSearch не подходит для этого, а также о важных аспектах реализации. Все современные сайты имеют функции автозаполнения (поиск по мере ввода) в строке поиска для повышения удобства пользователей (никто не хочет набирать целые поисковые запросы...). Очень важно, чтобы автозаполнение было быстрее, чем стандартный поиск, поскольку смысл автозаполнения заключается в том, чтобы начать показывать результаты, пока пользователь набирает текст. Если задержка будет высокой, это приведет к ухудшению качества работы пользователя.
Обратите внимание, что в результатах поиска присутствуют вопросы, связанные с функциями автомасштабирования, автотегирования и автозаполнения OpenSearch. Пользователи могут набрать еще несколько символов для уточнения результатов поиска.
Различные подходы к реализации автозаполнения в OpenSearch (поиск по мере ввода)
Существует множество способов реализации функции автозаполнения, которые в целом делятся на четыре основные категории:
Индексное время
Иногда требуется просто префиксное или инфиксное завершение в автозаполнении. Нередко можно встретить реализацию автозаполнения с использованием custom-анализаторов, что предполагает индексацию лексем таким образом, чтобы они соответствовали поисковому запросу пользователя.
Если продолжить наш пример, то перед нами документы, состоящие из "OpenSearch autocomplete", "OpenSearch auto-tag", "OpenSearch auto scaling" и "OpenSearch automatically". Анализатор по умолчанию не генерирует частичные лексемы для "autocomplete", "autoscaling" и "automatically", а поиск "auto" не дает никаких результатов.
Для решения этой проблемы в OpenSearch для индексирования лексем используются токенизаторы edge ngram или n-gram, которые позволяют получить результаты автозаполнения.
В данном подходе используются запросы Match, которые являются быстрыми, так как используют сравнение строк (в котором используется хэш-код), а точных лексем в индексе сравнительно меньше.
Время выполнения запроса
Автозаполнение может быть достигнуто путем замены совпадающих запросов на префиксные. В то время как запросы на совпадение работают на совпадении токенов (проиндексированных) с токенами (токенами поискового запроса), префиксные запросы (как следует из их названия) совпадают со всеми токенами, начиная с поисковых токенов, поэтому количество совпадающих документов (результатов) велико.
Как уже объяснялось, префиксный запрос не является точным совпадением лексем, а основан на совпадении символов в строке, что очень дорого и позволяет получить большое количество документов. Внутри OpenSearch для хранения лексем используется структура данных типа B+ tree. Полезно понять внутреннее устройство структуры данных, используемой в инвертированных индексах, и то, как различные типы запросов влияют на производительность и результаты.
В OpenSearch также появился булевский префиксный запрос Match. Он представляет собой комбинацию запросов Match и Prefix и обладает лучшими качествами из обоих миров. Он особенно полезен при наличии нескольких поисковых терминов. Например, если у вас есть foo bar baz
, то вместо того, чтобы выполнять префиксный поиск по всем условиям поиска (это дорого и дает меньше результатов), этот запрос будет выполнять префиксный поиск только по последнему условию и сопоставлять предыдущие условия в любом порядке. Это повышает скорость и релевантность результатов поиска.
Предложения по завершению
Это полезно, если вы предоставляете предложения по поисковым запросам, как, например, на сайтах электронной торговли или поиска отелей. Строка поиска предлагает предложения по запросу, в отличие от предложений, появляющихся в реальных результатах поиска, и после выбора одного из предложений, предоставленных программой completion suggestester, она выдает результаты поиска.
Она не выводит результаты поиска на основе поисковых запросов, как это было показано в нашем примере. Внутри системы он работает путем индексирования лексем, которые хотят предложить пользователи, а не на основе существующих документов.
Поиск по мере ввода
Это тип данных, предназначенный для облегчения автозаполнения запросов без предварительных знаний о настройке пользовательского анализатора. OpenSearch внутренне хранит различные лексемы (edge n-gram, shingles) одного и того же текста, и поэтому может использоваться как для префиксного, так и для инфиксного завершения. Это может быть удобно, если не знаком с расширенными возможностями OpenSearch, что характерно для трех других подходов. Для работы в простых случаях требуется не так много настроек, а примеры кода и более подробная информация доступны в официальной документации ОС.
Примеры кода
Время индекса
Определение индекса:
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 | { "settings": { "analysis": { "filter": { "autocomplete_filter": { "type": "edge_ngram", "min_gram": 1, "max_gram": 10 } }, "analyzer": { "autocomplete": { "type": "custom", "tokenizer": "standard", "filter": [ "lowercase", "autocomplete_filter" ] } } } }, "mappings": { "properties": { "title": { "type": "text", "analyzer": "autocomplete", "search_analyzer": "standard" :-> примечание search_analyzer } } } } |
Поисковый запрос:
1 2 3 4 5 6 7 8 9 | { "query": { "match": { "movie_name": { "query": "avengers inf" } } } } |
Время выполнения запроса
Определение индекса:
1 2 3 4 5 6 7 8 9 | { "mappings": { "properties": { "movie_name": { "type": "text" } } } } |
Поисковый запрос:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | { "query": { "bool": { "should": [ { "match": { "movie_name": "avengers" } }, { "prefix": { "movie_name": "inf" } } ] } } } |
Вопросы производительности
Почти все описанные выше подходы хорошо работают с небольшими наборами данных и легкой поисковой нагрузкой, но когда у вас есть массивный индекс, получающий большое количество запросов с автоматическим предложением, то SLA и производительность вышеупомянутых запросов становятся очень важными. Следующие пункты должны помочь вам выбрать подход, наиболее подходящий для ваших нужд:
- Токены Ngram или edge Ngram значительно увеличивают размер индекса, обеспечивая ограничения min и max gram в зависимости от приложения и возможностей. Планирование позволит избежать значительных проблем в производстве.
- Разрешение запросов с пустыми или малосимвольными префиксами может привести к появлению всех документов в индексе и вывести из строя весь кластер. Всегда лучше делать префиксный запрос только по термину (по нескольким полям) и ограничивать минимальное количество символов в префиксных запросах. В настоящее время эта проблема решается с помощью булевого префиксного запроса Match, о котором говорилось выше.
- Предусмотренный ЭС тип данных "поиск по мере ввода" токенизирует входной текст в различных форматах. Поскольку это решение, предоставляемое ES, не может удовлетворить всем требованиям, всегда лучше проверить все угловые случаи, необходимые для использования в вашем бизнесе. Кроме того, как уже упоминалось, оно токенизирует поля в нескольких форматах, что может увеличить размер индексного хранилища OpenSearch.
- Completion предполагает отдельное индексирование предложений и не затрагивает такой сценарий использования, как получение результатов поиска.
- Подходы, основанные на использовании индексного времени, являются быстрыми, поскольку меньше накладных расходов во время выполнения запроса, но они предполагают больше трудозатрат, таких как переиндексация, планирование емкости и увеличение стоимости диска. Время запроса легко реализовать, но поисковые запросы требуют больших затрат. Это очень важно понимать, поскольку в большинстве случаев пользователям приходится выбирать один из них, и понимание этого компромисса может помочь в решении многих проблем, связанных с производительностью.
Разное
В большинстве случаев решения, предлагаемые ОС для автозаполнения, либо не отвечают требованиям бизнеса, либо влияют на производительность больших систем, поскольку это не универсальные решения. В большинстве случаев для получения оптимизированного решения (более производительного и отказоустойчивого) пользователям приходится прибегать к настройкам, а решение проблем с производительностью OpenSearch не является тривиальным.