Elasticsearch עבור Bitrix: חיפוש מהיר פי 10

כאשר החיפוש הסטנדרטי ב-1C-Bitrix מאט בקטלוגים גדולים, אנחנו מציעים פתרון — שילוב Elasticsearch. הצוות שלנו מספק את הפרויקט במפתח מלא, ומבטיח חיפוש מהיר ומדויק גם עם מיליוני מוצרים. התוצאה היא מערכת אמינה שגדלה עם העסק שלך ונתמכת על ידינו בכל השלבים.
השירותים שאנו מציעים
מציג 1 מתוך 1כל 1626 השירותים
Elasticsearch עבור Bitrix: חיפוש מהיר פי 10
בינוני
~1-2 שבועות

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

שאלות נפוצות

כאשר החיפוש המובנה של Bitrix מפסיק לעבוד

עם 100 אלף מוצרים, החיפוש המובנה המבוסס על MySQL מאט: שאילתות ל-b_search_content לוקחות 300–500 אלפיות השנייה, ובמסד נתונים עם מיליון רשומות — מתרחש timeout. לקוחות מתלוננים על זמני חיפוש ארוכים, מיון לפי מחיר לא עובד, ופילטרים (facets) מעמיסים על מסד הנתונים. לדוגמה, פרויקט אחרון עם קטלוג של 250,000 מוצרים: החיפוש הסטנדרטי החזיר תוצאות תוך 2–3 שניות, ופילטרים מורכבים לפי מאפיינים (מחיר, מותג, מידה) גרמו להשהיות של 30 שניות. Elasticsearch פותר זאת באופן קיצוני: זמן תגובה של 5–30 אלפיות השנייה, אגרגציות בזמן אמת, טעויות הקלדה ומורפולוגיה מובנים כברירת מחדל. הניסיון שלנו — 30+ אינטגרציות ES עם Bitrix, ואנחנו מבטיחים תוצאה סופית. אנו משתמשים ברכיבים מורשים ומוסמכים על ידי 1C-Bitrix. כדי להבין אם Elasticsearch מתאים לפרויקט שלך, הזמינו אודיט — ננתח את העומס ומבנה הנתונים.

בעיות ש-Elasticsearch פותר

Elasticsearch מחליף את מנוע החיפוש הסטנדרטי של Bitrix ופותר שלוש בעיות מרכזיות:

  1. מהירות חיפוש טקסט מלא. MySQL FULLTEXT מתחיל להאט ב-50–100 אלף מסמכים. ES מתמודד עם מיליונים.
  2. חיפוש פאקטלי (אגרגציות). הפילטרים המובנים של Bitrix הם שאילתות נפרדות לכל מאפיין. ES מחזיר אגרגציות בבקשה אחת, ומעניק רווח של פי 10–20 בפילטרים מורכבים.
  3. חיפוש עם טעויות הקלדה ומילים נרדפות. ללא מודולים חיצוניים, MySQL לא תומך בחיפוש משוער (fuzzy). ל-ES יש fuzziness ומילים נרדפות מובנים.

אם אתם נתקלים בבעיות דומות, צרו קשר לאודיט — זה יעזור לזהות צווארי בקבוק.

למה Elasticsearch מהיר יותר מ-MySQL FULLTEXT?

מאפיין MySQL FULLTEXT Elasticsearch
סוג אינדקס B-tree + קובץ הפוך אינדקס הפוך + FST
מורפולוגיה נדרשים מילונים חיצוניים (morphy) Stemmer ומנתחים (רוסית)
חיפוש טעויות הקלדה לא נתמך Fuzziness (AUTO)
אגרגציות (פאקטים) לא נתמך נתמך, בשאילתה אחת
מהירות על מיליון מסמכים (שאילתה בודדת) 200–500 אלפיות השנייה 5–30 אלפיות השנייה

Elasticsearch מהיר פי 10–50 מ-MySQL FULLTEXT על נתונים גדולים.

ארכיטקטורת אינטגרציה

