Фасетный поиск что это

Фасетные фильтры: как готовить и с чем подавать

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

Понятия, чтобы было понятно

Полнотекстовый поиск — поиск товаров по слову или фразе. Для пользователя — это поле для ввода текста с кнопкой «Найти», которое доступно на любой странице сайта.

Фасетный поиск — поиск товара по нескольким характеристикам: цвету, размеру, объему памяти, цене и т.п. Для пользователя — это набор фильтров. Каждый фильтр связан только с одной характеристикой и наоборот. Значения фильтра — все возможные значения характеристики. Пользователь видит фильтры на странице раздела, категории, на странице с результатами полнотекстового поиска. Когда пользователь выбрал значение, фильтр считается активным.

Поведение фасетных фильтров

Коротко звучит так: фильтр фильтрует товары и фильтрует варианты выбора в других фильтрах.

Фильтрует товары

С этим просто. Пользователь выбрал:

Фильтрует варианты выбора в других фильтрах

«Ну… какие варианты есть — отображается, чего нет — скрывается» — примерно так бизнес описывает поведение фильтров. Звучит логично. На практике это работает так:

Отсюда вытекает универсальное правило: значения фильтра извлекаются из выборки товаров, которая сформирована остальными активными фильтрами.

Каждый активный фильтр имеет свою выборку товаров.

Если у нас N фильтров и:

Источник

Фасетные фильтры: структура и взаимодействие компонент

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

Мы используем vue, vuex, vue-route, поэтому дальше повествование будет в этом контексте. В основе взаимодействия лежат хуки компонент, хранилище состояния, роутер. Такую структуру можно воспроизвести на любом фреймворке с такими же составными частями.

UI и UX фасетного поиска

Что бы что-то найти пользователь может:

сделать поиск по тексту;

для нескольких характеристик выбрать одно или нескольких значений;

для некоторых характеристики установить диапазон значений;

просмотреть следующую страницу результата. Здесь есть варианты: подгрузить результат или посмотреть на отдельной странице;

перейти по ссылке, в которой уже установлены все параметры поиска.

Компоненты UI взаимодействуют по правилам:

Новое значение в поле полнотекстового поиска сбрасывает значения остальных компонент.

Новое значение для характеристики сбрасывает сортировку и пагинацию.

Новое значения сортировки сбрасывает пагинацию.

Категории отличаются характеристиками, поэтому при смене категории одни фильтры исчезают, другие появляются. Это значит, что компоненты динамически включаются или исключаются из алгоритма формирования запроса при смене категории.

Ситуация выглядит так, что компоненты уникальны и связаны уникальными правилами, и выстроить универсальную структуру не получиться. Но мы попробуем.

Структура

UI компоненты и миксины

Компоненты сортировки, пагинации, фильтры по характеристикам, полнотекстовый поиск и остальные используют FilterMixin и реализуют свои методы-хуки, другие детали их реализации касаются UX и не важны для понимания общей схемы.

Сущности бизнес логики

Список компонент, которые формируют критерий.

Список объектов для отображения. Если пагинация постраничная, то список объектов совпадает со списком текущего результата. В случае подгрузки, список содержит объекты от первой страницы до текущей.

Сервисы инфраструктуры

Алгоритм взаимодействия

Инициализация

Инициализация происходит при переходе на страницу поиска или при обновлении страницы. Компоненты создаются и монтируются в DOM. Компоненты, которые могут изменять критерии, регистрируют себя в ProductSearchPage. При регистрации ProductSearchPage добавляет компонент во внутренний репозиторий, другие операции не выполняются.

Затем Vue запускает наблюдатели (watch) в компонентах. ProductGrid наблюдает за роутом. Vue считает, что роут изменен при загрузке страницы или программном изменении, поэтому запускается наблюдатель. Наблюдатель грида обращается к ProductSearchPage, передает ему новый роут для запуска поиска. Важные детали:

