הגדרת צבירת פנים (Facet Aggregation) של Elasticsearch עבור 1C-Bitrix

סינון קטלוג עם עשרות אלפי מוצרים באמצעות MySQL אורך דקות. Elasticsearch עם צבירת פנים מקצר את הזמן לעשרות אלפיות השנייה. אבל החיפוש הסטנדרטי של Bitrix (search) אינו יכול להחזיר נתונים מצטברים עבור מסננים. אנו מבצעים אינטגרציה מותאמת אישית באמצעות ה-
השירותים שאנו מציעים
מציג 1 מתוך 1כל 1626 השירותים
הגדרת צבירת פנים (Facet Aggregation) של Elasticsearch עבור 1C-Bitrix
פשוט
~1 יום

הכישורים שלנו:

שאלות נפוצות

העבודות האחרונות

  • image_website-b2b-advance_0.webp
    פיתוח אתר חברה B2B ADVANCE
    1458
  • image_bitrix-bitrix-24-1c_fixper_448_0.webp
    פיתוח אתר לחברת FIXPER
    1019
  • image_bitrix-bitrix-24-1c_development_of_an_online_appointment_booking_widget_for_a_medical_center_594_0.webp
    פיתוח מבוסס Bitrix, Bitrix24, 1C לחברה פיתוח ווידג'ט להזמנת תורים אונליין למרכז רפואי
    761
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    פיתוח על בסיס 1C Enterprise עבור MIRSANBEL
    879
  • image_crm_dolbimby_434_0.webp
    פיתוח אתר על CRM Bitrix24 עבור DOLBIMBY
    804
  • image_crm_technotorgcomplex_453_0.webp
    פיתוח על בסיס Bitrix24 עבור חברת TECHNOTORGKOMPLEKS
    1162

סינון קטלוג עם עשרות אלפי מוצרים באמצעות MySQL אורך דקות. Elasticsearch עם צבירת פנים (facet aggregation) מקצר את הזמן לעשרות מילישניות. אבל החיפוש הסטנדרטי של ביטריקס (search) אינו יכול להחזיר נתונים מצטברים עבור מסננים. אנו מבצעים אינטגרציה מותאמת אישית דרך הלקוח הרשמי elasticsearch/elasticsearch. הגדרת צבירת פנים של Elasticsearch עבור 1C-Bitrix היא דרך להאיץ סינון קטלוג. הניסיון מראה: גישה זו מאיצה את טעינת דף המסנן ב-95% ומאפשרת לראות מיידית ספירות מוצרים עבור כל ערך מסנן.

הבעיה: עם 50,000 מוצרים, MySQL מבצע קיבוץ לפי מאפיינים ב-2–5 שניות, וכל מסנן דורש שאילתה נפרדת. Elasticsearch מחזיר גם את המוצרים עצמם וגם צבירות לפי מותגים, מחירים, מאפיינים בשאילתה אחת. זה נקרא צבירת פנים.

כיצד צבירת פנים מאיצה סינון

צבירות — שאילתה שמחזירה בו-זמנית תוצאות חיפוש וסטטיסטיקות לפי שדות: ספירת מסמכים עבור כל ערך מסנן. שאילתה אחת ל-Elasticsearch מחליפה N שאילתות ל-MySQL לספירת כל פן.

דוגמה: קטלוג מחשבים ניידים. שאילתה אחת מחזירה:

  • 240 מוצרים התואמים למסנן הנוכחי
  • לפי מותג: ASUS (45), Dell (38), HP (31)...
  • לפי זיכרון RAM: 8 GB (89), 16 GB (104), 32 GB (47)
  • לפי אלכסון: 15.6" (130), 14" (65)...

אלה הם פנים.

למה Elasticsearch מהיר יותר מ-MySQL עבור פנים

MySQL עם קיבוץ לפי מספר מאפיינים יוצר שאילתות כבדות עם GROUP BY ומספר JOIN. עם 50,000 מוצרים, שאילתה כזו אורכת 2–5 שניות. Elasticsearch מעבד את אותה צבירה ב-50–300 אלפיות השנייה.

פרמטר MySQL (CIBlockElement::GetList עם קיבוץ) Elasticsearch (צבירות)
זמן שאילתה עבור 50,000 מוצרים 2–5 שניות 50–300 אלפיות השנייה
מספר שאילתות לעמוד 1 ראשית + N לכל פן 1
קנה מידה למיליון מוצרים הידרדרות ל-30+ שניות 500 אלפיות השנייה – 2 שניות
תמיכה במסננים משולבים HAVING מורכב post_filter ו-nested

החיסכון במשאבי שרת משמעותי.

כיצד להגדיר מיפוי עבור שדות פנים

עבור צבירת פנים, שדות חייבים להיות keyword (ערך מדויק) או integer/float עבור טווחים מספריים. שדות מסוג text אינם ניתנים לצבירה (או נצברים לפי טוקנים, דבר שאינו הגיוני עבור פנים).