האינטגרציה מורכבת משלושה חלקים:

  1. אינדקסר (Indexer) — רכיב שקורא נתונים מ-Bitrix (בלוקי מידע, משתמשים, עמודים) וכותב מסמכים לאינדקס Elasticsearch.
  2. שער חיפוש (Search gateway) — מחליף בקשות סטנדרטיות ל-b_search_content בבקשות ל-Elasticsearch API. השער מיושם כ-proxy ב-PHP: הוא מקבל בקשה מהרכיב הסטנדרטי bitrix:search.page, הופך אותה ל-Elasticsearch query DSL, ומחזיר תוצאות בפורמט ש-Bitrix מצפה לו.
  3. מטפלי אירועים (Event handlers) — מעדכנים את האינדקס כאשר ישויות משתנות או נמחקות.

מבנה אינדקס לקטלוג מוצרים

האינדקס נוצר באמצעות Elasticsearch Mapping API. דוגמה ל-mapping למוצרים:

PUT /bitrix_catalog
{
  "mappings": {
    "properties": {
      "id": {
        "type": "integer"
      },
      "iblock_id": {
        "type": "integer"
      },
      "name": {
        "type": "text",
        "analyzer": "russian"
      },
      "description": {
        "type": "text",
        "analyzer": "russian"
      },
      "sku": {
        "type": "keyword"
      },
      "price": {
        "type": "float"
      },
      "active": {
        "type": "boolean"
      },
      "section_id": {
        "type": "integer"
      },
      "properties": {
        "type": "object"
      },
      "updated_at": {
        "type": "date"
      }
    }
  },
  "settings": {
    "analysis": {
      "analyzer": {
        "russian": {
          "type": "custom",
          "tokenizer": "standard",
          "filter": [
            "lowercase",
            "russian_stop",
            "russian_stemmer"
          ]
        }
      },
      "filter": {
        "russian_stemmer": {
          "type": "stemmer",
          "language": "russian"
        },
        "russian_stop": {
          "type": "stop",
          "stopwords": "_russian_"
        }
      }
    }
  }
}

המנתח PUT /bitrix_catalog { "mappings": { "properties": { "id": { "type": "integer" }, "iblock_id": { "type": "integer" }, "name": { "type": "text", "analyzer": "russian" }, "description": { "type": "text", "analyzer": "russian" }, "sku": { "type": "keyword" }, "price": { "type": "float" }, "active": { "type": "boolean" }, "section_id": { "type": "integer" }, "properties": { "type": "object" }, "updated_at": { "type": "date" } } }, "settings": { "analysis": { "analyzer": { "russian": { "type": "custom", "tokenizer": "standard", "filter": ["lowercase", "russian_stop", "russian_stemmer"] } }, "filter": { "russian_stemmer": { "type": "stemmer", "language": "russian" }, "russian_stop": { "type": "stop", "stopwords": "_russian_" } } } } } עם stemmer הוא ההבדל המרכזי מ-MySQL FULLTEXT, שבלי מילונים נוספים לא מבין מורפולוגיה.

דוגמה לתצורת מנתח עם מילים נרדפות
PUT /bitrix_catalog/_settings
{
  "analysis": {
    "filter": {
      "russian_synonyms": {
        "type": "synonym",
        "synonyms": [
          "брюки, штаны, джинсы => trousers",
          "смартфон, телефон, мобила => mobile"
        ]
      }
    },
    "analyzer": {
      "russian_with_synonyms": {
        "tokenizer": "standard",
        "filter": ["lowercase", "russian_stop", "russian_stemmer", "russian_synonyms"]
      }
    }
  }
}

איך להגדיר עדכון אינדקס אוטומטי?

הירשמו לאירועי בלוקי מידע:

// local/php_interface/init.php
AddEventHandler('iblock', 'OnAfterIBlockElementUpdate', 'esUpdateProduct');
AddEventHandler('iblock', 'OnAfterIBlockElementDelete', 'esDeleteProduct');

function esUpdateProduct(array &$arFields): void
{
    $client = getEsClient();
    $productId = (int)$arFields['ID'];
    // Переиндексировать один документ
    $client->index([
        'index' => 'bitrix_catalog',
        'id' => $productId,
        'body' => buildProductDocument($productId),
    ]);
}

function esDeleteProduct(int $productId): void
{
    getEsClient()->delete(['index' => 'bitrix_catalog', 'id' => $productId]);
}

