Elasticsearch: схема полей для фасетного поиска, фильтра в интернет-магазине

В этой статье я опущу такие подробности работы с Elasticsearch (далее по тексту просто ES), как:

1) Как установить

2) Как подключаться

3) Раскрывать полную схему mapping для товара интернет-магазина

4) Подробное описание всей структуры и всех запросов для получения страницы товаров с результатами поиска и фильтром.

И что-либо еще.

Здесь, как и написано в заголовке, лишь постараюсь описать схему только для полей характеристик товара и как для них делать запросы агрегации и фильтрации.

Предисловие

К написанию статьи пришел после неудачного опыта разработки интернет-магазина на фреймворке и MySQL с десятками тысяч товаров, которые в свою очередь имели несколько десятков характеристик и множество значений для них. Из-за множества запросов для получения значений фильтра товаров и, возможно, абсолютно неправильного структурирования таблиц или по какой-либо другой причине, сайт ужасно тормозил и долго загружался. Дошло до того, что в вебмастере яндекса получал подобную ошибку:

Скрин из интернета.

Сайт разрабатывал не я. Разбираться с ним в дальнейшем не было ни знаний, ни желания. Решил, что впоследствии буду самостоятельно разрабатывать интернет-магазин, но используя другое и нереляционное хранилище данных, а не Mysql. Выбор пал на ES и при изучении понимание структурирования характеристик товаров и получение для них значений, которые впоследствии безболезненно и не затрагивая код можно было бы менять, отняло много времени. Лично мне очень не хватало в русскоязычном интернете абсолютно простых примеров, какие есть, например, для PHP+Mysql.

Все описанное, лишь основано на моем личном опыте и понимании схемы и структуры документов, ориентированной на использовании для построения фасетного поиска в интернет-магазине к которому я пришел при изучении и разработке. Т.е статья рассчитана больше на новичков, которые начали изучать ES.

По сути дела

“Elasticsearch – это распределенный поисковый и аналитический движок на базе Apache Lucene. Полностью с описанием можно ознакомиться на официальном сайте.

Фасетный поиск (фасетная навигация) – поиск товара в разделе, категории или же на странице полнотекстового поиска по характеристикам: цвет, материал, цена, производитель и т.д. Для конечного пользователя – набор фильтров. Каждый фильтр – характеристика. Значения этого фильтра – все возможные значения характеристики. Для интернет-магазина это основная функция поиска, и пользователи ожидают, что она будет работать достаточно быстро.”

В приведенном ниже примере пользователь находится в категории “Люстры” и отфильтровал дополнительно товары в диапазоне цен от 1394 до 42207 руб. и с цветом черный. Было найдено 198 товаров, а на панели фильтров слева перечислены те характеристики, которые содержатся в результатах поиска, а также количество доступных значений, имеющих этот атрибут (количество фасетов):

Перейдя в интернет-магазине в раздел Люстры можно лично опробовать фильтр и повторить действия, описанные выше (на сайте используется ES).

Для создания фасетного поиска в ES есть достаточно мощный инструмент агрегирования. Одной из приятных особенностей агрегации является то, что они могут быть вложенными — другими словами, можно определить агрегации верхнего уровня, которые создают «корзины» (buckets) документов и другие агрегации, которые выполняются внутри этих корзин.

Для упрощения понимания, это в целом похоже на команду SQL GROUP_BY. На основе фильтров обобщаются, группируются документы по какому-то определенному признаку.

Индексирование значений фасета

Перед созданием агрегатов атрибуты документа, которые могут служить фасетами, необходимо проиндексировать в ES. Один из способов индексировать их — перечислить все атрибуты и их значения в одном поле, как в следующем примере:

"facets": { "color": "Черный", "style": "Лофт", "room": "Гостиная", }

Mapping ES при этом должен выглядеть так:

"facets": { "type": "nested", "properties": { "color": { "type": "keyword", }, "style": { "type": "keyword", } "room": { "type": "keyword", } } }

Такой подход может подойти, но для фасетирования в таком случае в запросах придется явно перечислять все имена полей, для которых мы хотим создать агрегацию.

"aggs": { "facets": { "nested": { "path": "facets" }, "aggs": { "color": { "terms": { "field": "facets.color" } }, "style": { "terms": { "field": "facets.style" } }, "room": { "terms": { "field": "facets.room" } }, } } }

Очевидно, что это не очень практично и эффективно при большом количестве характеристик товаров, которые со временем могут изменяться, дополняться. И, например, при удалении, изменении или добавлении новой характеристики товара придется вручную изменить mapping, переиндексировать и изменить запрос, добавив в него новое имя поля.

Вместо этого я пришел к следующему.

Разделил имена и значения фасетов, отправляемых в индекс эластика, следующим образом:

"string_facets": { { "name": "color", "value": "Черный" }, { "name": "color", "value": "Белый" }, { "name": "style", "value": "Лофт" }, { "name": "style", "value": "Техно" }, { "name": "room", "value": "Гостиная" }, { "name": "room", "value": "Спальня" } }

Mapping:

"string_facets": { "type": "nested", "properties": { "name": { "type": "keyword", }, "value": { "type": "keyword", } } }

Для фильтрации и агрегирования такой структуры требуются вложенные фильтры и вложенные агрегации в запросах.

Агрегация:

"aggs": { "aggs_text_facets": { "nested": { "path": "string_facets" }, "aggs": { "name": { "terms": { "field": "string_facets.name" }, "aggs": { "value": { "terms": { "field": "string_facets.value" } } } } } } }

