Задача
На сайте есть каталог товаров с многоуровневой вложенностью разделов. Требуется обеспечить нахождение товаров по запросам которые включают в себя названия разделов.
Пример: есть раздел “моющие средства” и товар “мыло”. В обычном случае компонент поиска в Битрикс при запросе “мыло” найдет нужный товар, а при запросе “моющее средство мыло” не найдет. Нужно это исправить.
Решение
Общая идея сводится к получению цепочки названий разделов, к которым принадлежит товар. Эту цепочку нужно сохранить в инфоблок товаров в отдельное поле, это поле добавить к списку участвующих в поиске. И переиндексировать поиск, что бы изменение данных товара попало в поисковый индекс.
Делать все эти действия можно либо на событие изменения товара, если у Вас ручное наполнение каталога, в этом случае нужно индексировать только изменения.
Если же у Вас проходит синхронизация с 1С, то описанные выше действия нужно выполнить после завершения синхронизации. О том, как корректно отловить событие окончания синхронизации с 1С можно узнать в статье Умный фильтр, бизнес-логика сайта и перестроение фасетного индекса
Битрикс настройка
Сперва проведем настройки в админке битрикса. Добавим в инфоблок каталога поле “Цепочка разделов для поиска” с типом “строка”, ставим галку “Участвует в поиске”. В это поле мы будем сохранять цепоцку с названиями разделов к которым принадлежит товар.
PHP код
Пример получения цепочки разделов по коду товара, обновление свойства и переиндексации поиска:
<?php
class CSearchHelper {
/**
* Обновление цепочек принадлежности к разделам
* @param int $iblockId
* @param array $arFilter
* @return bool|int|array
*/
public static function productsGroupChainRefresh($iblockId, $arFilter) {
if (!\Bitrix\Main\Loader::includeModule('iblock')
|| !\Bitrix\Main\Loader::includeModule('search')) {
return false;
}
$arFilterFinal = [
'IBLOCK_ID' => $iblockId,
$arFilter,
];
$dbEls = CIBlockElement::GetList([], $arFilterFinal, false, false, ['ID', 'NAME', 'IBLOCK_SECTION_ID']);
$arSecTree = [];
while ($arEl = $dbEls->GetNext()) {
if($arEl["IBLOCK_SECTION_ID"] || !isset($arSecTree[$arEl["IBLOCK_SECTION_ID"]])) {
$dbChain = CIBlockSection::GetNavChain(false, $arEl["IBLOCK_SECTION_ID"]);
$arChain = [];
while ($arGroup = $dbChain->GetNext()) {
$arChain[] = $arGroup['NAME'];
}
$arSecTree[$arEl["IBLOCK_SECTION_ID"]] = implode(', ', $arChain);
}
CIBlockElement::SetPropertyValuesEx($arEl['ID'], false, array('GROUP_CHAIN' => $arSecTree[$arEl["IBLOCK_SECTION_ID"]]));
}
//переиндексация поиска
$max_execution_time = 20;
$NS = [];
do {
$NS = CSearch::ReIndexAll(true, $max_execution_time, $NS);
} while(is_array($NS));
return $NS;
}
}
?>
Функцию productsGroupChainRefresh
нужно вызвать после завершения выгрузки, передав в нее параметр $iblockId - код информационного блока и $arFilter - фильтр для отбора элементов