Обзор фильтров для интернет-магазинов

Для 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-запросы для фильтрации, а собирает нужные опции товаров в массив типа
$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 стоит ее прочитать :-).
Александр
18 октября 2022, 17:46
modx.pro
4
955
+4
Поблагодарить автора Отправить деньги

Комментарии: 31

Николай Савин
18 октября 2022, 18:32
0
Для MiniShop3 планируется сделать фильтры встреенные в него
Откуда такая информация?
Лёша
19 октября 2022, 06:31
0
И не включать фильтры в поставку.
А что бы не включить? Если не надо, можно не использовать)
    Лёша
    19 октября 2022, 06:36
    0
    Например multisearch.io/. Не пробовал, но возможность интересная. Во всяком случае, там реализованно много фишек для более лучшего фильтра. Например исправление ошибок и опечаток и исправление раскладки.
    Для поиска советую яндекс поиск для интернет магазинов. Тоже исправляет ошибки, но бесплатно.
      Александр
      19 октября 2022, 06:54
      0
      Для поиска советую яндекс поиск для интернет магазинов
      У яндекса нет фасетного поиска. То есть фильтров нет
        Павел Голубев
        19 октября 2022, 13:05
        0
        Да и индексирует новые товары крайне медленно. А дефолтный внешний вид — страх и ужас.
        Павел Голубев
        19 октября 2022, 14:57
        0
        Можно попробовать вариант заранее просчитывать все возможные пересечения для фасетного фильтра и хранить эти варианты в кеш-таблице, которую в mysql сделать in-memory. Кажется так работает «умный фильтр» в Битрикс www.intervolga.ru/blog/projects/bitrix-smart-filter-citilink-style/

        Тот же подход но для Redis habr.com/ru/company/oleg-bunin/blog/316652/
          Александр
          20 октября 2022, 05:20
          +1
          заранее просчитывать все возможные пересечения для фасетного фильтра
          Тут одна проблема. Не знаю точную формулу, но знаю что кол-ко комбинаций пропорционально n!.. То есть, если кол-во флажков в фильтре около 200, то кол-во всех возможных комбинаций(ты назвал пересечения, но правильней комбинации. Если я правильно понял твою мысль :-)) около 200!.. Это 7886578673 6479050355 2363213932 1850622951 3597768717 3263294742 5332443594 4996340334 2920304284 0119846239 0417721213 8919638830 2576427902 4263710506 1926624952 8299311134 6285727076 3317237396 9889439224 4562145166 4240254033 2918641312 2742829485 3277524242 4075739032 4032125740 5579568660 2260319041 7032406235 1700858796 1789222227 8962370389 7374720000 0000000000 0000000000 0000000000 0000000000 00000 комбинаций. Не знаю как это работает в Битрих, но наверно до 200 флажков они не доходят. Максимум 50. И кеш по отзывам строится у них в течении 8 часов.
          Тот же подход но для Redis habr.com/ru/company/oleg-bunin/blog/316652/
          Там не тот же подход. Там вариация на тему массива значений опций, как в mFilter2, только в базе Redis.
          Допустим, у нас в Redis-е для Samsung-а есть вот 4 товара — 201, 202, 203, 204, а для Philips — 301, 302, 303. Ключ, соответственно, содержит ID рубрики, фильтр и вариант фильтра. Если пользователь выбирает в панели фильтров Samsung, мы запрашиваем в Redis, получаем ID товаров, отдаем их в компонент списка товаров, и наш компонент отображает 4 товара. Если пользователь выбирает еще производителя Philips, мы и делаем два запроса в Redis, берем объединение этих множеств и, соответственно, показываем семь элементов в нашем каталоге.
            Павел Голубев
            20 октября 2022, 12:10
            0
            ок, тогда можно рассмотреть типа такой штуки manticoresearch.com. Фасетный поиск из коробки manual.manticoresearch.com/Searching/Faceted_search и php клиент есть github.com/manticoresoftware/manticoresearch-php
              Александр
              20 октября 2022, 14:40
              0
              Manticore Search вариация Sphinx. Вчера пробовал установить OpenSearch вариацию ElasticSearch. Не поднял. Не справился с настройкой Docker :-(. Дальше без докера попробую, но пока некогда. Через неделю две пробовать буду.
              Вариации Sphinx, ElasticSearch требуют vds или отдельного выделенного сервера или даже кластера серверов. Они прожорливы. ElasticSearch требует минимум 60гб на сервере. OpenSearch у меня не запускался пока размер диска на виртуалке до 100гб не поднял.
              OpenSearch можно на https://aws.amazon.com/ru/opensearch-service/ подключить. Есть бесплатный тариф, но не понятно насколько его хватает. Платный тариф 1604,83 USD
          Алексей Смирнов
          20 октября 2022, 15:30
          0
          Вот вам материал еще подумать:
          > 3.mFilter2
          Странно что у вас получились цифры под 39 сек для 11к товаров. — у вас что-то не то с БД.
          Тк я тестировал 200к ресурсов с 20ю опциями и фильтрация + выборка проходила примерно 1..3 сек.
          Во вторых, чтобы ускорить выборку даже в самом msProduct minishop2 необходимо переписывать фильтр на прямые запросы. тк сжирает ресурсы и время в основном xpdo modx.
          Я писал свой магаз тестовый с нуля и делал другую структуру данных. и у меня получались выборки опций до 0,5 сек для 200к товаров с 20...40 опциями. А из кеша вообще копейки.
            Александр
            20 октября 2022, 15:57
            0
            я тестировал 200к ресурсов с 20ю опциями
            С 20 опциями или с 20 флажками?

            У меня 400 флажков. А у тебя сколько?
            у вас что-то не то с БД.
            БД стандартное.
              Алексей Смирнов
              20 октября 2022, 16:06
              0
              Я про опции, тк кол-во значений (флажков) не особо влияют на результат.
              Поэтому у меня в 20 опциях я делал примерно то ли 10 то ли 20 значений. те 20 опций * 10 значений — скромные 200 флажков (как вы выражаетесь).
                Александр
                20 октября 2022, 16:10
                0
                как вы выражаетесь
                Из правил modx.pro
                Здесь все друзья и обращение на «ты» говорит только об этом. Не нужно вычурной вежливости, мы люди простые.
                  Алексей Смирнов
                  20 октября 2022, 16:15
                  0
                  Если я тебя задел этим (что на вы обратился), извиняюсь. Я привык к не знакомым людям в реале и не в реале обращаться на Вы.
                  Успеха в поиске решений
                    Александр
                    20 октября 2022, 16:22
                    0
                    Просто следую правилам :-) Могу Вас на Вы называть.
                  Александр
                  20 октября 2022, 16:20
                  0
                  Интересно что тогда влияет???
                  Вообще тестовая база у меня от людей которые расширили msProductData через msfieldsmanager в том числе и json полями. У меня собрать с каталога с msProductData на тестовой инсталяции уходит 39с.
                  У них фильтр работает по полям msOption и собрать данные уходит примерно в течении 10с.
                  У них шаред хостинг бегет.
                  То есть никаких 1-3с на 200к товаров не может быть на шаред хостингах.
                  Что за различия? Как-то оптимизировали базу?
                    Александр
                    20 октября 2022, 16:28
                    0
                    200к товаров на 200 значений это 40 000 000 записей в базе.
                    В станданртной ситуации mFilter2 не может обработать 40 000 000 записей быстро.
                    Колитесь что Вы сделали? :-)
                      Александр
                      20 октября 2022, 16:40
                      0
                      Так есть один вариант. mFilter2 при запросе собирает информацию с выбранного каталога товаров, а не со всей базы данных. Если 200к товаров разбросанны по каталогам с 4000 товаров, то фильтрация как раз и 1-3сек будет :-)
                        Алексей Смирнов
                        20 октября 2022, 16:57
                        0
                        Ну да, в категориях находиться около 20...60к товаров (разные паренты), а не все 200.
                        В результате фильтрации в выборрку попадало около 10к ресурсов. у меня в корневом каталоге. те по всем товарам + 1...2 опции на фильтрацию и получал 10к
                        А по БД вроде ничего не делал. это был бегет и все стандартно было тк нужно было тестануть различия в скорости.
                        У меня там было 200к ресурсов и 4мл записей опций.
                        Если вернусь к своему магазину (он небыл выпушен в общественный релиз), возможно подниму тему скорости и оптимизации. но это в след. году. тогда и протестить еще раз смогу, тк я повернут на скорости, и хочется точно оценить масштаб. может и в правду на mfilter это было на 5...10 сек… (уверен я погорячился про 3 секунды), тк я помню что минишоповский я тестировал потом только через выборки msProduct и + своего сниппета.
                        будет время — подниму бекапы.
                        но там точно я не видел таких цифр под 30 сек.
                          Александр
                          20 октября 2022, 17:30
                          0
                          39с это из-за того что на тестовой инсталяции значения из msProductData брал.
                          {'!mFilter2' | snippet : [
                                          'showLog'=>1,
                                          'suggestions'=>1,
                                          'parents'   =>  $_modx->resource.id,
                                          'cacheTime'=>600,
                                          'limit'=>100,
                                          'suggestionsMaxFilters'=>10000,
                                          'filters'=>'
                                          ms|size:default,
                                          ms|insert:default,
                                          ms|material:default,
                                          ms|style:default,
                                          ms|metal_color:default,
                                          ms|sample_material:default,
                                          ms|insert_color:default,
                                          ms|insert_form:default,
                                          ms|cover:default,
                                          ms|appointment:default,
                                          ms|weaving_type:default,
                                          ms|for_whom:default',
                                      ]}
                          В коде msearch
                          foreach ($filters as $table => &$fields) {
                          				$method = 'get' . ucfirst($table) . 'Values';
                          				$keys = !empty($duplicates[$table])
                          					? array_diff(array_keys($fields), array_keys($duplicates[$table]))
                          					: array_keys($fields);
                          				if (method_exists($this->filtersHandler, $method)) {
                          					$fields = call_user_func_array(array($this->filtersHandler, $method), array($keys, $ids));
                          То есть, значения собираются для каждой опции отдельно. 12 раз пробегается по каталогу из 11к товаров.
                          Блин 1 раз пробегается :-(
                          Вот я повернут на объективности. Обманывая других часто обманываешь и себя. А это уже чревато. Лучше честно признать ошибку и придумать новое лучшее. Это не к Вам если что. Просто лирика про себя :-).
                          Скорость отдачи страницы больше секунды это уже плохо. Тем более если страница для покупателей.
                          Спасибо что поделились своим опытом. Будет интересно взглянуть на результаты Ваших тестов.
                            Алексей Смирнов
                            20 октября 2022, 17:39
                            0
                            У меня тоже есть вопросы к msFilter2 но я их пока стараюсь не озвучивать )
                            тк с моей стороны не было предложений и решений этих вопросов.
                            Как только будут, будут и решения. )
                            по оптимизации буду держать в голове тему.
                              Александр
                              20 октября 2022, 17:51
                              0
                              Чтобы решать вопросы надо их вообще знать :-). Я тоже предпочитаю без решений не выступать, но эту тему поднял уже для того что разобраться в проблеме. Какие вопросы возникают? И обзор их решений. Исследование в общем. Так что ваши вопросы к mFilter2 мне интересны
                    Александр
                    20 октября 2022, 16:02
                    0
                    Я писал свой магаз тестовый с нуля и делал другую структуру данных
                    Поделись структурой плиз.
                    Максим
                    03 ноября 2022, 14:16
                    0
                    Странно что у вас получились цифры под 39 сек для 11к товаров. — у вас что-то не то с БД.
                    Может просто проиндексировать БД надо? Там вроде в настройках mFilter что-то такое было.
                    Возможно, из-за этого такая цифра.
                    Василий Наумкин
                    03 ноября 2022, 21:26
                    0
                    Чтоб найти что-то в интернете нужно знать нужные слова :-). Проблема фильтрации в интернет-магазинах называется фасетный поиск. И yandex.ru/search/?text=Фасетный поиск дает уже кучу информации как его организовывать.
                    Я честно не знал, что это такое, когда писал. И не гуглил =)

                    В основном применяются специальные базы данных Sphinx/ElasticSearch/etc.
                    Которые так просто на любом PHP хостинге не запустишь. А это необходимо, если ты поставляешь дополнение для работы в MODX.

                    Тонкости фасетного поиска на массивах, таких же как применил @Василий Наумкин в mFilter2, описаны в статье История разработки фасетного поиска средствами PHP. И текущему автору mSearch2 стоит ее прочитать :-).
                    Текущему автору — возможно. А я писал mFilter2 в 2013 году и прочитать эту заметку не мог. Теперь могу, так что сохранил ссылку в закладках на будущее, спасибо!

                    P.S. Кстати, я продолжаю писать фасетный поиск, но уже на Vue и не универсально, а под проект. Самый свежий можно потыкать вот здесь — lehre.lu/map
                      Александр
                      27 ноября 2022, 11:52
                      0
                      Из лицензии mSearch2
                      «that you receive source code or can get it
                      if you want it, that you can change the software or use pieces of it
                      in new free programs»
                      «вы можете изменить программное обеспечение или использовать его части
                      в новых бесплатных программах;»
                      Так получилось что в FacetSearch который я только что написал 90% кода в js файле скопированно из mSearch2.
                      По лицензии я вроде могу это делать? Как вы к этому относитесь?
                      Просто js код я придумать лучше не могу :-(. PHP часть совершенно другая.
                        Николай Савин
                        28 ноября 2022, 09:06
                        +3
                        Компонент определенно получился отличным и очень популярным (особенно в искусственно созданных условиях не конкурентности в магазине). Но хотелось бы вас предупредить. В modx сообществе накопился ряд, не то, чтобы претензий, но замечаний как раз к JS части проекта. Вам как минимум нужно знать, что эти замечания существуют. Кратко могу упомянуть следующее
                        • Использование jquery большая часть разработчиков считает излишним в 2022. Код давно пишут на чистом JS.
                        • Для использования цифрового слайдера и авто подсказок используется jquery UI. Это лишняя устаревшая зависимость. Есть плагины, которые реализуют подобное качественнее. Кроме того слайдер не работает на телефонах.
                        • Привязка к ID на страницах. Тоже можно к проблемам отнести. Переиспользовать код на странице несколько раз не получится. Сейчас подобные вещи обычно к data атрибутам и классам привязывают.
                        • Сам по себе код большой, объемный трудночитаемый. В современном мире разработки обычно принято разбивать код на логические куски и выносить в отдельные модули.
                        Это я кратко описал, то что по-быстрому вспомнил. Если завяжется дискуссия — думаю еще много что всплывет. В общем проект очень хороший для своего времени. Но он с тех пор практически не развивается и в 2022 несколько устарел.
                          Александр
                          30 ноября 2022, 07:32
                          0
                          Использование jquery большая часть разработчиков считает излишним в 2022. Код давно пишут на чистом JS.
                          Я как раз пишу на jquery. На чистом js писать могу научиться, но код на js растягивается Меня добивает что на jquery можно написать
                          $(".nav-img").css('width', 33);

                          а на чистом надо целых 4 строки
                          var vNavimg = document.getElementsByClassName("nav-img");
                          for(var i=0;i<vNavimg.length;i++){
                              vNavimg[i].style.height = "33px";
                          }
                          Для использования цифрового слайдера и авто подсказок используется jquery UI. Это лишняя устаревшая зависимость. Есть плагины, которые реализуют подобное качественнее. Кроме того слайдер не работает на телефонах.
                          не знал что jquery UI не работает на телефонах. Какой компонент посоветуете? И вообще под телефоны надо для фильтров верстку другую. Потом думать буду.
                          Привязка к ID на страницах. Тоже можно к проблемам отнести. Переиспользовать код на странице несколько раз не получится. Сейчас подобные вещи обычно к data атрибутам и классам привязывают.
                          От ID я избавился, но переиспользовать код не получиться. Адресная строка одна и 2 фильтра в нее писать не могут. Я начал делать для 2 и более фильтров на странице, но отказался и отменил изменения.
                          Сам по себе код большой, объемный трудночитаемый. В современном мире разработки обычно принято разбивать код на логические куски и выносить в отдельные модули.
                          если вникнуть в код то на вполне логические блоки разбито. Можно разбить и по другому, но не факт что получиться лучше. Тем более у меня с этим не айс.
                          Василий Наумкин
                          28 ноября 2022, 09:06
                          0
                          Мне без разницы, я больше не занимаюсь дополнениями для MODX.

                          В магазине его поддерживает Илья Уткин, так что спрашивать нужно у него.
                        Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
                        31