Поиск

ProductSearchPage вызывает хук initFilterAndBuildFetchCriteria последовательно для всех зарегистрированных компонент. Он обязан быть у каждого компонента. Хук:

если в роуте есть значение, оно добавляется в критерии;

если в роуте нет значения, а в критериях есть, то удаляется из критериев.

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

ProductSearchPage выполняет поиск через сервис ProdcutService. ProdcutService

получает критерии поиска, создает из них запрос через QueryBuilder;

отправляет запрос в АПИ;

возвращает объект результата.

Применение критерия поиска

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

ProductSearchPage принимает компонент и вызывает в нем хук buildFetchCriteriaFromInternalValue. Хук изменяет критерии в соответствии с нужной стратегией и возвращает новый экземпляр.

Чуть выше я писал “новый роут формирует критерии для поиска с нуля”. Возникает правильный вопрос: зачем строить критерий, почему не строить сразу роут?

Деструктуризация

ProductGrid уничтожается, когда пользователь покидает страницу поиска. Деструктор компонента приводит ProductSearchPage в начальное состояние: нет результата, нет критериев и т.п. При повторном входе на страницу ProductSearchPage будет инициализирован из роута по новой.

Динамическое подключение фильтров

У нас нет фильтров привязанных к категориям. Таковы бизнес требования. Но ничего не мешает их реализовать. Наши категории отображаются в URL path. При выборе категории в фильтре будет сформирован новый роут с новым path. Применение нового роута приведет к перезагрузке страницы, запуститься инициализация. Можно до инициализации получить список характеристик, по которым нужна фильтрация, и создать нужные компоненты. Далее по схеме описанной выше. Ну… это в теории =)

Возможные улучшения

В текущей реализации много чего можно улучшить:

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

Вынести обработку отображения страниц в виде пагинации или подгрузки в отдельную стратегию.

Избавить ProductGrid от управление пагинацией.

Оптимизировать структуру ProductSearchPage, потому что список объектов для отображения и текущий результат пересекаются по объектам.

Источник

Фасетный поиск что это

Курс предназначен для администраторов интернет-магазинов, работающих на базе системы «1С-Битрикс: Управление сайтом». Изучение курса необходимо при работе с продуктом редакции Малый бизнес и выше при организации торговых операций через Интернет.

Поэтому, если у вас нет возможности обновить продукт и/или выполнить конвертацию, то описание работы с модулем Интернет-магазин смотрите в курсе Магазин до конвертации (до версии 15.0).

Читайте также:  какое количество алкоголя можно провозить в самолете в багаже в турцию

Кроме того, доступны для скачивания следующие материалы с описанием старого функционала магазина:

Курс Администратор. Бизнес завершает группу административных курсов по Bitrix Framework.

Начальные требования

Необходимый минимум знаний для изучения курса:

Неплохо было бы иметь базовые навыки установки и администрирования *nix-систем.

У нас часто спрашивают, сколько нужно заплатить

Ещё у нас есть Академия 1С-Битрикс, где можно обучиться на платной основе на курсах нашей компании либо наших партнёров.

Баллы опыта

уроке.

Тесты и сертификат

После изучения курса вам будет предложено пройти итоговые тесты на сертификацию.
Для доступа к итоговым тестам данного курса необходимо успешно сдать итоговые тесты курсов Администратор. Базовый и Администратор. Модули.
При успешной сдаче последовательности тестов на странице Моё обучение можно просмотреть результат обучения и загрузить сертификат в формате PDF.

Для преподавания оффлайн

Если данный курс берётся в качестве основы для оффлайного преподавания, то рекомендуемая продолжительность: 2 дня (16 академических часов).

Если нет интернета

Скачать материалы курса в формате EPUB. Файлы формата EPUB Чем открыть файл на
Android:
EPUB Reader
CoolReader
FBReader
Moon+ Reader
eBoox

