Блог разработчика 1С-Битрикс

Руководство по работе с Bitrix\Iblock\PropertyIndex\Storage

Storage — это низкоуровневый класс ядра 1С-Битрикс, отвечающий за хранение и поддержку фасетного индекса свойств инфоблока. В статье мы разберём каждую возможность класса и покажем полностью рабочие примеры кода, которые можно вставить в свои скрипты или модули.

Руководство по работе с Bitrix\Iblock\PropertyIndex\Storage

1. Зачем нужен фасетный индекс?

  • Быстрые фильтры в каталогах и умном фильтре
  • Поддержка сортировки по цене и числовым свойствам
  • Мгновенные выборки без «джойнов» по большому числу свойств

Без индекса каждый запрос к каталогу превращается в множественные JOIN-операции к таблицам b_iblock_element_property*, что заметно замедляет выборку.


2. Быстрый старт

<?php
use Bitrix\Iblock\PropertyIndex\Storage;
use Bitrix\Main\Loader;
Loader::includeModule('iblock');
$iblockId = 12;
$storage = new Storage($iblockId);
// 1. Проверяем, есть ли индекс
if (!$storage->isExists()) {
    $storage->create();
}
// 2. Добавляем запись (пример для цены)
$sectionId = 44;
$elementId = 1001;
$priceFacetId = Storage::priceIdToFacetId(1); // ID базовой цены
$dictionaryValue = 0; // для цены всегда 0
$price = 2999.90;
$includeSubsections = true;
$storage->addIndexEntry(
    $sectionId,
    $elementId,
    $priceFacetId,
    $dictionaryValue,
    $price,
    $includeSubsections
);

Совет: при массовом импорте используйте batch-методы (queueIndexEntryflushIndexEntries), чтобы сократить время вставки.


3. Обзор методов и примеры

3.1 create() — создание индекса

if (!$storage->isExists()) {
    $storage->create();
}

Метод создаёт таблицу вида b_iblock_<IBLOCK_ID>_index и накладывает оптимальные индексы SQL.

Ошибка, которую часто допускают: создавать индекс повторно. Всегда проверяйте isExists().

3.2 addIndexEntry() — одиночная вставка

$facetId = Storage::propertyIdToFacetId($propertyId);
$storage->addIndexEntry(
    $sectionId,
    $elementId,
    $facetId,
    $dictionaryValue, // либо ID значения, либо 0
    $propertyNumber, // числовое значение или цена
    $includeSubsections // true|false
);
  • Для строковых/списочных свойств указываем $value — ID из словаря (b_iblock_dictionary), а $valueNum = 0.
  • Для числовых / цен — наоборот: $value = 0, $valueNum — фактическое число.

3.3 Пакетная вставка: queueIndexEntry() + flushIndexEntries()

$items = getMyImportArray(); // массив элементов
$storage = new Storage($iblockId);
foreach ($items as $row) {
    $storage->queueIndexEntry(
        $row['SECTION_ID'],
        $row['ELEMENT_ID'],
        Storage::propertyIdToFacetId($row['PROP_ID']),
        $row['DICT_ID'],
        0,
        false
    );
}
$storage->flushIndexEntries();
  • Используйте, если вставляете >1000 записей за один проход.
  • Внутри класса стоит лимит $insertMax = 1024000 байт, после которого буфер пишется автоматически.

3.4 deleteIndexElement() — удаление записей элемента

$storage->deleteIndexElement($elementId);

Полезно при доиндексации после обновления свойств отдельного товара.

3.5 drop() — полное удаление индекса

if ($storage->isExists()) {
    $storage->drop();
}

Используйте, если нужно пересобрать индекс с нуля (например, после массового удаления свойств).

3.6 Служебные методы преобразования идентификаторов

// Свойство → Facet
$facetId = Storage::propertyIdToFacetId($propertyId);
// Цена → Facet
$facetId = Storage::priceIdToFacetId($priceId);
// Facet → Свойство
$propertyId = Storage::facetIdToPropertyId($facetId);
// Facet → Цена
$priceId = Storage::facetIdToPriceId($facetId);
// Проверки
if (Storage::isPriceId($facetId)) { /* ... */ }
if (Storage::isPropertyId($facetId)) { /* ... */ }

Почему чёт/нечёт?

Внутри класса идентификаторы свойств хранятся как чётные числа (propertyId * 2), а цен — нечётные (priceId * 2 + 1). Это даёт мгновенную проверку принадлежности.


4. Практические сценарии

4.1 Генерация индекса после импорта каталога

