
Что-то блог MODX на Хабре совсем зачах, нужно его немного оживить. Хочу представить вам свой компонент, который недавно был практически полностью переписан и расширен.
Он называется mSearch и сначала задумывался как простейший поиск по сайту, с учетом морфологии русского языка. То есть, нужно было простое и легкое решение для любого сайта, без установки Sphinx и других серьезных систем.
В процессе гугления я натолкнулся на одну интересную реализацию этой задачи при помощи phpMorphy. Полнотекстовый поиск по таблице с индексом, с генерацией разных словарных форм. Идея мне приглянулась, под мои критерии она подходила и я написал свое решение, положив в основу этот метод.
mSearch
Это сниппет и плагин. Сниппет ищет, плагин индексирует документы при сохранении.
Установка, как обычно, из репозитория, в 4 клика. При этом, вам будет предложено скачать и установить словари phpMorphy для русского языка — не отказывайтесь.
Если вы устанавливаете расширение на рабочий сайт, нужно будет проиндексировать имеющиеся документы. Делается это просто:
[[!mSearch?
&indexer=`1`
&offset=`0`
&limit=`200`
]]
Первый параметр показывает, что мы не ищем, а индексируем, а второй и третий — ограничение выборки, для больших сайтов. Если документов много, а сервер дохлый — придется работать в несколько запусков, меняя значение offset, в данном примере сначала 0, потом 200, потом 400 и т.д.
Индексация нужна только один раз, после установки на сайт, если там есть документы. Потом индексировать будет плагин mSearchIndexer при сохранении документов.
Все параметры mSearch вы можете увидеть в свойствах сниппета, а сюда я напишу только основные:
- &indexer — включение индексации. Нужно только один раз, после установки.
- &indexFields — какие поля ресурса индексировать?
- &disablePhpMorphy — отключение phpMorphy. Поиск становится гораздо менее точным.
- &includeMS — при выводе результатов включать свойста товаров из miniShop.
- &includeTVs — при выводе товаров включать ТВ параметры.
- &includeTVList — список имен ТВ параметров, через запятую, для вывода.
- &parents — список родителей, в которых будет производиться поиск на глубину 10 вложений, через запятую.
- &returnIds — вместо оформленных результатов возвращать id найденных страниц, через запятую.
- &resources — список обязательных ресурсов, через запятую, в которых будет произведен поиск.
- &templates — искать только среди ресурсов с этими номерами шаблонов, через запятую.
- &tpl — шаблон оформления вывода.
- &where — JSON выражения для тонкой настройки вывода найденных ресурсов.
Параметры indexFields, includeTVs, includeTVList, disablePhpMorphy есть и у плагина. Они отвечают за индексацию ресурса при обновлении.
Также эти параметры влияют и на первичную индексацию (&indexer=`1`).
Обращая ваше внимание, что множество параметров не случайно называются как у getResources. Это означает, что и работают они так же. Сниппет прекрасно поддерживает вывод с пагинацией через getPage и может, в некоторых случая, спокойно заменять getResources.
Поиск идет по таблице индекса, что дает неплохую скорость, даже при большом количестве документов, проверял на ~6000 шт.
Конечно, этот метод не идеален, с релевантностью не все гладко, но лучше решения для русского языка и MODX в любом случае нет. Главное, на мой взгляд то, что можно довольно хорошо уточнить где искать, с каким шаблоном, среди каких родителей и т.д. Ну а параметр &where позволяет и вовсе, как угодно настроить поиск.
Погонять поиск можно тут. Учтите, что морфология работает только для русского языка.
Логичным применением mSearch стал поиск по товарам магазина, и вывод найденных результатов, вместо getResources. А это в свою очередь развилось в написание сниппета mFilter, для гибкой фильтрации найденных товаров.
mFilter
Это второй сниппет в mSearch. Он самостоятельно генерирует фильтры для найденных (или заранее указанных) ресурсов, используя чанки для оформления. Цифровые значения по умолчанию выводятся как слайдер, текстовые — как чекбоксы.
Фильтры строятся по ТВ параметрам ресурсов, и\или свойств товаров miniShop.
Выглядит это вот так.
Вы видите слева параметры, справа результаты поиска на моем демо-сайте по фразе «Sony». Возле каждого параметра есть циферки — это количество результатов, которое вы получите при выборе это параметра. То есть, все варианты фильтра просчитываются на лету, и невозможные комбинации отключаются.
Работает исключительно через Ajax (js скрипт для примера прилагается). При загрузке страницы выбираются все нужные свойства ресурсов для построения фильтров, кэшируются, и выводится форма фильтров. при каждом изменении формы, она отправляется на сервере и в ответ приходят результаты и состояние переключателей фильтра.
Работает довольно шустро, но на действительно больших объемах пока не проверял.
Основные параметры mFilter:
- &resources — список ресурсов для работы, через запятую. Если не указать, попытается поискать результаты через mFilter.
- &includeTVs — включить ТВ для генерации фильтров
- &includeTVList — использовать эти ТВ, через запятую
- &excludeTVList — не использовать эти ТВ, через запятую
- &includeMS — брать параметры miniShop для построения фильтра
- &includeMSList — какие именно поля товаров miniShop использовать для фильтра
- &sortFilters — порядок вывода фильтров, с префиксами, например: `tv_category,tv_subcategory,ms_add2,tv_color,tv_size,tv_action,ms_price`
- + если будете искать ресурсы, то любые параметры mSearch
- + любые параметры getPage и\или getResources\msGetResources
Для вывода результатов фильтра необходимы сниппеты getPage, getResources (если используете miniShop — msGetResources).
Также требуются jquery плагины:
jQuery 1.7+ — основной помошник
jQueryUI 1.8+ — нужен только виджет slider для цен и других числовых значений
jQuery Form 2.7+ — отправка формы и прием ответа
Для вывода товаров из определенной категории магазина miniShop можно использовать такой сниппет getCatIds:
if (!empty($_REQUEST['query'])) {return;}
$parent = $modx->resource->id;
$tmp = $modx->getChildIds($parent);
if (empty($tmp)) {return 0;}
$tpls = explode(',', $modx->getOption('minishop.goods_tpl'));
$q = $modx->newQuery('modResource', array('id:IN' => $tmp, 'template:IN' => $tpls, 'deleted:!=' => 1, 'published' => 1));
$q->select('id');
if ($q->prepare() && $q->stmt->execute()) {
$ids = $q->stmt->fetchAll(PDO::FETCH_COLUMN, 0);
return implode(',', $ids);
}
Пример вызова:
[[!mFilter?
&resources=`[[!getCatIds]]`
&includeTVs=`1`
&includeTVList=`category,color,size,action`
&includeMS=`1`
&includeMSList=`add2,price`
&tpl=`tpl.msGoods.row`
&templates=`5`
&sortFilters=`tv_category,tv_subcategory,ms_add2,tv_color,tv_size,tv_action,ms_price`
]]
Заключение
Все чанки оформления и js скрипт для работы идут в комплекте. Конечно, не факт, что у вас все получится настроить как надо с первого раза, для этого нужны кое-какие знания MODX, jQuery и понимание работы Ajax. При всех «не работает!» первым делом смотрите в консоль своего браузера на предмет ошибок javascript.
Но я, как смог, постарался упростить задачу построения фильтра. И теперь, при определенном желании, вы сможете сделать это у себя на сайте\магазине.
Вы можете посмотреть в админке моего демо-сайта, как включены фильтр и поиск. Логин и пароль: test.
Кстати
Вся работа над mFilter была заказа и оплачена веб-студией Simple Dream.