iPhone:
FBReader
CoolReader
iBook
Bookmate

Windows:
Calibre
FBReader
Icecream Ebook Reader
Плагины для браузеров:
EpuBReader – для Firefox
Readium – для Google Chrome

Как проходить учебный курс?

Источник

История разработки фасетного поиска средствами PHP

Как экспериментальный Pet Project дошел до production и на что способны современные версии языка PHP. Немного о проблематике фасетного поиска в части построения агрегатов.

Если ваша первая реакция: «Почему не на Sphinx/ElasticSearch/etc?», не торопитесь с выводами. Воспринимайте изложенное как интересный исследовательский опыт в области возможностей языка и его оптимизаций.

Спойлер: пришлось даже написать порт на GoLang, чтобы лучше понять пути оптимизации кода.

Агрегаты

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

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

У каждого значения фильтра необходимо отобразить количество товаров, которое за ним скрывается. Все это пересечения множеств, вычисление которых происходит при помощи агрегатов (по аналогии с ElasticSearch aggregations)

Чтобы реализовать третий вариант, необходимо исключить влияние значения поля фасета на другие значения этого поля при фильтрации в агрегате, условно назову это «Self Filtering».

Если алгоритм не ограничивает Self Filtering, при этом скрывает недоступные варианты, то в кейсе с телефонами для поля «цвет» останется доступным только значение «белый», так как «синий», «черный» не совместимы с этим выбором. Это неудобно, так как пользователь не сможет изменить выбор цвета. Если мы ограничим Self Filtering, то при выборе одного цвета, остальные варианты цветов останутся доступными для выбора.

Эта особенность вынуждает строить агрегаты для каждого поля отдельно, исключая из списка фильтры по этому полю.

Более подробно эта проблематика разобрана в статье “Фасетные фильтры: как готовить и с чем подавать”.

Примеры фасетного поиска в известных интернет-магазинах:

На этих примерах видны различные варианты реализации.

Скрытие лишних значений проще реализовать на ElasticSearch. Часто в нем используют сквозной индекс по всем товарам, но в этом случае не известен полный стартовый набор фильтров для категории. В ElasticSearch можно использовать отдельные индексы под каждую категорию, иногда такой подход более эффективен. Так же на ElasticSearch проще работать с динамическими наборами свойств.

Отключение и счетчик товаров проще реализовать на Bitmap или Sphinx. Сложнее обстоят дела, когда наборы свойств заранее не известны.

При должном усердии любое поведение можно реализовать и на Sphinx и на Elastic.

Кстати, в SuperJob фасетный поиск организован на Sphinx, еще ребята делали собственные патчи в код самого Sphinx, но это тема отдельной статьи или даже доклада.

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

Возникновение задачи

На тот момент я работал в компании ПартКом (оптовая продажа автозапчастей), которая на первый взгляд, совершенно не про эксперименты IT и инновации. Большой Enterprise с 80 млн. SKU номенклатуры, логистикой, складами, доставкой и прочими прелестями жизни.

Внезапно образовался свободный промежуток времени и ресурсы, что позволило взяться за идею запуска MVP каталога автоаксессуаров (щетки, лампочки, коврики, масло и тп), все то, что удобно покупать на основе характеристик.

Устройство инфраструктуры

Сервис обработки прайс-листов обрабатывал огромные объемы данных и загружал их в БД, в минуту заливались десятки миллионов строк прайс-листов.

Описания товаров и их характеристики заполнялись в отдельном сервисе специально обученными людьми.

Из общего объема номенклатуры нас интересовало порядка 300 тыс наименований товаров, которые были в наличии на складах. Эти товары были разбросаны по 100 категориям. В каждой категории дерева каталога могли находиться несколько сущностей «Продукт».