האירוע russian מופעל גם על שינויים דרך API (ייבוא 1C), שחשוב לרעננות האינדקס.

אינדוקס נתונים

אינדוקס ראשוני רץ דרך סקריפט cron. הנתונים נקראים בקבוצות באמצעות PUT /bitrix_catalog/_settings { "analysis": { "filter": { "russian_synonyms": { "type": "synonym", "synonyms": [ "брюки, штаны, джинсы => trousers", "смартфон, телефон, мобила => mobile" ] } }, "analyzer": { "russian_with_synonyms": { "tokenizer": "standard", "filter": ["lowercase", "russian_stop", "russian_stemmer", "russian_synonyms"] } } } } עם // local/php_interface/init.php AddEventHandler('iblock', 'OnAfterIBlockElementUpdate', 'esUpdateProduct'); AddEventHandler('iblock', 'OnAfterIBlockElementDelete', 'esDeleteProduct'); function esUpdateProduct(array &$arFields): void { $client = getEsClient(); $productId = (int)$arFields['ID']; // Переиндексировать один документ $client->index([ 'index' => 'bitrix_catalog', 'id' => $productId, 'body' => buildProductDocument($productId), ]); } function esDeleteProduct(int $productId): void { getEsClient()->delete(['index' => 'bitrix_catalog', 'id' => $productId]); } ו-offset כדי למנוע עומס זיכרון:

\Bitrix\Main\Loader::includeModule('iblock');
$client = \Elasticsearch\ClientBuilder::create()
    ->setHosts(['localhost:9200'])
    ->build();
$offset = 0;
$batchSize = 100;
do {
    $res = \CIBlockElement::GetList(
        [],
        ['IBLOCK_ID' => CATALOG_IBLOCK_ID, 'ACTIVE' => 'Y'],
        false,
        ['nTopCount' => $batchSize, 'nPageSize' => $batchSize, 'iNumPage' => floor($offset / $batchSize) + 1],
        ['ID', 'NAME', 'DETAIL_TEXT', 'IBLOCK_ID', 'IBLOCK_SECTION_ID']
    );
    $bulk = [];
    while ($item = $res->GetNext()) {
        $bulk[] = ['index' => ['_index' => 'bitrix_catalog', '_id' => $item['ID']]];
        $bulk[] = [
            'id' => (int)$item['ID'],
            'iblock_id' => (int)$item['IBLOCK_ID'],
            'name' => $item['NAME'],
            'description'=> strip_tags($item['DETAIL_TEXT']),
            'section_id' => (int)$item['IBLOCK_SECTION_ID'],
            'active' => true,
            'updated_at' => date('c'),
        ];
        $offset++;
    }
    if (!empty($bulk)) {
        $client->bulk(['body' => $bulk]);
    }
} while ($res->SelectedRowsCount() === $batchSize);

Bulk API מאפשר שליחת עד 1000 מסמכים בבקשה אחת. אל תשתמשו בבקשות OnAfterIBlockElementUpdate בודדות לאינדוקס ראשוני — זה איטי פי 10–50.

שאילתת חיפוש

החליפו את הרכיב הסטנדרטי CIBlockElement::GetList() ברכיב מותאם אישית שמבצע שאילתות ל-Elasticsearch:

$response = $client->search([
    'index' => 'bitrix_catalog',
    'body' => [
        'query' => [
            'multi_match' => [
                'query' => $searchQuery,
                'fields' => ['name^3', 'description', 'sku'],
                'type' => 'best_fields',
                'fuzziness' => 'AUTO',
            ],
        ],
        'sort' => ['_score' => ['order' => 'desc']],
        'from' => ($page - 1) * $pageSize,
        'size' => $pageSize,
    ],
]);

הפרמטר nTopCount = 100 מספק חיפוש טעויות הקלדה: למילים עד 5 תווים מותרת החלפה אחת; למילים ארוכות יותר — 2 החלפות.

מה כלול בעבודה שלנו?

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

לוח זמנים ליישום

היקף רכיבים משך
בסיסי התקנת ES, mapping, אינדקסר, שער חיפוש 5–7 ימים
מלא חיפוש פאקטלי באמצעות אגרגציות, הצעות (suggest), מילים נרדפות, השלמה אוטומטית 10–14 ימים

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