מיפוי בעת יצירת אינדקס:

curl -X PUT http://localhost:9200/bitrix_catalog_s1 \ -H "Content-Type: application/json" \ -d '{ "mappings": { "properties": { "title": { "type": "text", "analyzer": "russian", "fields": { "keyword": {"type": "keyword"} } }, "brand": {"type": "keyword"}, "price": {"type": "float"}, "category_id": {"type": "integer"}, "properties": { "type": "nested", "properties": { "code": {"type": "keyword"}, "value": {"type": "keyword"}, "value_num": {"type": "float"} } } } } }' 

מאפייני מוצר מאוחסנים כאובייקטים של curl -X PUT http://localhost:9200/bitrix_catalog_s1 \ -H "Content-Type: application/json" \ -d '{ "mappings": { "properties": { "title": { "type": "text", "analyzer": "russian", "fields": { "keyword": {"type": "keyword"} } }, "brand": {"type": "keyword"}, "price": {"type": "float"}, "category_id": {"type": "integer"}, "properties": { "type": "nested", "properties": { "code": {"type": "keyword"}, "value": {"type": "keyword"}, "value_num": {"type": "float"} } } } } }' — זה מאפשר סינון נכון של שילובי ערכים של אותו מאפיין. עוד על מיפוי בתיעוד Elasticsearch.

כיצד ליישם פוסט-פילטר עבור פנים בלתי תלויים

בעיה סטנדרטית: בעת בחירת מסנן «מותג: ASUS», צבירת המותגים עדיין צריכה להראות את כל המותגים עם ספירות נוכחיות — אחרת המשתמש לא יכול לעבור ל-Dell. כאן משתמשים ב-nested: סינון מוחל על תוצאות, אך לא על צבירות.

{ "query": {"match_all": {}}, "post_filter": {"term": {"brand": "ASUS"}}, "aggs": { "brands": {"terms": {"field": "brand"}} } } 

הצבירה מחושבת על כל הבסיס, התוצאות מסוננות לפי ASUS. המשתמש רואה את רשימת המותגים המלאה ויכול לעבור.

שאילתה עם צבירות מ-PHP

מחלקה לעבודה עם Elasticsearch דרך הלקוח הרשמי post_filter:

use Elasticsearch\ClientBuilder; class CatalogElasticSearch { private $client; private $index = 'bitrix_catalog_s1'; public function __construct() { $this->client = ClientBuilder::create() ->setHosts(['localhost:9200']) ->build(); } public function getFacets(array $filters = [], string $query = ''): array { $must = []; if ($query) { $must[] = ['match' => ['title' => $query]]; } foreach ($filters as $code => $values) { $must[] = [ 'nested' => [ 'path' => 'properties', 'query' => [ 'bool' => [ 'must' => [ ['term' => ['properties.code' => $code]], ['terms' => ['properties.value' => (array)$values]] ] ] ] ] ]; } $params = [ 'index' => $this->index, 'body' => [ 'query' => ['bool' => ['must' => $must]], 'aggs' => [ 'brands' => [ 'terms' => ['field' => 'brand', 'size' => 50] ], 'price_range' => [ 'range' => [ 'field' => 'price', 'ranges' => [ ['to' => 10000], ['from' => 10000, 'to' => 30000], ['from' => 30000, 'to' => 60000], ['from' => 60000] ] ] ], 'properties_facets' => [ 'nested' => ['path' => 'properties'], 'aggs' => [ 'prop_codes' => [ 'terms' => ['field' => 'properties.code', 'size' => 20], 'aggs' => [ 'prop_values' => [ 'terms' => ['field' => 'properties.value', 'size' => 100] ] ] ] ] ] ], 'size' => 24, 'from' => 0 ] ]; return $this->client->search($params); } } 

אינדוקס מוצרי ביטריקס

נתונים לאינדוקס נאספים דרך { "query": {"match_all": {}}, "post_filter": {"term": {"brand": "ASUS"}}, "aggs": { "brands": {"terms": {"field": "brand"}} } } ונשלחים ל-Elasticsearch בקבוצות באמצעות Bulk API:

function indexCatalogToElastic(int $iblockId): void { $client = ClientBuilder::create()->setHosts(['localhost:9200'])->build(); $batchSize = 200; $offset = 0; do { $res = CIBlockElement::GetList( [], ['IBLOCK_ID' => $iblockId, 'ACTIVE' => 'Y'], false, ['nTopCount' => $batchSize, 'nPageSize' => $batchSize, 'iNumPage' => ($offset / $batchSize) + 1], ['ID', 'NAME', 'DETAIL_TEXT', 'PROPERTY_BRAND', 'PROPERTY_*'] ); $body = []; $count = 0; while ($el = $res->GetNextElement()) { $fields = $el->GetFields(); $props = $el->GetProperties(); $properties = []; foreach ($props as $code => $prop) { if (!empty($prop['VALUE'])) { $properties[] = [ 'code' => $code, 'value' => is_array($prop['VALUE']) ? implode(', ', $prop['VALUE']) : $prop['VALUE'] ]; } } $body[] = ['index' => ['_index' => 'bitrix_catalog_s1', '_id' => $fields['ID']]]; $body[] = [ 'title' => $fields['NAME'], 'brand' => $props['BRAND']['VALUE'] ?? '', 'properties' => $properties ]; $count++; } if (!empty($body)) { $client->bulk(['body' => $body]); } $offset += $batchSize; } while ($count === $batchSize); } 

