besnovatyj / yii2-cms-search-manticore
Ядро сквозного поиска на Manticore Search для Yii2 CMS: настоящая лемматизация русского, поиск с опечатками и с ошибочной раскладкой клавиатуры, подсказки «возможно, вы имели в виду». Подключается к фасаду besnovatyj/yii2-cms-search, требует запущенный демон searchd.
Package info
github.com/besnovatyj/yii2-cms-search-manticore
Type:yii2-extension
pkg:composer/besnovatyj/yii2-cms-search-manticore
Requires
- php: >=8.4
- ext-mbstring: *
- ext-pdo: *
- ext-pdo_mysql: *
- besnovatyj/yii2-cms-contracts: ^1.0
- besnovatyj/yii2-cms-helpers: ^1.0
- besnovatyj/yii2-cms-kernel: ^1.0
- besnovatyj/yii2-cms-search: ^1.0
- yiisoft/yii2: ~2.0.0
Requires (Dev)
- roave/security-advisories: dev-latest
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
Ядро для фасада besnovatyj/yii2-cms-search: на сервере работает
демон searchd, а сайт получает настоящую морфологию русского. Ради чего именно — ниже.
Оформлено модулем, а не пакетом: ядро включают и выключают менеджером модулей, а морфологию, подсказки и допуск опечаток настраивают в админке (раздел настроек «Search»). Выключенный модуль означает «этого ядра в системе нет» — фасад останется на своём.
Что умеет
| Возможность | Есть | Как сделано |
|---|---|---|
| Ранжирование | да | BM25 с учётом близости слов, плюс веса полей (field_weights) |
| Лемматизация русского | да | lemmatize_ru_all — словарь, а не отсечение окончаний: «людям» находит «человек» |
| Поиск с опечатками | да | OPTION fuzzy=1 силами демона, расстояние Левенштейна по символам |
| Ошибочная раскладка | да | layouts='ru,us': «ghbdtn» находит «привет» |
| Подсветка совпадений | да | HIGHLIGHT() из хранимых полей, той же морфологией, что и поиск |
| Подсказка «возможно, вы имели в виду» | да | CALL QSUGGEST по словарю индекса, когда не нашлось ничего |
| Вкладки по разделам | да | группировка по разделу отдельным запросом (см. ниже) |
| Веса разделов | да | множитель документа участвует в сортировке: WEIGHT() * boost |
| Сортировка по дате | да | дата публикации хранится атрибутом |
| Обновление по одной записи | да | REPLACE INTO в таблицу реального времени |
Почему лемматизация — это не «то же самое, что стемминг»
Стеммер работает правилами и умеет только отрезать: «ботинки» → «ботинк». Этого хватает на падежи и числа, и именно так работает ядро на TNTSearch. Но правилами нельзя получить «человек» из «людям», «идти» из «шла», «год» из «лет» — там меняется основа. Лемматизатор берёт форму из словаря, поэтому находит то, чего стеммер не найдёт никогда.
Вариант _all индексирует все возможные разборы омонима: «стекло» найдётся и как существительное,
и как форма глагола «стечь». Для сайта в тысячу документов полнота важнее экономии места.
Подключение
Демон говорит по протоколу MySQL, поэтому пакет не тянет ни HTTP-клиента, ни нового расширения
PHP: подходит тот же pdo_mysql, которым приложение работает с базой. Отдельного компонента
приложения ядро не заводит.
Реквизиты приходят вместе с окружением — через SecretReader, то есть из файла
/run/secrets/<имя>, а если файла нет, из одноимённой переменной окружения. Ровно так приложение
получает реквизиты базы, и ровно поэтому переезд на другой сервер не требует захода в админку:
| Имя | По умолчанию | Смысл |
|---|---|---|
MANTICORE_HOST |
127.0.0.1 |
адрес демона; в docker-сборке — имя сервиса |
MANTICORE_PORT |
9306 |
порт SQL-интерфейса |
MANTICORE_USER |
пусто | учётная запись; пусто — подключение анонимное, годится только при выключенной авторизации демона |
MANTICORE_PASSWORD |
пусто | пароль учётной записи |
Пароль не попадает ни в настройки, ни в базу, ни в дамп, ни в резервную копию настроек.
Параметры соединения, которые ядро задаёт само и которые не являются делом вкуса: charset не
указывается (иначе Yii отправит демону ненужный SET NAMES), подготовка выражений эмулируется
драйвером (демон поддерживает её не полностью), кэш схемы выключен, соединение имеет таймаут —
упавший демон не должен превращать страницу поиска в зависший запрос.
Настройки индекса (админка, раздел «Search»)
| Настройка | По умолчанию | Смысл |
|---|---|---|
| Имя таблицы индекса | пусто | пусто — составляется из имени базы проекта |
| Морфология | auto |
по языку сайта; после смены нужна переиндексация |
| Подсказки | включены | требуют словаря подстрок: индекс вырастает в разы. После смены нужна переиндексация |
| Допуск опечаток | 2 |
сколько букв в слове может не совпасть; 0 — искать без опечаток |
| Раскладки клавиатуры | auto |
по языку сайта; пусто — не распознавать чужую раскладку |
| Окно совпадений | 5000 |
глубина листания и точность счётчиков |
Таблица индекса
Одна таблица реального времени, имя по умолчанию — bescms_search_<имя базы проекта>. Суффикс
появляется сам: один демон нередко обслуживает несколько сайтов на сервере, и индексы не должны
мешать друг другу.
| Столбец | Что это |
|---|---|
title, content, keywords |
полнотекстовые поля: по ним ищут, из них берётся подсвеченный фрагмент |
doc_type |
раздел (blog.post) — фильтр и вкладки |
published |
дата публикации — сортировка по свежести |
boost |
важность документа — множитель к релевантности |
stamp |
метка прогона переиндексации |
Столбец назван doc_type, а не type, потому что type — служебное слово в синтаксисе создания
таблиц Manticore.
Переиндексация без «слепого» окна
Индекс не пересобирается рядом с рабочим и не подменяется в конце, как это приходится делать ядру
на TNTSearch. Таблица реального времени заменяет документы на месте по стабильным идентификаторам
каталога, а в конце прогона удаляются строки, которых прогон не подтвердил (stamp меньше
текущего). Поэтому:
- поиск не остаётся без индекса ни на мгновение;
- прерванная пересборка ничего не ломает: часть документов останется с прошлого прогона до следующего запуска;
- удалённые и снятые с публикации материалы исчезают из выдачи в конце прогона.
Морфология «запекается» в таблицу, поэтому смена языка сайта требует пересоздания таблицы — ядро делает это само в начале полной пересборки, сверив настройки таблицы с текущими.
Особенности, о которых стоит знать
- Поиск с опечатками выполняет Manticore Buddy — спутник демона, входящий в штатную поставку. Если его нет (урезанная сборка), демон отвергает запрос: ядро один раз ловит отказ, пишет предупреждение в лог и повторяет запрос без режима опечаток. Поиск продолжает работать.
- Вкладки считаются отдельным запросом, а не конструкцией
FACET:FACET— это мультизапрос, а режим опечаток с мультизапросами несовместим. Отдельный запрос с группировкой даёт те же цифры и работает при любом режиме. Заодно вкладки считаются без учёта выбранного раздела — «Новости» показывают своё число и тогда, когда открыты «Услуги». - Счётчики ограничены окном
max_matches(по умолчанию 5000 совпадений на запрос, настраивается). До этого числа общее количество найденного точно, дальше — «не меньше». - Подсветка безопасна. Демон расставляет в тексте метки, ничего не значащие в HTML; фрагмент
экранируется целиком, и только потом метки превращаются в
<mark>. Содержимое документа не может внести в страницу разметку. - Кириллица работает без настройки: словарь символов по умолчанию (
non_cjk) включает русский алфавит.
Авторизация
У Manticore есть встроенная авторизация (пользователи, пароли, права) начиная с версии 27.1.5, по
умолчанию выключенная. Ядро её поддерживает: достаточно положить имя пользователя и пароль в
секреты окружения. Приложению хватает прав read, write и schema — администрирование и
репликация ему не нужны.
Первого администратора демон заводит отдельным режимом запуска searchd --auth-non-interactive,
и этот режим обращается к УЖЕ ЗАПУЩЕННОМУ демону: сначала старт, потом создание учёток.
Включённая авторизация не отменяет закрытого порта: индекс содержит тексты всех материалов сайта, включая снятые с публикации, поэтому наружу демон выставлять не следует в любом случае.
Как ядро подключается к фасаду
Module реализует Besnovatyj\Search\contracts\SearchEngineProvider и объявляет один
дескриптор: ключ manticore, подпись для настроек и класс движка. Больше фасаду ничего не нужно —
он находит ядро обходом модулей и создаёт движок контейнером, а выключенное в менеджере модулей
ядро просто исчезает из списка.
Вся DI-проводка пакета живёт в src/config/common.php (секция container.singletons).
Bootstrap-класса у пакета нет намеренно: container.singletons применяется раньше любой возможной
точки обращения и при этом лениво, поэтому отдельный bootstrap был бы вторым способом сделать то
же самое. Соединение объявлено синглтоном не ради экономии: SHOW META (число найденного)
читается тем же линком, которым выполнен сам запрос.
Требования
- установленный и включённый модуль фасада
Search; - Manticore Search 6.3 или новее — режим опечаток появился в 6.3; лемматизаторы входят в пакет начиная с версии 25.0, встроенная авторизация — с 27.1.5;
- доступ к порту 9306 (протокол MySQL) с сервера приложения;
ext-pdo_mysql,ext-mbstring.
Демон обязан переживать перезагрузку сервера — это единственное, что добавляет ядро в эксплуатацию. Ни отдельной базы, ни репликации, ни JVM ему не нужно; типичный расход памяти на индекс сайта — десятки мегабайт.