«Продукт» определял набор допустимых атрибутов и их значений для определенного вида товаров (например: щетки могут иметь тип/размер/применяемость). У каждого продукта был четко определенный и структурированный набор атрибутов. Продукты в одной категории являлись родственными, часть их атрибутов пересекалась. В атрибутах часто встречались длинные перечисления, а также они могли содержать сразу несколько значений из доступного списка (MultiValue).

Товары привязывались к «продуктам» по номенклатуре и так же редактировались в каталоге. Товар содержал фактические значение атибутов, можно представить как объект одного из классов «продукт».

Сервис каталога умел возвращать данные о категориях, товарах и продуктах по JSON API, при этом он не держал существенную нагрузку, был исключительно внутренним информационным сервисом.

Решение

Первая неделя была потрачена на дополнительные исследования. Провели эксперименты со Sphinx и ElasticSearch, концепцией bitmask + redis/etc, вариант реализации в БД на SQL не рассматривался.

Sphinx

Отличное решение, но не очень хорошо ложилось в предметную область из-за динамической структуры свойств групп товаров, которая была доступна только по JSON API. Сложность построения специфических агрегатов не добавляла энтузиазма.

У нас уже был подобный сервис с фасетами на Sphinx, который достался по наследству и вызывал много сложностей.

ElasticSearch

Еще одно отличное решение, но вспоминаем, что нам нужно загружать от 20 млн строк в минуту и это совершенно нетривиальная задача. При разработке сервиса хранения цен и остатков мы экспериментировали с загрузкой в InnoDb, TokuDb, RocksDb, MongoDb, ElasticSearch. На тот момент выбрали InnoDb как самый простой и достаточно быстрый движок MySQl,

Читайте также:  по каким дорогам можно ездить на мопеде до 50 кубов

Bitmask

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

У нас было много значений-списков и превращать их в битовые маски не очень тривиальная задача, более того размер списков не был прогнозируем (могли быть и по 500 вариантов). В этом случае легко ошибиться с размером битовой маски и выйти за ее пределы. Еще атрибуты могли содержать одновременно несколько значений.

Задачу можно было решить с использованием любого из этих инструментов, но это было достаточно сложно, долго и дорого.

Разработать высококлассную архитектуру, развернуть надежную кластерную инфраструктуру ElasticSearch, настроить мониторинги и алерты, выступить с докладом на конференции 🙂

Продуктовый подход

Поскольку предпринимаемые усилия были маленьким внутренним стартапом, кроме инженерного взгляда на процесс, есть еще и продуктовый, у которого совершенно свои метрики, правила запуска и тестирование гипотез, ROI, unit-экономика. Спасибо продуктовой и стартап движухе, которую устраивает Аркадий Морейнис, это сильно изменило мои взгляды на разработку, продукты и стартапы.

Мы находились в высококонкурентном сегменте с низкой маржинальностью, где абсолютно отсутствовали гарантии окупаемости «правильного подхода». Закопать тонны ресурсов в проект, который может не полететь бы ло бы очень обидно.

Хотелось протестировать гипотезу как можно быстрее, по возможности, пойти обходным и коротким путем. Решили, что не будем складывать данные о ценах и наличии в механизм фасетного поиска. Будем оперировать только данными о свойствах товаров. Это открыло возможности для маневра.

Вдохновляли ребята из Badoo и ManyChat, у которых отлично получались делать кастомные решения и они оказывались более эффективными как с точки зрения производительности, так и экономически.

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

На первоначальную доработку библиотеки ушла пара свободных вечеров. После нескольких итераций по оптимизации производительности получился относительно компактный и достаточно быстрый индекс. Код обрабатывал данные 200,000 товарах с 10 атрибутами менее 0,2 c. Размер категории каталога не превышал 20,000 товаров, это давало огромный запас по производительности. После того как я убедился, что библиотека справляется с нужной нагрузкой, взяли ее на вооружение для запуска MVP на рабочем проекте.

Решение на PHP библиотеке и на ElasticSearch крутились параллельно на тестовом