
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 не установлен
- Зайти в Установщик → mFilter
- Нажать жёлтую кнопку «Построить индекс» в правом верхнем углу
- Дождаться сообщения об успехе
На каталоге 30k построение занимает 5–10 секунд, на 200k — до 2 минут.
После массового импорта товаров
Зайти на вкладку Обслуживание и нажать «Пересобрать сейчас» (или «Через Scheduler» для больших каталогов). Без этого новые товары не появятся в фильтрах.
