Обзор фильтров для интернет-магазинов
Для MiniShop планируется сделать фильтры встроенные в него. Вопрос как и каким образом их делать? Я разбирался в фильтрах mFilter2, пробовал делать mapFilter и, сейчас, искал в интернете реализации фильтров. Насколько нашел есть 4 способа реализации фильтра. Под катом их обзор.
1) SQL-запросы. При выборе какого-то фильтра формируется SQL-запрос, который и вытаскивает из базы нужные товары. Например Фильтры в интернет-магазине. Урок 4. Пишем базовый php-код.
Но здесь, недостаток. Такие фильтры пишутся для конкректных наборов товаров и фильтров. То есть их не включишь в minishop3. У каждого магазина свой набор и им нужно писать такие фильтры самостоятельно.
В mv-framework эта задача, например, облегчена но все равно нужно программирование https://mv-framework.ru/filtratciia/.
В принципе можно написать оболочку, что будет конструировать SQL-запросы для опций minshop3, но это надо изучать и писать. По скорости работы таких фильтров ничего сказать не могу.
2) Аутсорсинг фильтров Фильтры можно просто отдать на аутсорс. Например https://multisearch.io/. Не пробовал, но возможность интересная. Во всяком случае, там реализованно много фишек для более лучшего фильтра. Например исправление ошибок и опечаток и исправление раскладки.
3) mFilter2.
mFilter2 не строит SQL-запросы для фильтрации, а собирает нужные опции товаров в массив типа
suggestions получает отправляя в функцию Filter тот же выбор фильтров, добавляя в него значение для которого считается suggestions.
mFilter2 тупит в основном на получении опции товаров в массив. Если массив закешировать (Кешируем mFilter2), то фильтрация и suggestions работают быстро.
На каталоге с 11706 товаров.
Сбор данных для фильтра из базы в массив занимает 39.1501031с
фильтрация с suggestions 0.8021080с.
Особенность mFilter2, что массив опции товаров создается только для товаров в выбранном каталоге. Если разбить товары по каталогам с 10 000 товоров в каталоге, то кешированный mFilter2 может быстро работать и на 1 000 000 товаров думаю. Но не пробовал конечно. Но 50 000 товаров точно работать будут.
4) mapFilter .
В mapFilter(https://github.com/touol/mapFilter) пробовал реализовать такую идею.
База некоторые операции делает быстрее кода. А собирание id в масcив это просто where `значение опции` IN ('значение1','значение2') а пересечение массивов — это INNER JOIN.
mapFilter делает в базе 4 таблицы: mfOption -> список опций, mfOptVal -> таблица id опции — значение опции, mfResVal — > id значения опции — id товара, mfCategoryChild -> список id товаров в категории.
Для опции for_whom id значения 607 SQL-запрос, фильтрующий товары, будет:
На каталоге с 11706 товаров.
Загрузка каталога с фильтром
Возмозно запросы можно было как то оптимизировать, но сейчас на ум ничего не приходит :-(.
Скорость зависит от размера таблицы mfResVal — > id значения опции — id товара. Сейчас там 200 000 строк. Это от 27т. товаров. При 400 000 строк скорость будет около 6-10с.
Выводы
Я думаю для minishop будет лучше просто обновить mFilter2. И не включать фильтры в поставку. Возможно у Вас другое мнение. И возможно Вам есть, что добавить по методам фильтрации. Буду признателен, если напишете в комментариях.
PS
Чтоб найти что-то в интернете нужно знать нужные слова :-). Проблема фильтрации в интернет-магазинах называется фасетный поиск. И https://yandex.ru/search/?text=Фасетный поиск дает уже кучу информации как его организовывать. В основном применяются специальные базы данных Sphinx/ElasticSearch/etc.
Тонкости фасетного поиска на массивах, таких же как применил @Василий Наумкин в mFilter2, описаны в статье История разработки фасетного поиска средствами PHP. И текущему автору mSearch2 стоит ее прочитать :-).
1) SQL-запросы. При выборе какого-то фильтра формируется SQL-запрос, который и вытаскивает из базы нужные товары. Например Фильтры в интернет-магазине. Урок 4. Пишем базовый php-код.
Но здесь, недостаток. Такие фильтры пишутся для конкректных наборов товаров и фильтров. То есть их не включишь в minishop3. У каждого магазина свой набор и им нужно писать такие фильтры самостоятельно.
В mv-framework эта задача, например, облегчена но все равно нужно программирование https://mv-framework.ru/filtratciia/.
В принципе можно написать оболочку, что будет конструировать SQL-запросы для опций minshop3, но это надо изучать и писать. По скорости работы таких фильтров ничего сказать не могу.
2) Аутсорсинг фильтров Фильтры можно просто отдать на аутсорс. Например https://multisearch.io/. Не пробовал, но возможность интересная. Во всяком случае, там реализованно много фишек для более лучшего фильтра. Например исправление ошибок и опечаток и исправление раскладки.
3) mFilter2.
mFilter2 не строит SQL-запросы для фильтрации, а собирает нужные опции товаров в массив типа
$filter = [
'таблица|опция1'=>[
'значение1 опции1'=>[
'id товара с этой опцией1',
'id товара с этой опцией2',
'id товара с этой опцией3',
и т.д.
],
'значение2 опции1'=>[
'id товара с этой опцией1',
'id товара с этой опцией2',
'id товара с этой опцией3',
и т.д.
],
и т.д.
],
и т.д.
];Теперь если в фильтре выбрано 'значение1 опции1','значение3 опции1' и 'значение1 опции3', то mFilter2 собирает массив1 все id товаров для 'значение1 опции1' добавляет все id для 'значение3 опции1', собирает массив2 id 'значение1 опции3' и делает уникальное пересечение этих массивов( то есть собирает массив из id которые есть и в массив1 и в массив2. Причем берет только те id которых в нем еще нет). делается это в функции Filter.suggestions получает отправляя в функцию Filter тот же выбор фильтров, добавляя в него значение для которого считается suggestions.
mFilter2 тупит в основном на получении опции товаров в массив. Если массив закешировать (Кешируем mFilter2), то фильтрация и suggestions работают быстро.
На каталоге с 11706 товаров.
Сбор данных для фильтра из базы в массив занимает 39.1501031с
фильтрация с suggestions 0.8021080с.
Особенность mFilter2, что массив опции товаров создается только для товаров в выбранном каталоге. Если разбить товары по каталогам с 10 000 товоров в каталоге, то кешированный mFilter2 может быстро работать и на 1 000 000 товаров думаю. Но не пробовал конечно. Но 50 000 товаров точно работать будут.
4) mapFilter .
В mapFilter(https://github.com/touol/mapFilter) пробовал реализовать такую идею.
База некоторые операции делает быстрее кода. А собирание id в масcив это просто where `значение опции` IN ('значение1','значение2') а пересечение массивов — это INNER JOIN.
mapFilter делает в базе 4 таблицы: mfOption -> список опций, mfOptVal -> таблица id опции — значение опции, mfResVal — > id значения опции — id товара, mfCategoryChild -> список id товаров в категории.
Для опции for_whom id значения 607 SQL-запрос, фильтрующий товары, будет:
SELECT *
FROM `modx_mapfilter_category_childs` AS `mfCategoryChild`
JOIN `modx_mapfilter_options_resource_values` `for_whom` ON for_whom.resource_id = mfCategoryChild.resource_id
JOIN `modx_site_content` `modResource` ON modResource.id = mfCategoryChild.resource_id
JOIN `modx_ms2_products` `Data` ON Data.id = mfCategoryChild.resource_id
WHERE `for_whom`.`value_id` IN ('607')
ORDER BY modResource.menuindex ASC
LIMIT 20;В тестах выяснились следующие результаты: На каталоге с 11706 товаров.
Загрузка каталога с фильтром
0.0007010: mapFilter start
0.5347850: getFilter
1.0177059: getFilter getOptions
0.0010240: getFilter prepareRequest
0.2693081: getSuggestions2
0.0229859: getSuggestions2
0.2386901: getSuggestions2
0.0774949: getSuggestions2
0.0205290: getSuggestions2
0.1115170: getSuggestions2
0.0648751: getSuggestions2
0.0280590: getSuggestions2
0.0214498: getSuggestions2
0.0044181: getSuggestions2
0.0487919: getSuggestions2
0.0001740: getFilter getSuggestions2
0.0159881: filter init
0.2298169: filter getResources
0.0150809: filter getOptions
2.7234099: Total time
2 097 152: Memory usageфильтрация0.0000539: filter init
0.1498890: filter getResources
0.0353141: filter getOptions
0.2935281: getSuggestions2
0.0499680: getSuggestions2
0.2742038: getSuggestions2
0.1711061: getSuggestions2
0.2710280: getSuggestions2
0.2764280: getSuggestions2
0.1371169: getSuggestions2
0.2698560: getSuggestions2
0.0342002: getSuggestions2
0.0048778: getSuggestions2
0.0064230: getSuggestions2
0.0001552: getFilter getSuggestions
1.9741569: Total time
2 097 152: Memory usageТо есть, как выяснилось, mapFilter работает медленнее кешированного mFilter2. Возмозно запросы можно было как то оптимизировать, но сейчас на ум ничего не приходит :-(.
Скорость зависит от размера таблицы mfResVal — > id значения опции — id товара. Сейчас там 200 000 строк. Это от 27т. товаров. При 400 000 строк скорость будет около 6-10с.
Выводы
Я думаю для minishop будет лучше просто обновить mFilter2. И не включать фильтры в поставку. Возможно у Вас другое мнение. И возможно Вам есть, что добавить по методам фильтрации. Буду признателен, если напишете в комментариях.
PS
Чтоб найти что-то в интернете нужно знать нужные слова :-). Проблема фильтрации в интернет-магазинах называется фасетный поиск. И https://yandex.ru/search/?text=Фасетный поиск дает уже кучу информации как его организовывать. В основном применяются специальные базы данных Sphinx/ElasticSearch/etc.
Тонкости фасетного поиска на массивах, таких же как применил @Василий Наумкин в mFilter2, описаны в статье История разработки фасетного поиска средствами PHP. И текущему автору mSearch2 стоит ее прочитать :-).
Комментарии: 31
Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
Кажется ошибся. Тема для minishop2
Тот же подход но для Redis habr.com/ru/company/oleg-bunin/blog/316652/
Там не тот же подход. Там вариация на тему массива значений опций, как в mFilter2, только в базе Redis.
Вариации Sphinx, ElasticSearch требуют vds или отдельного выделенного сервера или даже кластера серверов. Они прожорливы. ElasticSearch требует минимум 60гб на сервере. OpenSearch у меня не запускался пока размер диска на виртуалке до 100гб не поднял.
OpenSearch можно на https://aws.amazon.com/ru/opensearch-service/ подключить. Есть бесплатный тариф, но не понятно насколько его хватает. Платный тариф 1604,83 USD
> 3.mFilter2
Странно что у вас получились цифры под 39 сек для 11к товаров. — у вас что-то не то с БД.
Тк я тестировал 200к ресурсов с 20ю опциями и фильтрация + выборка проходила примерно 1..3 сек.
Во вторых, чтобы ускорить выборку даже в самом msProduct minishop2 необходимо переписывать фильтр на прямые запросы. тк сжирает ресурсы и время в основном xpdo modx.
Я писал свой магаз тестовый с нуля и делал другую структуру данных. и у меня получались выборки опций до 0,5 сек для 200к товаров с 20...40 опциями. А из кеша вообще копейки.
У меня 400 флажков. А у тебя сколько?
БД стандартное.
Поэтому у меня в 20 опциях я делал примерно то ли 10 то ли 20 значений. те 20 опций * 10 значений — скромные 200 флажков (как вы выражаетесь).
Успеха в поиске решений
Вообще тестовая база у меня от людей которые расширили msProductData через msfieldsmanager в том числе и json полями. У меня собрать с каталога с msProductData на тестовой инсталяции уходит 39с.
У них фильтр работает по полям msOption и собрать данные уходит примерно в течении 10с.
У них шаред хостинг бегет.
То есть никаких 1-3с на 200к товаров не может быть на шаред хостингах.
Что за различия? Как-то оптимизировали базу?
В станданртной ситуации mFilter2 не может обработать 40 000 000 записей быстро.
Колитесь что Вы сделали? :-)
В результате фильтрации в выборрку попадало около 10к ресурсов. у меня в корневом каталоге. те по всем товарам + 1...2 опции на фильтрацию и получал 10к
А по БД вроде ничего не делал. это был бегет и все стандартно было тк нужно было тестануть различия в скорости.
У меня там было 200к ресурсов и 4мл записей опций.
Если вернусь к своему магазину (он небыл выпушен в общественный релиз), возможно подниму тему скорости и оптимизации. но это в след. году. тогда и протестить еще раз смогу, тк я повернут на скорости, и хочется точно оценить масштаб. может и в правду на mfilter это было на 5...10 сек… (уверен я погорячился про 3 секунды), тк я помню что минишоповский я тестировал потом только через выборки msProduct и + своего сниппета.
будет время — подниму бекапы.
но там точно я не видел таких цифр под 30 сек.
В коде msearch
То есть, значения собираются для каждой опции отдельно. 12 раз пробегается по каталогу из 11к товаров.Блин 1 раз пробегается :-(
Вот я повернут на объективности. Обманывая других часто обманываешь и себя. А это уже чревато. Лучше честно признать ошибку и придумать новое лучшее. Это не к Вам если что. Просто лирика про себя :-).
Скорость отдачи страницы больше секунды это уже плохо. Тем более если страница для покупателей.
Спасибо что поделились своим опытом. Будет интересно взглянуть на результаты Ваших тестов.
тк с моей стороны не было предложений и решений этих вопросов.
Как только будут, будут и решения. )
по оптимизации буду держать в голове тему.
Возможно, из-за этого такая цифра.
Которые так просто на любом PHP хостинге не запустишь. А это необходимо, если ты поставляешь дополнение для работы в MODX.
Текущему автору — возможно. А я писал mFilter2 в 2013 году и прочитать эту заметку не мог. Теперь могу, так что сохранил ссылку в закладках на будущее, спасибо!
P.S. Кстати, я продолжаю писать фасетный поиск, но уже на Vue и не универсально, а под проект. Самый свежий можно потыкать вот здесь — lehre.lu/map
Так получилось что в FacetSearch который я только что написал 90% кода в js файле скопированно из mSearch2.
По лицензии я вроде могу это делать? Как вы к этому относитесь?
Просто js код я придумать лучше не могу :-(. PHP часть совершенно другая.
- Использование jquery большая часть разработчиков считает излишним в 2022. Код давно пишут на чистом JS.
- Для использования цифрового слайдера и авто подсказок используется jquery UI. Это лишняя устаревшая зависимость. Есть плагины, которые реализуют подобное качественнее. Кроме того слайдер не работает на телефонах.
- Привязка к ID на страницах. Тоже можно к проблемам отнести. Переиспользовать код на странице несколько раз не получится. Сейчас подобные вещи обычно к data атрибутам и классам привязывают.
- Сам по себе код большой, объемный трудночитаемый. В современном мире разработки обычно принято разбивать код на логические куски и выносить в отдельные модули.
Это я кратко описал, то что по-быстрому вспомнил. Если завяжется дискуссия — думаю еще много что всплывет. В общем проект очень хороший для своего времени. Но он с тех пор практически не развивается и в 2022 несколько устарел.а на чистом надо целых 4 строки
не знал что jquery UI не работает на телефонах. Какой компонент посоветуете? И вообще под телефоны надо для фильтров верстку другую. Потом думать буду.
От ID я избавился, но переиспользовать код не получиться. Адресная строка одна и 2 фильтра в нее писать не могут. Я начал делать для 2 и более фильтров на странице, но отказался и отменил изменения.
если вникнуть в код то на вполне логические блоки разбито. Можно разбить и по другому, но не факт что получиться лучше. Тем более у меня с этим не айс.
В магазине его поддерживает Илья Уткин, так что спрашивать нужно у него.