כאשר קטלוג מוצרים ב-1C-Bitrix גדל ל-200 אלף פריטים, החיפוש הסטנדרטי מפסיק להתמודד — זמן התגובה עובר את 10 השניות. בפרויקט אחד עם קטלוג של 500 אלף מוצרים, חיפוש דרך LIKE ארך עד 18 שניות.
לאחר הטמעת FULLTEXT עם מנתח ngram, הזמן ירד ל-0.2 שניות. ביצענו ביקורת, הגדרנו את my.cnf, יצרנו אינדקסים ופיתחנו רכיב מותאם אישית. צרו קשר כדי להעריך את הפרויקט שלכם.
החיפוש הסטנדרטי של Bitrix פועל דרך הרכיב bitrix:search.page ומודול החיפוש. הוא בונה אינדקס משלו בטבלת b_search_content — כל רכיבי האינפובלוקים, הדפים והפורומים נכנסים לשם.
החיפוש פועל דרך LIKE '%query%', אשר עם נפח של למעלה מ-100 אלף רכיבים הופך לסריקת טבלה עם הידרדרות ל-5–15 שניות. הפתרון הוא אינדקסים מלאים של MySQL (FULLTEXT) ישירות על טבלאות האינפובלוקים או על b_search_content.
כיצד FULLTEXT עובד ב-MySQL
אינדקס FULLTEXT נבנה על גבי עמודות טקסט (CHAR, VARCHAR, TEXT). שאילתות מבוצעות באמצעות MATCH() AGAINST(). שני מצבים: IN NATURAL LANGUAGE MODE (דירוג לפי רלוונטיות) ו-IN BOOLEAN MODE (תמיכה באופרטורים: +required, -exclude, *prefix).
מגבלות FULLTEXT ב-MySQL:
- אורך מילה מינימלי כברירת מחדל:
innodb_ft_min_token_size = 3. מילים קצרות מכך אינן מאונדקסות. - מילות עצירה (innodb_ft_server_stopword_table) — יש להשבית אותן או להגדיר אותן מחדש.
- עבור השפה הרוסית נדרש קידוד תקין (utf8mb4) ועדיף מנתח חיצוני (ngram עבור InnoDB או Sphinx/Manticore כחלופה).
בחירת מנוע חיפוש וארכיטקטורת חיפוש
מנתח Ngram עבור קירילית
מנתח ה-FULLTEXT הסטנדרטי של MySQL מכוון לאנגלית: אורך מילה מינימלי של 3 תווים, מילות עצירה. מילים בקירילית הן לעתים קרובות קצרות יותר (מילות יחס, מילות חיבור), ולכן הן אינן מאונדקסות.
מנתח ה-ngram מפצל טקסט לביגרמות (2 תווים כל אחת) ואינו תלוי בשפה. זה מבטיח שכל שאילתה של שני תווים או יותר תמצא התאמות. הגדרת ngram_token_size=2 ב-my.cnf מפעילה מצב ביגרמות.
שימוש באינדקסי FULLTEXT
אם החיפוש נדרש רק עבור הקטלוג, יעיל יותר לאנדקס ישירות את הטבלאות b_iblock_element (NAME, DETAIL_TEXT) ו-b_iblock_section (NAME). זה מפחית את העומס על b_search_content ומפשט את הארכיטקטורה.
עם זאת, עבור חיפוש כלל-אתרי נוח יותר להשתמש ב-b_search_content — הוא כולל את כל סוגי התוכן.
הגדרה ויצירת אינדקסים
הגדרת my.cnf עבור FULLTEXT ברוסית
[mysqld] innodb_ft_min_token_size = 2 innodb_ft_enable_stopword = OFF ngram_token_size = 2
לאחר השינוי, יש ליצור מחדש את כל אינדקסי ה-FULLTEXT — הפעלה מחדש של MySQL בלבד אינה מספיקה.
יצירת אינדקס FULLTEXT על טבלאות b_search_content ואינפובלוקים
טבלת b_search_content היא הנקודה המרכזית של החיפוש ב-Bitrix. עמודות מפתח: TITLE, BODY. עבור הקטלוג אנו indexing את b_iblock_element עם העמודה SEARCHABLE_CONTENT, שאותה Bitrix ממלאת אוטומטית.
ALTER TABLE b_search_content ADD FULLTEXT INDEX ft_search_content (TITLE, BODY) WITH PARSER ngram;
ALTER TABLE b_iblock_element ADD FULLTEXT INDEX ft_iblock_element_search (NAME, SEARCHABLE_CONTENT) WITH PARSER ngram;
WITH PARSER ngram הוא מנתח המובנה ב-MySQL 5.7+ המפצל טקסט לביגרמות/טריגרמות. הוא עובד היטב עבור קירילית ואינו דורש כלים חיצוניים.
ניטור ותחזוקה של אינדקסים
בדיקת מצב האינדקס
-- Statistics of InnoDB FULLTEXT indexes
SELECT * FROM INFORMATION_SCHEMA.INNODB_FT_INDEX_CACHE;
SELECT * FROM INFORMATION_SCHEMA.INNODB_FT_INDEX_TABLE;
-- Forced rebuild
SET GLOBAL innodb_optimize_fulltext_only = ON;
OPTIMIZE TABLE b_search_content;
SET GLOBAL innodb_optimize_fulltext_only = OFF;
שאילתות אלה עוזרות לעקוב אחר אכלוס האינדקס ולבנות אותו מחדש בעת הצורך.
פיתוח קומפוננטה מותאמת אישית עבור FULLTEXT
עקיפת החיפוש ב-Bitrix
bitrix:search.page הסטנדרטי אינו משתמש ב-FULLTEXT — הוא עובד דרך Bitrix ORM עם LIKE. כדי לחבר את FULLTEXT, אנו עוקפים את השאילתה בקומפוננטת עטיפה מותאמת אישית או דרך האירוע OnBeforeIBlockElementGetList.
חיפוש מותאם אישית מינימלי דרך FULLTEXT:
namespace Local\Search;
class FulltextSearcher { private \Bitrix\Main\DB\Connection $db; public function __construct() { $this->db = \Bitrix\Main\Application::getConnection(); }
public function search(string $query, int $page = 1, int $limit = 20): array
{
$query = $this->sanitizeQuery($query);
$offset = ($page - 1) * $limit;
// BOOLEAN MODE with prefix search
$boolQuery = '+' . implode('* +', explode(' ', $query)) . '*';
$sql = "
SELECT sc.ID, sc.TITLE, sc.URL, sc.MODULE_ID, sc.ITEM_ID,
MATCH(sc.TITLE, sc.BODY) AGAINST (? IN BOOLEAN MODE) AS relevance
FROM b_search_content sc
WHERE MATCH(sc.TITLE, sc.BODY) AGAINST (? IN BOOLEAN MODE)
AND sc.SITE_ID = ?
AND sc.PUBLIC = 'Y'
ORDER BY relevance DESC
LIMIT ? OFFSET ?
";
$result = $this->db->query($sql, [$boolQuery, $boolQuery, SITE_ID, $limit, $offset]); $rows = []; while ($row = $result->fetch()) { $rows[] = $row; }
return $rows; } public function count(string $query): int { $boolQuery = '+' . implode('* +', explode(' ', $query)) . '*';
$result = $this->db->query(
"SELECT COUNT(*) AS cnt FROM b_search_content WHERE MATCH(TITLE, BODY) AGAINST (? IN BOOLEAN MODE) AND SITE_ID = ? AND PUBLIC = 'Y'",
[$boolQuery, SITE_ID]
);
return (int)$result->fetch()['cnt'];
}
private function sanitizeQuery(string $query): string
{
$query = preg_replace('/[+\-><()\~*"@]+/', ' ', $query);
$query = preg_replace('/\s+/', ' ', trim($query));
return mb_substr($query, 0, 255);
}
}
קומפוננטת חיפוש מותאמת אישית
תבנית הקומפוננטה משתמשת ב-FulltextSearcher במקום המודול הסטנדרטי:
// /local/components/local/search.fulltext/component.php
if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED !== true) die();
$query = trim($_GET['q'] ?? '');
if (mb_strlen($query) < 2) {
$this->arResult['ITEMS'] = [];
$this->arResult['TOTAL'] = 0;
$this->IncludeComponentTemplate();
return;
}
$searcher = new \Local\Search\FulltextSearcher();
$page = max(1, (int)($_GET['PAGEN_1'] ?? 1));
$this->arResult['ITEMS'] = $searcher->search($query, $page);
$this->arResult['TOTAL'] = $searcher->count($query);
$this->arResult['QUERY'] = htmlspecialchars($query);
$this->arResult['PAGE'] = $page;
$this->SetResultCacheKeys([]); // we do not cache search
$this->IncludeComponentTemplate();
ביצועים והשוואת מנתחים
מדדים השוואתיים
| שיטה | זמן עבור 100 אלף רשומות | שימוש ב-CPU | אינדוקס |
|---|---|---|---|
LIKE %query% |
5–15 שניות | 100% מליבה אחת | לא נדרש |
| FULLTEXT (BOOLEAN) | 0.1–0.5 שניות | 10–20% | נדרש ראשוני |
| מנתח | אורך מילה מינימלי | תמיכה בקירילית | דורש הגדרה |
|---|---|---|---|
| Standard | 3 תווים | חלקית | לא |
| ngram (ביגרמות) | 2 תווים | מלאה | ngram_token_size |
כיצד ליישם FULLTEXT ב-5 שלבים
- ביקורת: מדידת זמן החיפוש הנוכחי, ניתוח נפח הנתונים.
- הגדרת MySQL: קביעת
ngram_token_size=2, השבתת מילות עצירה. - יצירת אינדקסים: הרצת ALTER TABLE עם FULLTEXT ומנתח ngram.
- פיתוח קומפוננטה: יצירת
FulltextSearcherמותאם אישית ותבנית. - בדיקות: בדיקות עומס, השוואת "לפני/אחרי".
מה כלול בעבודה
- ביקורת של אינדקס החיפוש הנוכחי, מדידת זמן השאילתה.
- הגדרת MySQL:
ngram_token_size, השבתת מילות עצירה. - יצירת אינדקסי FULLTEXT על
b_search_contentו/או טבלאות אינפובלוקים. - פיתוח קומפוננטת חיפוש מותאמת אישית עם שאילתות FULLTEXT.
- בנייה מחדש של אינדקס החיפוש של Bitrix (
BXSearch::reindex()). - בדיקות עומס עם דוח "לפני/אחרי".
- אספקת תיעוד וסקריפטים למיגרציה.
- תמיכה חינם למשך חודש אחד.
הניסיון שלנו בפיתוח Bitrix הוא למעלה מ-10 שנים, הצלחנו להאיץ חיפוש ביותר מ-50 פרויקטים. קבלו ייעוץ — כתבו לנו, נעריך את הפרויקט שלכם בתוך יום אחד.
על פי התיעוד של MySQL 8.0, מנתח ה-ngram מבטיח אינדוקס נכון של קירילית ללא תלויות חיצוניות (MySQL Ngram Full-Text Parser).