Фильтрация:

"filter": { "nested": { "path": "string_facets", "filter": { "bool": { "must": { { "terms": { "string_facets.name": "color" } }, { "terms": { "string_facets.value": "Черный" } } } } } } }

Это касается характеристик, у которых значения текстовые. Характеристики с числовыми значениями необходимо хранить и анализировать отдельно. Это связано с тем, что числовые характеристики (например, размеры: ширина, длина) иногда имеют огромное количество различных значений. И вместо того, чтобы перечислять все возможные значения, достаточно просто получить минимальное и максимальное значения и отобразить их в виде селектора диапазона или ползунка. Это возможно, только если значения хранятся в виде чисел.

В mapping это будет выглядеть следующим образом:

"number_facets": { "type": "nested", "properties": { "name": { "type": "keyword", }, "value": { "type": "double", } } }

Агрегация.

"aggs_number_facet": { "nested": { "path": "number_facets" }, "aggs": { "name": { "terms": { "field": "number_facets.name" }, "aggs": { "value": { "stats": { "field": "number_facets.value" } } } } } }

При таком подходе отпадает необходимость знать список доступных характеристик во время запроса. Также в любой момент можно просто изменить данные в индексе, удалив или изменив нужные характеристики, при этом без изменения маппинга и запросов.

Ps. Организовав таким образом схему документов и прописав все необходимые запросы, я столкнулся с еще одной проблемой. При фильтрации оставлялись только товары с выбранным значением в фильтре товаров, соответственно нельзя было выбрать несколько значений одного и того же фильтра, что в моем случае влияло на удобство для пользователей. Для описания решения проблемы требуется отдельная статья.

#разработка #поиск #движки #elasticsearch #интернет_магазины

0
15 комментариев
Написать комментарий...
Валентин Потапов

Вошел, не вижу описанного поиска. Что сделал не так ? Может на планшете ваши фильтры недоступны ?
Идею попробую применить для доски недвижимости. Никогда сам не работал с nosql бд, поэтому выглядит перспективно

Ответить
Развернуть ветку
Владислав
Автор

И если не сложно ответить, что за планшет?

Ответить
Развернуть ветку
Валентин Потапов

Galaxy Tab S2

Ответить
Развернуть ветку
Владислав
Автор

Я не смогу сейчас вам ответить, так как надо пересматривать стили и описанные в них разрешения экрана. Однозначно, это ошибка верстки не учтенная при адаптировании сайта. На планшетах и мобильных устройствах кнопка фильтрации находится вдругом конце справа от выбора сортировки.

Ответить
Развернуть ветку
Валентин Потапов

Да, на телефоне нашел.

Еще подумал, что могу у себя в сервисе грузоперевозок такую фичу попробовать настроить. Там тоже относительно много реквизитов у грузов и рейсов

Ответить
Развернуть ветку
Владислав
Автор

Буду действительно очень рад, если описанное вам чем-то поможет)

Ответить
Развернуть ветку
Владислав
Автор

Ну, это уже отдельная проблема верстки) Скажите, пожалуйста, какое разрешение экрана? Кнопка открытия фильтра должна быть с противоположной стороны от выбора "Сортировки".

Ответить
Развернуть ветку
Валентин Потапов

Я даже не знаю где тут разрешение смотреть

Ответить
Развернуть ветку
Валентин Потапов

Тут лично мне не хватает группировок категорий, например, Цвета и свертки и развертки категорий как вложенных папок. Т.е. свертка есть, но поштучная

Ответить
Развернуть ветку
Владислав
Автор

Не понял сообщения от слова совсем. Дополнительная группировка в виде: Если характеристика имеет отношение к цвету, то соотнести это к группе "Цвет"? На прошлой версии сайта было именно так и я не посчитал не посчитал нужным в этой сфере повторять это. Мне показалось это не столь нужным, но критику я услышал, спасибо)

Ответить
Развернуть ветку
Валентин Потапов

Это не критика. Просто есть существенные и несущественные атрибуты и даже категории свойств обьектов. Для каждого они свои. Видеть их все сразу в линейном списке явно более комфортных для мозга 6 это значит напрягать мозг юзера.

Ответить
Развернуть ветку
Владислав
Автор

Касаемо эластика, описанного в статье, описанное вами можно организовать, так как на выходе значений эластик отдает массив данных, которыми можно жонглировать как-угодно, просто для описанного вами будет немного другая схема.

Ответить
Развернуть ветку
Владислав
Автор

Чем лично мне он и понравился впоследствии)

Ответить
Развернуть ветку
Валентин Потапов

Мне показалось или нет, что чатгпт может настраивать коды этих офсетов для нужной мне бд гораздо быстрее, чем я разберусь с его документацией и системой понятий. Вы не пробовали еще это сделать ?

Ответить
Развернуть ветку
Владислав
Автор

Нет, не пробовал и очень сомневаюсь, что ChatGPTна это способен. Возможно я дико ошибаюсь, но по моему личному ощущению на данный момент касаемо всех новостей и материалов про ChatGPT, это больше реклама и хайп. Единственное в чем я пытался его юзать совсем немного, это рерайт статей. Полученные результаты полная чушь. Попробуйте и напишите статью о результате, многим будет интересно.

Ответить
Развернуть ветку
12 комментариев
Раскрывать всегда