Работая с e-commerce проектами, интернет-магазинами, онлайн-аптеками, маркетплейсами, в конечном счете даже с сайтами знакомств, приходится охватывать большое количество данных и разрабатывать удобный инструмент для поиска. Таким инструментом и является Elasticsearch.
В этой статье расскажем, что такое Elasticsearch и как им пользоваться, рассмотрим его преимущества и недостатки, покажем примеры использования. А также поделимся лайфхаками использования этого инструмента для поиска.
ЧТО ТАКОЕ ELASTICSEARCH
Elasticsearch — это система полнотекстового поиска, написанная на Java. Кроме того, Elasticsearch — это нереляционное хранилище документов в формате JSON, которое выпущено как проект с открытым исходным кодом в соответствии с условиями лицензии Apache.
КАК РАБОТАЕТ
В Elasticsearch отправляются данные в виде документов JSON с помощью API или такого инструмента, как Logstash. Elasticsearch сохраняет документ в индекс кластера и делает его доступным для поиска. После этого можно найти и извлечь документ, используя API Elasticsearch.
ES позволяет производить поиск по документам в режиме реального времени, он горизонтально масштабируется и поддерживает многопоточность. У других NoSQL-систем выигрывает качеством и скоростью обработки текста и гибким полнотекстовым поиском по всей базе документов.
ПОЧЕМУ УДОБНО РАБОТАТЬ С ELASTICSEARCH
В сервисах с большим количеством данных, а именно e-commerce проектах, интернет-магазинах, онлайн-аптеках, маркетплейсах, сайтах знакомств, поиск — это фасеты. Фасеты — это условия для поиска, например, набор тегов или фильтр по значениям.
Работая с большим объемом данных, мы сталкиваемся со стандартной схемой данных для типичного e-commerce в СУБД, и выглядит она примерно так:
В такой CMS-системе хранятся товары, свойства, категории, свойства категорий и категории свойств, а также много связей и сущностей. Данных слишком много. И для того, чтобы все нормально структурировать, нам нужен инструмент для поиска.
АЛЬТЕРНАТИВЫ ELASTICSEARCH
Перед тем, как начать работать с Elasticsearch, рассмотрим альтернативные СУБД, которые могут помочь сделать поиск удобным — MongoDB и PostgreSQL.
MongoDB:
- Нет схем, то есть не нужно заморачиваться о структуре данных, вносим всё, что угодно;
- Web scale — при повышении нагрузки масштабирование делается очень легко как по горизонтали, так и по вертикали;
- Отсутствует аналог конструкции JOIN, а это значит, что мы не тратим время на соединение таблиц, в документе уже все есть;
- Вся валидация делается в коде. Если вы откроете любое руководство, вы увидите, что сначала ставят драйвер для MongoDB, а только потом библиотеку валидаций;
- Нет аналогов хранимых процедур. Опять же — все в коде.
PostgreSQL:
- Есть нереляционное хранилище в формате JSON, можно назначать ограничения на поля. Например, есть таблица пользователя, у пользователя есть персональные данные, надо ввести на них ограничения. JSON отлично справится с этим;
- Наличие TextSearch функций, которые обеспечивают работу полнотекстового поиска;
- Есть хранимые процедуры;
- Большое количество таблиц/столбцов/индексов;
- Не все ORM понимают JSON.
Используя MongoDB и PostgreSQL для полнотекстового поиска, мы получим ограниченную функциональность. В некоторых случаях её будет достаточно, но для полноценного удобства предлагаем использовать Elasticsearch (далее — ES). У него также есть свои плюсы и минусы.
ПРЕИМУЩЕСТВА
- Для общения с Elasticsearch используется RESTful API или, если говорить простым языком, обыкновенные HTTP-запросы. И работать с ним можно прямо из браузера. Для просмотра информации о сервисе достаточно обратиться по адресу localhost:9200;
- Можно сделать валидацию на уровне хранилища, в ES это называется маппингом (mappings);
- ES умеет работать с разными запросами — простыми, сложными, структурированными — и различными типами данных;
- JSON QUERY — ещё более декларативный синтаксис, чем SQL;
- Kibana — визуальный инструмент для ES, чтобы взаимодействовать с данными, которые хранятся в индексах ES. Веб-интерфейс Kibana позволяет выполнять и тестировать запросы, настраивать сам ES-кластер и многие другие вещи.
Кроме преимуществ, у ES есть, конечно, и недостатки.
НЕДОСТАТКИ
Разработчики ES предоставляют нужные библиотеки для большинства популярных языков в разделе Elasticsearch Clients. И по ряду причин это не самый лучший выбор. Клиент для каждого языка имеет свои несовершенства:
- Java Client — вложенные цепные вызовы на лямбдах, синтаксис на любителя;
- JavaScript Client — максимально похож на jQuery.ajax — в целом работает и ладно;
- Go Client — можно вызвать Search().Raw([]byte), т.е. мы точно так же пишем многострочный текстовый запрос, как мы это делали в примере с PHP и HEREDOC;
- .NET Clients — что-то среднее между Java- и Go-клиентами;
- PHP Client — не умеет работать с шаблонами и в целом мало что умеет;
- Python Client — предлагает очень странный DSL.
Используя Elasticsearch Clients, мы пытаемся взять запрос в самом ES в формате JSON, например:
{
"_source" : [
"guidEsId",
... еще 36 полей ...
"expireDates.77"
],
"query" : {
"function_score" : {
"query" : {
"bool" : {
"must" : [
{"exists" : {"field" : "priceBuyer.77"}},
{"terms" : {"mnn" : ["пурген"]}},
{"term" : {"active" : "1"}},
{"regexp" : {"itemName" : ".*таблетки.*25мг.*"}},
{"range" : {"packQuantity" : {"lte" : 10}}},
{"range" : {"priceBuyer.77" : {"gt" : 0}}},
{"terms" : {"itemGroup" : [96]}},
{
"bool" : {
"should" : [
{"range" : {"quantities.77.1" : {"gt" : 0}}},
{"range" : {"quantities.77.8" : {"gt" : 0}}},
{"range" : {"quantities.77.7" : {"gt" : 0}}},
{"range" : {"quantities.77.6" : {"gt" : 0}}}
]
}
}
]
}
}
}
}
}
и построить его в привычном ООП-стиле. Но на выходе в ES Clients получается такой результат:
SearchResponse<Product> search = new ElasticsearchClient().search(s -> s
.query(q -> q
.bool(b -> b
.must(m -> m
.exists(...)
.terms(...)
.term(...)
.regexp(...)
.range(...)
.range(...)
.terms(...)
.bool(b -> b
.should(sh -> sh
.range(...)
.range(...)
)
)
)
)
)
)
Поначалу может быть нормально и писать будет легко. Но когда пишутся запросы по 100-120 строк, появляются огромные нечитаемые циклы. В дальнейшем это будет невозможно поддерживать другим разработчикам.
Поэтому предлагаем альтернативу Elasticsearch Clients:
Клиент для языка программирования | Чем предлагаем заменить |
Java Client | org.apache.http.client.HttpClient |
JavaScript Client | node:http/axios/Fetch API |
Ruby Client | Net::HTTP |
Go Client | net/http |
.NET Clients | System.Net.Http.HttpClient |
PHP Client | curl/GuzzleHttp |
Python Client | requests или httpx |
Python Client | reqwest |
ЛАЙФХАКИ, КОТОРЫЕ ПОМОГУТ ВАМ ПРИ ИСПОЛЬЗОВАНИИ ELASTICSEARCH
- Вам не всегда нужен ESClient
Несмотря на то, что мы говорили раньше, в начале пути можно и нужно пользоваться ESClient. Поскольку сначала писать на JSON-языке запросов может быть непривычно и крайне сложно. Со временем у вас соберется пул типичных запросов, которые выполняются всегда. В этот момент вы уже, скорее всего, устанете писать на цепных запросах в Query Builder. Тогда и можно будет перейти к стадии «выкидываем ESClient» и изучить шаблоны, в частности шаблонизатор Mustache.
- Опасайтесь вложенных полей
У ES есть «вложенные поля» — nested object type. Содержимое полей хранится как отдельный документ и может сэкономить память. Например, если у вас есть список пользователей, и есть внутренний объект «организация, к которой принадлежит этот пользователь». Если вы сделаете индекс привычным образом, у каждого пользователя будет, условно говоря, «своя уникальная организация» как отдельное поле. Если сделать nested-объекты, в nested-индексе сохранится один объект организации, а все остальные пользователи будут просто на него ссылаться. Мы по факту сжимаем и входные, и индексированные данные.
- Сжимайте ответы
В ES есть &filter_path. Это параметр, который позволяет указать, какие поля из итогового ответа вы забираете. Потому что в ES в поиске возвращает объект, в котором есть поле hits, в котором лежит поле hits, который является массивом, в котором лежат объекты. И внутри этого объекта, например, поля _source и _score.
- hits.hits._source — вернет тело документа;
- hits.hits._score — вернет рейтинг соответствия;
- hits.hits._id — вернет внутренний ID документа.
Кроме этого, ES возвращает много дополнительных данных, а filter_path позволяет обрезать ответ.
- Фильтруйте то, что уже отфильтровано
Вы часто видели в интернет-магазинах возможность выбрать телефон на 16ГБ оперативной памяти, 32ГБ и 64ГБ. Можно поставить галочку на 32ГБ, и все остальные варианты скрываются как неактивные. В этом случае работает фильтр, и другие варианты фильтрации отображаться не будут. Но иногда пользователю нужно оставить и другие опции доступными для выбора. Для этого в ES есть post_filter — он фильтрует то, что уже отфильтровано и сгруппировано. В конечном итоге у пользователя есть отфильтрованный список тех параметров, которые ему нужны в телефоне. Но при этом остаются доступными и другие опции, по которым также можно искать.
{
"query": {
"bool": {
"filter": {
"term": {"brand": "gucci" }
}
}
},
"aggs": {
"colors": {
"terms": { "field": "color" }
},
"color_red": {
"filter": {
"term": { "color": "red" }
},
"aggs": {
"models": {
"terms": { "field": "model" }
}
}
}
},
"post_filter": {
"term": { "color": "red" }
}
}
В этом примере мы показываем пользователю, какие еще цвета доступны для поиска, но показываем только красные модели.
- Пользуйтесь Aliases
Alias — это псевдонимы индексов. Изначально в чем проблема — чаще всего мы смотрим в один индекс. И у этого индекса статическое имя, которое где-то в настройках уже лежит. Иногда, чтобы добавить данные, нужно перестроить индекс. Но заставлять пользователя ждать индексацию — не разумно, это занимает много времени. В ES можно сделать это в фоновом режиме. Мы просто создаем новый индекс и наполняем его. В это время пользователь спокойно ищет данные по старому индексу и не знает, что данные о товаре поменялись. После этого мы просто переключаем индекс, это занимает буквально 10 миллисекунд. Пользователь даже ничего не заметит.
{
"actions" : [
{ "remove" : { "index" : "НАШ_ИНДЕКС_2000_01_01", "alias" : "ИМЯ ИНДЕКСА" } },
{ "add" : { "index" : "НАШ_ИНДЕКС_2022_09_17", "alias" : "ИМЯ ИНДЕКСА" } }
]
}
- Не индексируйте то, что не ищете
Например, есть организация, в организации есть люди, у людей есть машины. Кроме этого, в данных организации у человека указан опыт вождения в годах. Можно искать человека по любым данным — по имени, фамилии, можно искать по личному номеру автомобиля. Но вряд ли кто-то будет искать человека по опыту вождения. Соответственно, индексируйте только то, что ищете. Просто указывайте тип и отключите индексацию поля через {"index": false}:
{ "mappings": { "properties": { "exp": { "type": "integer", "index": false }}}}
- Читайте How-to
How-to — это гайд по оптимизации работы с ES от его разработчиков. Читая его, вы лучше поймете, что и как использовать, а также сможете выполнить ряд оптимизаций для повышения производительности вашего проекта.
В ИТОГЕ
Elasticsearch — хороший инструмент полнотекстового поиска, который поможет ускорить ваш проект и сократить время работы над ним. Но для того, чтобы разобраться во всех его возможностях, потребуется много практики. Особенно если вы хотите построить на его основе NoSQL-хранилище.
ПОЛЕЗНЫЕ ССЫЛКИ
- Доклад «Elasticsearch: искать, фильтровать и не сломать»;
- Статья «Неполнотекстовый поиск: специфичные возможности Elasticsearch для сложных задач»;
- Материал «Фильтруйте свои данные».
статьи по теме
-
ЧитатьUSE CASE — что это, из чего состоит и как избежать ошибок при написании27.06.2024
-
ЧитатьКак перейти с Java на Kotlin при создании веб-приложений? Ресурсы для начала изучения и мнения экспертов07.05.2024
-
ЧитатьИспользование снифферов трафика в тестировании на примере Charles Proxy04.04.2024
-
ЧитатьMediaSoft Java Weekend — 4 доклада с презентациями для Java-разработчиков20.03.2024
-
ЧитатьЛайфхаки при использовании Java29.02.2024
-
ЧитатьБрокеры сообщений — что это, из чего состоят, плюсы и минусы: сравниваем Apache Kafka, Redis и RabbitMQ02.08.2023