function buildFacetIndex(int $iblockId): void
{
    $storage = new Storage($iblockId);
    if ($storage->isExists()) {
        $storage->drop(); // начнём с чистого листа
    }
    $storage->create();
    $rsElements = CIBlockElement::GetList(
        [],
        ['IBLOCK_ID' => $iblockId, 'ACTIVE' => 'Y'],
        false,
        false,
        ['ID', 'IBLOCK_SECTION_ID']
    );
    while ($arElement = $rsElements->Fetch()) {
        indexProduct($storage, $arElement);
    }
    $storage->flushIndexEntries();
}

function indexProduct(Storage $storage, array $arElement): void
{
    $elementId = (int)$arElement['ID'];
    $sectionId = (int)$arElement['IBLOCK_SECTION_ID'];
    // индексируем цены
    $price = CPrice::GetBasePrice($elementId);
    if ($price) {
        $storage->queueIndexEntry(
            $sectionId,
            $elementId,
            Storage::priceIdToFacetId($price['CATALOG_GROUP_ID']),
            0,
            (float)$price['PRICE'],
            true
        );
    }
    // индексируем свойства
    $props = CIBlockElement::GetPropertyValuesArray(
        $arValues,
        $storage->getIblockId(),
        ['ID' => $elementId]
    );
    foreach ($arValues[$elementId] as $propertyId => $prop) {
        if ($prop['PROPERTY_TYPE'] === 'N') {
            // числовое
            $storage->queueIndexEntry(
                $sectionId,
                $elementId,
                Storage::propertyIdToFacetId($propertyId),
                0,
                (float)$prop['VALUE'],
                true
            );
        } elseif ($prop['PROPERTY_TYPE'] === 'L') {
            // списочное
            $storage->queueIndexEntry(
                $sectionId,
                $elementId,
                Storage::propertyIdToFacetId($propertyId),
                (int)$prop['VALUE_ENUM_ID'],
                0,
                true
            );
        }
    }
}

Скрипт создаёт индекс «с нуля», обрабатывая каждую цену и свойство согласно типу.

4.2 Использование индекса в своём компоненте фильтра

$facet = new Bitrix\Iblock\PropertyIndex\Facet($iblockId);
// получаем элементы, у которых цена до 3000
$result = $facet->query([
    ['FACET_ID' => Storage::priceIdToFacetId(1), '<=VALUE_NUM' => 3000]
]);
while ($row = $result->fetch()) {
    echo 'Элемент ' . $row['ELEMENT_ID'] . PHP_EOL;
}

PropertyIndex\Facet опирается на данные таблицы Storage, поэтому предварительное создание индекса — обязательное условие.


5. Полезные советы и подводные камни

Совет Пояснение
Создавайте индекс офлайн При больших каталогах (>50 000 товаров) выполняйте create() и заполнение на тестовом стенде или скриптом cron в «окно» минимальной нагрузки.
Следите за SQL-буфером Если меняете $insertMax, балансируйте между количеством соединений и размером запроса (max_allowed_packet в MySQL).
Не смешивайте значения Одно свойство = одна запись в индексе. Для множественных свойств добавляйте запись на каждое значение.
Регулярно чистите индекс После удаления товаров вызывайте deleteIndexElement() или пересоздавайте индекс, чтобы не накапливать «мёртвые» ссылки.

6. Заключение

Storage — мощный, но недооценённый инструмент в 1С-Битрикс. Он позволяет:

  • ускорить фильтрацию каталога в разы;
  • разгрузить базу за счёт отказа от тяжёлых JOIN-ов;
  • легко масштабировать проект за счёт простого расширения индекса.

Используйте примеры из статьи как основу для своих скриптов импорта, cron-задач и компонентов. При грамотной настройке фасетный индекс служит надёжным фундаментом высокой скорости e-commerce-проектов.

Теги:  фасетный индекс, Storage, инфоблоки, оптимизация, фильтрация, свойства инфоблока


Стоимость услуг по разработке и сопровождению сайтов на 1C-Битрикс

Лендинг

от 3 дней

от 25 000 рублей

Разработка одностраничного сайта на платформе Битрикс

* стоимость зависит от наличия верстки, использования готового решения и т.д.

Интернет-магазин на готовом решении

от 7 дней

от 40 000 рублей
запуск сайта в максимально короткие сроки

* указана минимальная стоимость. Стоимость выбранной лицензии «1С-Битрикс» оплачивается отдельно.

Модули и компоненты для «1С-Битрикс»

оценка производится на основе предоставленного Технического Задания

от 20 000 рублей
Разработка дополнительных модулей для 1С-Битрикс, расширение функционала, внедрение любых решений, требующихся для выполнения ваших бизнес-задач.

* стоимость зависит от конкретной задачи, ее объема и сложности выполняемых работ.