Налаштування рекомендацій товарів на основі історії покупок 1С-Бітрікс

Наша компанія займається розробкою, підтримкою та обслуговуванням рішень на Бітрікс та Бітрікс24 будь-якої складності. Від простих односторінкових сайтів до складних інтернет-магазинів, CRM систем з інтеграцією 1С та телефонії. Досвід розробників підтверджено сертифікатами від вендора.
Пропоновані послуги
Показано 1 з 1 послугУсі 1626 послуг
Налаштування рекомендацій товарів на основі історії покупок 1С-Бітрікс
Проста
~1 робочий день
Часті питання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851
  • image_bitrix-bitrix-24-1c_development_of_an_online_appointment_booking_widget_for_a_medical_center_594_0.webp
    Розробка на базі Бітрікс, Бітрікс24, 1С для компанії Development of an Online
    585
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Розробка на базі 1С Підприємство для компанії МИРСАНБЕЛ
    751
  • image_crm_dolbimby_434_0.webp
    Розробка сайту на CRM Бітрікс24 для компанії DOLBIMBY
    657
  • image_crm_technotorgcomplex_453_0.webp
    Розробка на базі Бітрікс24 для компанії ТЕХНОТОРГКОМПЛЕКС
    989

Налаштування рекомендацій товарів на основі історії покупок 1С-Bitrix

Історія покупок — найсильніший поведінковий сигнал. Користувач уже заплатив гроші — це не перегляд, не клік, це підтверджений інтерес. Рекомендації на основі історії покупок працюють за двома патернами: «покупці цього товару також купили» (item-based) та «ваші минулі покупки схожі на покупки користувачів X, вони також взяли Y» (user-based). Обидва патерни реалізуються прямо в Bitrix без зовнішніх ML-сервісів.

Таблиці з даними про покупки

Уся історія замовлень в Bitrix — три ключові таблиці:

  • b_sale_order — замовлення: поля USER_ID, CANCELED, STATUS_ID, PRICE
  • b_sale_order_basket — склад замовлень: ORDER_ID, PRODUCT_ID, QUANTITY, PRICE
  • b_catalog_product — наявність товару: QUANTITY, AVAILABLE

Для рекомендацій використовуємо тільки незміновані замовлення (CANCELED = 'N') у фінальних статусах. Статус F (Finished) — стандартний фінальний, але на багатьох проектах використовуються користувацькі статуси.

Item-based: «часто купують разом»

Основний патерн — блок «Часто купують з цим товаром» на карточці товару:

SELECT
    ob2.PRODUCT_ID,
    COUNT(DISTINCT ob1.ORDER_ID) AS co_purchase_count,
    SUM(ob2.QUANTITY)            AS total_qty
FROM b_sale_order_basket ob1
JOIN b_sale_order_basket ob2
    ON ob1.ORDER_ID = ob2.ORDER_ID
    AND ob2.PRODUCT_ID != ob1.PRODUCT_ID
JOIN b_sale_order o
    ON o.ID = ob1.ORDER_ID
    AND o.CANCELED = 'N'
    AND o.DATE_INSERT > NOW() - INTERVAL '90 days'
WHERE ob1.PRODUCT_ID = :target_product_id
GROUP BY ob2.PRODUCT_ID
ORDER BY co_purchase_count DESC
LIMIT 20;

Цей запит виконується офлайн через агент Bitrix — раз на 4 години. Результат записується в таблицю:

CREATE TABLE b_product_cross_sell (
    SOURCE_ID        INT NOT NULL,
    RECOMMENDED_ID   INT NOT NULL,
    SCORE            INT NOT NULL,
    UPDATED_AT       TIMESTAMP DEFAULT NOW(),
    PRIMARY KEY (SOURCE_ID, RECOMMENDED_ID)
);
CREATE INDEX idx_cross_sell_source ON b_product_cross_sell(SOURCE_ID, SCORE DESC);

Індекс (PRODUCT_ID, ORDER_ID) на b_sale_order_basket критично важливий — без нього JOIN на великих магазинах (100k+ замовлень) буде виконуватися секундами.

User-based: персональні рекомендації для авторизованого користувача

Для конкретного користувача будується список товарів, які купили «схожі» покупці. «Схожість» — перетин історії покупок.

function getUserBasedRecs(int $userId, int $limit = 8): array {
    // 1. Історія покупок поточного користувача
    $myOrderIds = array_column(
        \Bitrix\Sale\OrderTable::getList([
            'filter' => ['USER_ID' => $userId, 'CANCELED' => 'N'],
            'select' => ['ID'],
        ])->fetchAll(),
        'ID'
    );

    if (empty($myOrderIds)) return getPopularItems($limit);

    $myProductIds = array_column(
        \Bitrix\Sale\Internals\BasketTable::getList([
            'filter' => ['ORDER_ID' => $myOrderIds],
            'select' => ['PRODUCT_ID'],
        ])->fetchAll(),
        'PRODUCT_ID'
    );

    // 2. Користувачі, які купили ті ж товари
    // 3. Товари цих користувачів, які у нас немає
    $res = $GLOBALS['DB']->Query("
        SELECT ob2.PRODUCT_ID, COUNT(DISTINCT o2.USER_ID) AS score
        FROM b_sale_order_basket ob1
        JOIN b_sale_order o1 ON o1.ID = ob1.ORDER_ID AND o1.USER_ID = {$userId}
        JOIN b_sale_order_basket ob2 ON ob2.ORDER_ID IN (
            SELECT DISTINCT o3.ID FROM b_sale_order o3
            JOIN b_sale_order_basket ob3 ON ob3.ORDER_ID = o3.ID
                AND ob3.PRODUCT_ID IN (" . implode(',', array_map('intval', $myProductIds)) . ")
            WHERE o3.USER_ID != {$userId} AND o3.CANCELED = 'N'
        )
        WHERE ob2.PRODUCT_ID NOT IN (" . implode(',', array_map('intval', $myProductIds)) . ")
        GROUP BY ob2.PRODUCT_ID
        ORDER BY score DESC
        LIMIT {$limit}
    ");

    $ids = [];
    while ($row = $res->Fetch()) $ids[] = (int)$row['PRODUCT_ID'];
    return $ids;
}

Фільтрування рекомендованих товарів

Рекомендовані ID передаються в фінальний фільтр перед відображенням — вилучити неактивні, зняті з продажу, з нульовим залишком:

$availableIds = \CIBlockElement::GetList(
    ['SORT' => 'ASC'],
    [
        'ID'        => $recommendedIds,
        'ACTIVE'    => 'Y',
        'IBLOCK_ID' => CATALOG_IBLOCK_ID,
        '>CATALOG_QUANTITY' => 0,
    ],
    false,
    ['nTopCount' => 8],
    ['ID']
)->fetchAll();

Кеш та інвалідація

Кеш рекомендацій item-based: за PRODUCT_ID, TTL = 4 години (синхронно з агентом оновлення). Кеш user-based: за USER_ID, TTL = 30 хвилин — коротше, тому що історія користувача змінюється. Інвалідація: при збереженні нового замовлення (OnSaleOrderSaved) скинути кеш для всіх товарів із замовлення за тегом product_recs_{id}.