Setting up mass changes to product properties in 1C-Bitrix

Our company is engaged in the development, support and maintenance of Bitrix and Bitrix24 solutions of any complexity. From simple one-page sites to complex online stores, CRM systems with 1C and telephony integration. The experience of developers is confirmed by certificates from the vendor.
Our competencies:
Development stages

Bulk Product Property Update Configuration in 1C-Bitrix

In a catalog of 15,000 products, you need to set a new "Material" property on 8,000 items in a specific category, fix a typo in a facet filter value for 2,000 products, and remove the "Recommended" flag from half the assortment. Doing this through individual product cards takes weeks. The task is solved with batch updates via the API and data integrity controls.

Where Properties Are Stored

Product properties in Bitrix are stored in several locations depending on type:

  • Element fields (NAME, PREVIEW_TEXT, ACTIVE, etc.) — b_iblock_element
  • Iblock propertiesb_iblock_element_property, where IBLOCK_PROPERTY_ID is the property ID and VALUE is the value
  • Multiple properties — several rows in b_iblock_element_property with the same IBLOCK_ELEMENT_ID and IBLOCK_PROPERTY_ID
  • List-type propertiesVALUE contains the text value, VALUE_ENUM_ID references b_iblock_property_enum

For trade offers — the same structure, but IBLOCK_ID points to the offers iblock, not the main catalog.

Bulk Update via CIBlockElement::SetPropertyValues

The basic approach for small volumes (up to 1,000 elements):

$iblockId   = 10; // Catalog iblock ID
$propertyCode = 'MATERIAL';
$newValue   = '100% Cotton';

// Get list of elements in the target section
$res = \CIBlockElement::GetList(
    [],
    ['IBLOCK_ID' => $iblockId, 'SECTION_ID' => 42, 'ACTIVE' => 'Y'],
    false,
    false,
    ['ID']
);

while ($row = $res->Fetch()) {
    \CIBlockElement::SetPropertyValues(
        $row['ID'],
        $iblockId,
        $newValue,
        $propertyCode
    );
}

At high volumes this method is slow — it reads current values, compares them, then updates. Each call involves multiple SQL queries.

Fast Update via D7 ORM

For volumes of 1,000+ elements, use direct updates to b_iblock_element_property:

use Bitrix\Iblock\ElementPropertyTable;

// First get the property ID
$propertyId = getPropertyIdByCode($iblockId, 'MATERIAL');

// Get element IDs in batches
$elementIds = getElementIdsBySectionBatch($iblockId, $sectionId, 500);

foreach (array_chunk($elementIds, 500) as $chunk) {
    // Check which elements already have a record
    $existing = ElementPropertyTable::getList([
        'filter' => [
            'IBLOCK_PROPERTY_ID' => $propertyId,
            'IBLOCK_ELEMENT_ID'  => $chunk,
        ],
        'select' => ['ID', 'IBLOCK_ELEMENT_ID'],
    ])->fetchAll();

    $existingMap = array_column($existing, 'ID', 'IBLOCK_ELEMENT_ID');

    foreach ($chunk as $elementId) {
        if (isset($existingMap[$elementId])) {
            // Update existing record
            ElementPropertyTable::update($existingMap[$elementId], ['VALUE' => '100% Cotton']);
        } else {
            // Insert new record
            ElementPropertyTable::add([
                'IBLOCK_ELEMENT_ID'  => $elementId,
                'IBLOCK_PROPERTY_ID' => $propertyId,
                'VALUE'              => '100% Cotton',
            ]);
        }
    }
}

After directly modifying the table, the iblock cache must be cleared:

\Bitrix\Iblock\InformationBlock::cleanTagCache($iblockId);
\Bitrix\Main\Application::getInstance()->getTaggedCache()->clearByTag('iblock_id_' . $iblockId);

Bulk Update of List-Type Values

List-type properties (used in the facet filter) store the text value in VALUE and the enum ID in VALUE_ENUM_ID, referencing b_iblock_property_enum. When changing a value, both fields must be updated.

// Find the ID of the new value in the enum
$enumRes = \CIBlockPropertyEnum::GetList(
    [],
    ['PROPERTY_ID' => $propertyId, 'VALUE' => 'Blue']
);
$enum = $enumRes->Fetch();
$enumId = $enum['ID'];

// Update
ElementPropertyTable::update($existingPropId, [
    'VALUE'         => 'Blue',
    'VALUE_ENUM_ID' => $enumId,
]);

If the required value does not yet exist in the enum — add it first via CIBlockProperty::SetEnumValues() or directly in b_iblock_property_enum.

Update via Admin Import

For non-technical users or recurring updates, CSV import via Catalog → Import is a better fit. File template: first row contains headers with field codes (ID, PROPERTY_MATERIAL, PROPERTY_COLOR). Bitrix updates only those properties whose columns are present in the file.

Limitation of the standard import: no conditional logic ("update property only if current value is empty"). For such scenarios — scripts only.

Facet Index Invalidation

After bulk changes to properties used in the facet filter (catalog.smart.filter), the index must be rebuilt:

\Bitrix\Iblock\PropertyIndex\Manager::markIblockToReindex($iblockId);
// or force immediately:
$indexer = new \Bitrix\Iblock\PropertyIndex\Indexer($iblockId);
$indexer->startIndex();
$indexer->continueIndex(0);
$indexer->endIndex();

On a catalog of 50,000+ products, reindexing takes several minutes — run it in the background via an agent or cron.

Estimated Timelines

Volume Method Time
Up to 500 products Admin UI / SetPropertyValues 1–3 hours
500–5,000 products D7 batch update 3–6 hours
5,000–50,000 products D7 + queue + reindexing 1–2 days