Skip to content
mFilter
mFilter
Фасетная фильтрация для MODX 3 с поддержкой SEO URL
  1. Компоненты
  2. mFilter

mFilter 1.4.0

Главное

Денормализованный индекс фасетов для масштабируемой фильтрации каталогов 100k+ товаров. Заменяет JOIN'ы к msProductData, msProductOption, modTemplateVarResource на чтение готового индекса.

Замеры на каталоге 30k:

ОперацияДо 1.4.0После 1.4.0
Применение фильтра~30 сек~1.7 сек
Расчёт suggestion'овсотни мсединицы мс

Каталоги 200k+, ранее неюзабельные, теперь работают за секунды.

Новые возможности

Индекс фасетов

Две таблицы:

  • mfl_facet_index_text — для текстовых значений (бренды, цвета, размеры)
  • mfl_facet_index_num — для числовых (price, weight)

Обе с покрывающим PRIMARY KEY (filter_key, filter_value, product_id). Запросы суждений сводятся к одному скану индекса + GROUP BY.

Прозрачное чтение

Обработчики FilterType сами определяют, проиндексирован ли ключ, и переключаются между индексом и старым путём (JOIN к источнику) в каждом запросе.

  • Существующие наборы фильтров работают без изменений
  • Никакого source: facet_index в админке — индекс прозрачен для пользователя
  • Если индекс ещё не построен — fallback на источники работает корректно

Бесшовная миграция при upgrade

При обновлении пакета резолвер ставит задачу mfl_rebuild_facet_index в очередь Scheduler (если установлен). До завершения сборки сайт работает на fallback'е — простоев нет.

Без Scheduler сборка запускается одной кнопкой в админке.

Вкладка «Обслуживание»

Полностью переработана — теперь главный блок про индекс фасетов:

  • Статус индекса (активен / в очереди / не построен)
  • Количество текстовых и числовых записей
  • Время последней пересборки
  • Кнопки «Пересобрать сейчас» и «Через Scheduler»

Старый прогрев baseIds (warmup) перенесён в сворачиваемый блок с пометкой «Legacy» — он ещё нужен для AJAX-режима, но после стабилизации индекса будет упрощён.

Подробнее: Обслуживание.

Кнопка «Переиндексация» в шапке

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

Подсвечивается жёлтым, пока индекс не построен — привлекает внимание администратора к тому, что одно нажатие кратно ускорит сайт.

Инкрементальная пересборка при сохранении набора

При изменении конфигурации набора фильтров пересобирается только тот ключ, который изменился — а не весь каталог. Добавил новый фильтр weight — пересобирается только weight.

Изменения определяются по содержимому конфига независимо от порядка ключей (рекурсивный ksort + json_encode).

Производительность

Batch SQL для текстовых suggestion'ов

Если на странице ≥2 индексированных текстовых фильтров, их значения собираются одним GROUP BY (filter_key, filter_value) запросом вместо N отдельных. На страницах с несколькими фильтрами — ускорение ~4×.

COUNT(*) вместо COUNT(DISTINCT)

В чтениях из индекса используется обычный COUNT(*) — благодаря уникальности PRIMARY KEY дедупликация не нужна. Оптимизатор пропускает лишний шаг.

Raw PDO fetch в Filter::applyToIds

При фильтрации внешнего списка ID (например, после mSearch) используется прямой PDO fetch вместо getIterator() — экономит 2–3 секунды на выборках 30k+ строк.

Инфраструктура

Новые таблицы БД

ТаблицаНазначение
mfl_facet_index_textТекстовые значения фильтров
mfl_facet_index_numЧисловые значения фильтров
mfl_request_runsРегистрация запросов с большими списками ID
mfl_request_idsСписки ID для JOIN-замены IN(...)

Подробнее: Модели и БД.

Новая Scheduler-задача

mfl_rebuild_facet_index — фоновая полная пересборка индекса. Используется при upgrade и для еженедельной sanity-пересборки (если включить recurring вручную).

Эндпойнт статуса

/maintenance/status теперь возвращает блок facet_index с количеством строк, временем последней пересборки и флагом pending-run.

Хардeнинг

По результатам code review:

  • INSERT IGNORE возвращает количество строк через PDO::exec() — убран лишний SELECT ROW_COUNT()
  • REGEXP для числовых значений принимает опциональный знак и запятую как разделитель; REPLACE перед CAST не даёт '1,5' молча превратиться в 1
  • Whitelist ^[a-zA-Z0-9_]+$ для имени поля в индексаторе — defense-in-depth
  • Ошибки PDO prepare/execute в Filter::applyToIds логируют errorInfo + SQL вместо тихого пустого результата

Известные ограничения

OnDocFormSave не реализован

Индекс не обновляется автоматически при редактировании товара через админку MODX. После массовых правок товаров нужна ручная переиндексация (кнопка «Переиндексация» в шапке).

Будет реализовано в следующем патче.

Удаление набора не чистит индекс

При удалении набора фильтров его данные остаются в индексе до следующей полной пересборки. На корректность не влияет — лишние строки игнорируются. Косметика.

Race condition в buildAll()

Между TRUNCATE и завершением INSERT...SELECT есть окно (минуты на больших каталогах), когда долгоживущие PHP-процессы с прогретым кэшем ключей могут видеть пустой индекс. В типичных FPM-конфигурациях практически не проявляется.

Отслеживается: issue #12. Фикс через shadow-table RENAME запланирован.

Что делать после обновления

Если установлен Scheduler

Ничего — индекс соберётся в фоне на ближайшем тике.

Если Scheduler не установлен

  1. Зайти в Установщик → mFilter
  2. Нажать жёлтую кнопку «Построить индекс» в правом верхнем углу
  3. Дождаться сообщения об успехе

На каталоге 30k построение занимает 5–10 секунд, на 200k — до 2 минут.

После массового импорта товаров

Зайти на вкладку Обслуживание и нажать «Пересобрать сейчас» (или «Через Scheduler» для больших каталогов). Без этого новые товары не появятся в фильтрах.