Фасетный инфоблок: агент, который дорисовывает индекс в фоне
Когда без фасета больно
У торговых инфоблоков вкладка свойств может требовать фасетный индекс: без него bitrix:catalog.smart.filter считает ограничения медленнее и не всегда совпадает с тем, что реально выдаёт каталог после правок свойств или массовой выгрузки. В административной части сборку обычно запускают вручную, но если фон задачи обрывается или флаг активности слетел после миграции, хочется «дожимать» задачу на cron без участия оператора.
Зачем отдельный агент
Стандартный мастер блокирует сессию и не годится для ночной автоматизации. Агент в Bitrix выполняется пачками между хитами либо, что надёжнее для долгих проходов, по расписанию сервера, когда включён режим выполнения агентов на cron. Обёртка ниже просто проверяет строку инфоблока в таблице: поле PROPERTY_INDEX должно быть Y, иначе индекс ещё «холодный».
Состав решения
Loader::includeModule('iblock')перед любыми вызовами API инфоблоков.- Чтение
PROPERTY_INDEXчерезIblockTable. - Фабрика
\Bitrix\Iblock\PropertyIndex\Manager::createIndexer(): цепочкаstartIndex(), сброс курсора,continueIndex()с таймаутом в секундах,endIndex(). - После завершения — сброс кеша умного фильтра и менеджер тегирования инфоблока, чтобы публичка не продолжала отдавать устаревшее дерево параметров.
Полный класс агента
Подберите свой идентификатор торгового инфоблока вместо $facetTargetIblockPk и перенесите namespace под автозагрузку проекта (/local/php_interface/init.php или Composer). Строка, которую должен выполнять агент из списка, — \Custom\FacetIndexing\StaleFacetRebuildAgent::runScheduled();.
namespace Custom\FacetIndexing;
use Bitrix\Main\Loader;
class StaleFacetRebuildAgent
{
/**
* Вызывается регламентным агентом. Нужна cron-диспетчеризация для тяжёлых задач.
*/
public static function runScheduled(): string
{
if (!Loader::includeModule('iblock')) {
return '\\'.__METHOD__.'();';
}
$facetTargetIblockPk = 18;
if (!static::isFacetStillMarkedInactive($facetTargetIblockPk)) {
return '\\'.__METHOD__.'();';
}
static::rollFacetPass($facetTargetIblockPk);
return '\\'.__METHOD__.'();';
}
protected static function isFacetStillMarkedInactive(int $facetTargetIblockPk): bool
{
if ($facetTargetIblockPk <= 0) {
return false;
}
$sheet = \Bitrix\Iblock\IblockTable::getList([
'select' => ['ID', 'PROPERTY_INDEX'],
'filter' => ['=ID' => $facetTargetIblockPk],
]);
if ($facetRow = $sheet->fetch()) {
return ($facetRow['PROPERTY_INDEX'] ?? '') !== 'Y';
}
return false;
}
protected static function rollFacetPass(int $facetTargetIblockPk): void
{
$facetDriver = \Bitrix\Iblock\PropertyIndex\Manager::createIndexer($facetTargetIblockPk);
if (!$facetDriver) {
return;
}
$facetDriver->startIndex();
$facetDriver->setLastElementId(0);
$facetDriver->continueIndex(86400);
$facetDriver->endIndex();
\CBitrixComponent::clearComponentCache('bitrix:catalog.smart.filter');
\CIBlock::clearIblockTagCache($facetTargetIblockPk);
}
}Параметр continueIndex
Число 86400 задаёт допустимую длительность одной итерации в секундах. Если каталог огромный, ядро само режет работу по отрезку; агент, вызываемый чаще, подхватит остаток. Для маленького каталога одного прохода часто достаточно.
На что посмотреть при вводе
- Убедитесь, что в настройках модуля агенты реально выполняются с cron на боевой среде, иначе тяжёлый блокировщик упирается в таймауты веб-хитов.
- Не запускайте параллельно два процесса, которые одновременно гонят
continueIndex()на один инфоблок — получите очередность за счёт блокировок и лишнее напряжение на базе. - Следите за логированием ошибок PHP: любое фатальное прерывание до
endIndex()может оставить промежуточное состояние.
Итог
Небольшой сервисный класс переводит ручную кнопку «перестроить фасет» в повторяемый фон без дублирования SQL. Связка Manager::createIndexer и очистка кеша умного фильтра синхронизирует счётчики на витрине с фактическим набором значений свойств после обменов или импорта каталога.
Не хотите копаться сами?
Починю за 1-3 дня. Без предоплаты — оплата по результату.
15+ лет опыта с 1С-Битрикс · Без предоплаты · 7 дней гарантии