כיצד לעדכן אינדקסים: השוואת גישות

שיטה מהירות עומס על מסד הנתונים מתאים ל
אינדוקס מלא מחדש איטי (שעות) גבוה ריצה ראשונית
עדכון מצטבר מהיר (דקות) נמוך שינויים שוטפים

מומלץ לשלב בין השניים: אינדוקס מלא פעם ביום, עדכון מצטבר דרך סוכנים. דוגמה להגדרת סוכן לעדכון מצטבר:

בקובץ elasticsearch/elasticsearch הוסף:

CAgent::AddAgent( "CatalogElasticSearch::incrementalUpdate();", "elastic", "N", 60, date('Y-m-d H:i:s'), "Y", date('Y-m-d H:i:s'), 30 ); 

הפונקציה use Elasticsearch\ClientBuilder; class CatalogElasticSearch { private $client; private $index = 'bitrix_catalog_s1'; public function __construct() { $this->client = ClientBuilder::create() ->setHosts(['localhost:9200']) ->build(); } public function getFacets(array $filters = [], string $query = ''): array { $must = []; if ($query) { $must[] = ['match' => ['title' => $query]]; } foreach ($filters as $code => $values) { $must[] = [ 'nested' => [ 'path' => 'properties', 'query' => [ 'bool' => [ 'must' => [ ['term' => ['properties.code' => $code]], ['terms' => ['properties.value' => (array)$values]] ] ] ] ] ]; } $params = [ 'index' => $this->index, 'body' => [ 'query' => ['bool' => ['must' => $must]], 'aggs' => [ 'brands' => [ 'terms' => ['field' => 'brand', 'size' => 50] ], 'price_range' => [ 'range' => [ 'field' => 'price', 'ranges' => [ ['to' => 10000], ['from' => 10000, 'to' => 30000], ['from' => 30000, 'to' => 60000], ['from' => 60000] ] ] ], 'properties_facets' => [ 'nested' => ['path' => 'properties'], 'aggs' => [ 'prop_codes' => [ 'terms' => ['field' => 'properties.code', 'size' => 20], 'aggs' => [ 'prop_values' => [ 'terms' => ['field' => 'properties.value', 'size' => 100] ] ] ] ] ] ], 'size' => 24, 'from' => 0 ] ]; return $this->client->search($params); } } בודקת את הטבלה CIBlockElement::GetList לשינויים בדקה האחרונה ושולחת מסמכים מעודכנים.

איך אנחנו עושים זאת: תהליך ההתקנה

  1. ביקורת — ניתוח מבנה הקטלוג הנוכחי, מאפיינים, מספר מוצרים, עומס.
  2. תכנון — הגדרת מיפוי, הגדרות שרד, רפליקות, מדיניות אינדוקס.
  3. פיתוח — כתיבת מחלקת אינדוקס, אינטגרציה עם ביטריקס (סוכנים, אירועים), יישום מסנן עם פוסט-פילטר.
  4. בדיקות — השוואת מהירות MySQL ו-Elasticsearch, אימות נכונות צבירות תחת שילובים שונים.
  5. פריסה — הגדרת ניטור, גיבויים, תיעוד.

מה כלול בעבודה

  • הגדרה ואופטימיזציה של אינדקס Elasticsearch למבנה הקטלוג
  • קוד אינדוקס (Bulk API) עם אינטגרציה דרך סוכני ביטריקס
  • יישום רכיב מסנן עם פנים ופוסט-פילטר
  • בדיקות ביצועים על הנתונים שלך
  • תיעוד והדרכת מנהלים
  • אחריות על פעולת האינדוקס ונכונות הצבירות

לוחות זמנים ואחריות

לוחות זמנים משוערים — מ-3 עד 7 ימי עסקים בהתאם למורכבות הקטלוג. העלות מחושבת באופן אישי. יש לנו ניסיון של שנים והשלמנו 50+ פרויקטים של אינטגרציית Elasticsearch עם 1C-Bitrix. אנו מספקים אחריות על פעולת האינדוקס ונכונות הפנים.

קבל ייעוץ על הגדרת Elasticsearch לקטלוג שלך. ספר לנו על הקטלוג שלך — נכין תוכנית אינטגרציה והצעת מחיר.