Просмотров: 34033
Дата последнего изменения: 13.11.2023
Сложность урока:
4 уровень - сложно, требуется сосредоточиться, внимание деталям и точному следованию инструкции.
5
Недоступно в лицензиях:
Ограничений нет
Модуль Поиск включает в себя сквозной индекс по разным сущностям, отдельные фильтры по атрибутам сущностей, поддержку стемминга, точный поиск по атрибутам и связанным сущностям.
Но появилась потребность изменить поиск в связи с новым пользовательским сценарием работы. Это новый фильтр, в котором помимо атрибутов (где мы ищем точно), появился произвольный поиск по строке (где пользователь может набирать произвольные комбинации символов). Таким образом, новый поиск:
- должен быть интерактивным в конкретной сущности;
- комбинировать поиск по тексту и фильтр по атрибутам;
- быть быстрым (так называемым поиском «на кончиках пальцев»);
- результатом поиска должно являться текущее представление сущности.
Следовательно, возкникла задача: как эффективно ускорить, изменить и скрестить поиск с фильтрами? Для ее решения было предложено 3 варианта:
- Интегрировать текущий модуль поиска с фильтрами по сущностям - реализовать оказалось непросто и поиск не будет быстрым.
- Перейти на внешний поисковый индекс (Sphinx, Lucene) - результаты тестирования показали хорошую индексацию по атрибутам, тексту, но сложно скрестить результаты поиска с их представлениями в списке. Дополнительная сложность - это десятки и сотни тысяч индексов для облачных версий Битрикс24.
- Полнотекстовый индекс Mysql - оказался подходящим вариантом, о нем поговорим ниже.
Что такое полнотекстовый индекс Mysql?
- Индекс строится по одному или нескольким текстовым полям.
- Mysql разбивает содержимое полей на «слова» и заносит их в отдельные таблицы для построения «обратного» индекса.
- При построении запроса таблицы с индексами прозрачно «джоинятся» к основному запросу.
- Поиск возможен на естественном языке или в режиме
boolean
.
Как использовать индекс?
Для этого существует оператор MATCH (col1,col2,...) AGAINST (expr [search_modifier])
. У него есть модификаторы, которые позволяют определить нам на каком языке искать:
- IN NATURAL LANGUAGE MODE - на естественном языке;
- IN BOOLEAN MODE - в режиме
boolean
;
- WITH QUERY EXPANSION - в этом режиме Mysql делает 2 запроса: сначала ищет по первому запросу, находит записи, затем во второй запрос подставляет строчки из первого. Таким образом, сильно расширяется область поиска.
Запрос обычно выглядит следующим образом:
SELECT * FROM articles WHERE MATCH (title,body) AGAINST ('database' IN NATURAL LANGUAGE MODE);
Отметим особенности поиска Mysql:
- При запросе на натуральном языке Mysql сам сортирует результат по релевантности.
- Двойной поиск с параметром WITH QUERY EXPANSION.
- Поиск по умолчанию регистронезависимый.
- В качестве
expr
может использоваться только литерная строка.
При поиск в режиме boolean
можно использовать дополнительные параметры:
- + Должно быть
- - Не должно быть
- (no operator) Или
- @distance Расстояние
- >~< Вес
- ( ) Группировка
- * Маска (проставляется только справа)
Типичный запрос выглядит так:
SELECT * FROM articles WHERE MATCH (title,body) AGAINST ('+MySQL -YourSQL' IN BOOLEAN MODE);
Быстродействие поиска Mysql:
- Полнотекстовый индекс существенно оптимизирован: кеширование на модификацию, partitioning (разбиение на несколько таблиц индекса).
- Быстрее LIKE на один-несколько порядков.
- Быстрее нашего модуля поиска в 2-10 раз за счет оптимизации.
- Возможен реально интерактивный поиск.
Требования к поиску:
- Только Innodb (c 5.6) и MyISAM.
- Не поддерживаются таблицы с «партициями».
- Не поддерживаются иероглифы и некоторые кодировки (ucs2).
Использование в продукте
- Создание индекса:
В инсталляторе модуля новый файл install_ft.sql с содержимым:
CREATE fulltext index IXF_B_USER_INDEX_1 on b_user_index (SEARCH_USER_CONTENT);
В install.php код:
$errors = $DB->RunSQLBatch($_SERVER["DOCUMENT_ROOT"]."/bitrix/modules/main/install/mysql/install_ft.sql");
if ($errors === false)
{
$entity = \Bitrix\Main\UserTable::getEntity();
$entity->enableFullTextIndex("SEARCH_USER_CONTENT");
}
- Индексирование:
- Можно создать либо текстовую колонку в таблице, либо связанную отдельную таблицу (предпочтительнее для больших существующих таблиц).
- В индексированную колонку можно записывать объединенные данные с разных колонок (искать «везде»).
- На одном хите практически невозможно ни заполнить эту колонку, ни построить индекс по имеющимся данным.
- Рекомендуем пошаговый индексатор на агентах.
- Индексатор:
- Базовый класс Bitrix\Main\Update\Stepper запускает агенты и предоставляет графический интерфейс.
- Необходимо реализовать метод execute(), который выполняет реальную работу в пошаговом режиме.
- Метод getHtml() предоставляет интерфейс с аяксовым «хитователем» - желтый прогресс-индикатор.
- Метод bind() добавляет агента (в обновлении).
- Использование:
- Добавлены новые операторы в построитель запросов:
"*" => "FT", // partial full text match
"*=" => "FTI", // identical full text match
"*%" => "FTL", // partial full text match based on LIKE
- Необходимо учитывать, что индекса может не быть:
$operation = (LogIndexTable::getEntity()->fullTextIndexEnabled("CONTENT")? '*' : '*%‘);
- В новый фильтр ORM добавлена поддержка match: методы whereMatch(), whereNotMatch(), а также хелпер matchAgainstWildcard().
Пример обращения к сущности:
$res = \Bitrix\Main\UserIndexTable::getList(array(
"select" => array("SEARCH_ADMIN_CONTENT"),
"filter" => array(
"*SEARCH_ADMIN_CONTENT" => \Bitrix\Main\Search\Content::prepareStringToken("vad dumbrav"),
)
));
var_dump(\Bitrix\Main\Entity\Query::getLastQuery())
Запрос:
string(216) "SELECT
`main_user_index`.`SEARCH_ADMIN_CONTENT` AS `SEARCH_ADMIN_CONTENT`
FROM `b_user_index` `main_user_index`
WHERE MATCH (`main_user_index`.`SEARCH_ADMIN_CONTENT`) AGAINST ('(+inq* +qhzoeni*)' IN BOOLEAN MODE)";
Более медленный запрос с LIKE:
string(277) "SELECT
`main_user_index`.`SEARCH_ADMIN_CONTENT` AS `SEARCH_ADMIN_CONTENT`
FROM `b_user_index` `main_user_index`
WHERE ((UPPER(`main_user_index`.`SEARCH_ADMIN_CONTENT`) like '%INQ%' ESCAPE '!' AND UPPER(`main_user_index`.`SEARCH_ADMIN_CONTENT`) like '%QHZOENI%' ESCAPE '!'))"
Особенности по использованию полнотекстового поиска:
- Минимальная длина слова в индексе (по умолчанию 3). Параметр соединения ft_min_token_size.
- Словарь стоп-слов. Можно или отключить, или преобразовывать данные методом Bitrix\Main\Search\Content::prepareStringToken().
- Поиск по части слова справа (звездочка слева). Можно декомпозировать не длинные слова: 123456789, 23456789, 3456789…