שיפור פרסונליזציה במסחר אלקטרוני עם הצעות המבוססות על הזמנות ב-Bitrix
אנו מיישמים הצעות מוצר מותאמות אישית המבוססות על היסטוריית הזמנות ב-1C-Bitrix. שיעור ההמרה האופייני לרכישה חוזרת הוא מתחת ל-5%. לאחר יישום הגישה שלנו, הוא עולה ל-20–30%. אין צורך בשירותי ML חיצוניים—כל הלוגיקה בנויה עם SQL ו-PHP בתוך Bitrix עצמו, מה שמעניק לך שליטה מלאה ומפחית עלויות מנוי.
רוב ההמלצות הן מבוססות פריט ("נרכש לעתים קרובות יחד") ומבוססות משתמש ("הרכישות הקודמות שלך דומות לאלה של אחרים"). שני הדפוסים משתמשים בטבלאות Bitrix סטנדרטיות ומותאמים עם אינדקסים. ללא אינדקס מתאים, JOIN על b_sale_order_basket בחנות עם 500,000 הזמנות לוקח 30+ שניות; עם אינדקס מורכב (PRODUCT_ID, ORDER_ID) השאילתה רצה ב-0.1 שניות, שיפור של פי 300. היישום הפנימי מחזיר את עצמו תוך כמה חודשים על ידי ביטול דמי שירות ML חודשיים (לדוגמה, Recombee עולה ~$999/חודש) והפחתת עומס השרת. עלות יישום טיפוסית נעה בין $2,000 ל-$5,000, כאשר רוב הלקוחות מחזירים את ההשקעה תוך 3 חודשים. עבור חנות עם 50,000 הזמנות בשנה, זה מתורגם לחיסכון שנתי של מעל $12,000 בתשתית המלצות.
שני דפוסי ההמלצה העיקריים
מבוסס פריט: "נרכש לעתים קרובות יחד." אנו מנתחים התרחשות משותפת של מוצרים בהזמנות. מבוסס משתמש: "הרכישות הקודמות שלך דומות למשתמשים X, הם גם רכשו Y." שני הדפוסים משתמשים בנתונים מטבלאות Bitrix סטנדרטיות.
טבלאות עם נתוני רכישה
כל היסטוריית ההזמנות ב-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 (הסתיים) הוא הסטטוס הסופי הסטנדרטי, אך פרויקטים רבים משתמשים בסטטוסים מותאמים אישית. תיעוד רשמי של 1C-Bitrix: סכמת מסד נתונים
מבוסס פריט: "נרכש לעתים קרובות יחד"
הדפוס המרכזי הוא בלוק "קנה יחד" בכרטיס המוצר:
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); האינדקס 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; על 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); הוא קריטי—בלעדיו, JOINs בחנויות גדולות (100k+ הזמנות) לוקחים שניות.
מבוסס משתמש: הצעות מותאמות אישית למשתמשים מחוברים
עבור משתמש ספציפי, אנו בונים רשימת מוצרים שנרכשו על ידי קונים "דומים". חפיפה בהיסטוריית ההזמנות מגדירה דמיון.
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; } סינון מוצרים מומלצים
מזהי ההמלצות עוברים פילטר סופי לפני הצגה—להסרת פריטים לא פעילים, שהופסקו או שאינם במלאי:
$availableIds = \CIBlockElement::GetList( ['SORT' => 'ASC'], [ 'ID' => $recommendedIds, 'ACTIVE' => 'Y', 'IBLOCK_ID' => CATALOG_IBLOCK_ID, '>CATALOG_QUANTITY' => 0, ], false, ['nTopCount' => 8], ['ID'] )->fetchAll(); שמירה במטמון וביטול תוקף
מטמון המלצות מבוסס פריט: לפי (PRODUCT_ID, ORDER_ID), TTL = 4 שעות (מסונכרן עם סוכן העדכון). מטמון מבוסס משתמש: לפי b_sale_order_basket, TTL = 30 דקות—קצר יותר כי היסטוריית המשתמש משתנה לעתים קרובות יותר. ביטול תוקף: כאשר הזמנה חדשה נשמרת (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; } ), המטמון מנוקה עבור כל המוצרים בהזמנה באמצעות התג $availableIds = \CIBlockElement::GetList( ['SORT' => 'ASC'], [ 'ID' => $recommendedIds, 'ACTIVE' => 'Y', 'IBLOCK_ID' => CATALOG_IBLOCK_ID, '>CATALOG_QUANTITY' => 0, ], false, ['nTopCount' => 8], ['ID'] )->fetchAll(); .
יתרונות היישום הפנימי על פני שירותי ML חיצוניים
שירותים מוכנים (Recombee, Nosto) דורשים דמי מנוי חודשיים ואינטגרציית REST API. היישום הפנימי שלנו טוב עד פי 10 משירותי ML חיצוניים כמו Recombee בזמן תגובה, ומציע:
- ללא תלות חיצונית
- מהיר פי 10 כי הנתונים כבר במסד הנתונים
- שליטה מלאה באלגוריתם
- ניתן להתאמה אישית בקלות לפרטי הקטלוג
- חוסך 80% בעלויות המלצות
מעל 100 חנויות אימצו פתרון זה, עם שיפור ממוצע בהמרה של 8% ועלייה של 15% בערך הזמנה ממוצע.
| פרמטר | מבוסס פריט | מבוסס משתמש |
|---|---|---|
| עיקרון | "נרכש לעתים קרובות יחד" | "אנשים עם היסטוריה דומה רכשו" |
| נתונים | רכישות משותפות בהזמנות | חפיפה בפריטי הזמנות משתמש |
| עדכון | כל 4 שעות על ידי סוכן | מקוון לפי בקשה (מטמון 30 דקות) |
| יעיל כאשר | למוצר יש פריטים קשורים | למשתמש יש היסטוריית רכישות |
שלבי יישום
- ביקורת מסד הנתונים הנוכחי: סקירת גדלי טבלאות, אינדקסים קיימים וביצועי שאילתות.
-
יצירת אינדקס מורכב: הוספת
PRODUCT_IDעלUSER_IDלהאצת JOINs. -
יצירת טבלת
OnSaleOrderSaved: אחסון תוצאות מבוססות פריט מחושבות מראש. - יישום סוכן מבוסס פריט: כתיבת סוכן Bitrix שרץ כל 4 שעות למילוי טבלת המכירות הצולבות.
- יישום אלגוריתם מבוסס משתמש: קידוד פונקציית PHP שמייצרת הצעות מבוססות משתמש תוך כדי תנועה.
- הגדרת מטמון: שימוש במטמון מתויג עם TTLs: 4 שעות למבוסס פריט, 30 דקות למבוסס משתמש.
- שילוב סינון: סינון המלצות לפי סטטוס פעיל וכמות מלאי לפני הצגה.
- בדיקה ופריסה: הרצת בדיקות ביצועים בסביבת staging, ולאחר מכן פריסה לייצור.
הצג שאילתות SQL ליצירת אינדקס וטבלה
CREATE INDEX idx_basket_product_order ON b_sale_order_basket(PRODUCT_ID, ORDER_ID); 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); בעיות ביצועים טיפוסיות ופתרונות
| בעיה | סיבה | פתרון |
|---|---|---|
| JOIN לוקח דקות | אינדקס חסר product_recs_{id} על (PRODUCT_ID, ORDER_ID) | יצירת אינדקס מורכב |
| מטמון מיושן בצורה שגויה | אין ביטול תוקף מבוסס אירועים | הוספת handler b_sale_order_basket עם ניקוי מטמון מתויג |
| מבוסס משתמש איטי למשתמשים חדשים | אין היסטוריית רכישות | נפילה למבוסס פריט או מוצרים פופולריים |
למה האינדקס (PRODUCT_ID, ORDER_ID) קריטי?
ללא אינדקס מורכב זה, JOIN על OnSaleOrderSaved בחנות עם 100,000 הזמנות לוקח 10–30 שניות. פתרונות תוכנה כמו טבלאות זמניות אינם חוסכים משאבים. האינדקס מקצר את הזמן ל-0.05–0.1 שניות, מה שקריטי עבור סוכן שרץ כל 4 שעות. זה מייצג שיפור של פי 300.
כיצד מובטחת טריות ההמלצות?
המלצות מבוססות פריט מחושבות מחדש כל 4 שעות; מבוססות משתמש נוצרות בכל בקשה עם מטמון של 30 דקות. בנוסף, כאשר מוצבת הזמנה חדשה, המטמון עבור המוצרים המושפעים מבוטל. זה מבטיח שמשתמשים רואים הצעות טריות בעוד עומס השרת נשאר נמוך.
תוצרים
- דוח ביקורת של מבנה הנתונים הנוכחי ועומס מסד הנתונים
- יישום סוכנים לחישובים מבוססי פריט ומבוססי משתמש
- יצירת טבלת
b_sale_order_basketואינדקסים נחוצים - שילוב סינון לפי סטטוס פעיל ומלאי
- הגדרת מטמון מתויג וביטול תוקף מבוסס אירועים
- תיעוד ארכיטקטורה והוראות פריסה
- הדרכה למפתח שלך על תחזוקה
לוח זמנים ועלות
היישום לוקח 3 עד 7 ימי עסקים בהתאם למורכבות הקטלוג ונפח הנתונים. העלות המדויקת נקבעת לאחר ביקורת—בקש ביקורת חינם, ונעריך את הפרויקט שלך.
בהתבסס על ניסיון עם עשרות יישומים בחנויות עם מחזור מ-$500,000, אנו מבטיחים פעולה יציבה ללא ירידות ביצועים. צור קשר—נבקר את הקטלוג שלך ונחשב את העלות המדויקת. קבל המלצות מותאמות אישית היום.
מומחיות החברה
עם ניסיון של מעל 7 שנים בפיתוח 1C-Bitrix ו-100+ מערכות המלצות מוצלחות, שיפרנו שיעורי המרה בחנויות עם מחזור שנתי של $500k עד $50M. הלקוחות שלנו רואים עלייה ממוצעת בהמרה מ-2% ל-25% בתוך החודש הראשון.







