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 properties —
b_iblock_element_property, whereIBLOCK_PROPERTY_IDis the property ID andVALUEis the value -
Multiple properties — several rows in
b_iblock_element_propertywith the sameIBLOCK_ELEMENT_IDandIBLOCK_PROPERTY_ID -
List-type properties —
VALUEcontains the text value,VALUE_ENUM_IDreferencesb_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 |

