Анализ русского текста: морфология, стемминг, Hunspell
В предыдущей статье мы разобрали анализатор english — быстрый и надёжный, потому что английская морфология относительно бедна. С русским языком ситуация принципиально иная: тут нужно разбираться в словоформах, и простое правиловое обрезание суффиксов работает куда хуже.
Почему русская морфология — особая задача
Английское слово run имеет четыре формы: run, runs, ran, running. У русского глагола «купить» уже только личных форм — дюжина, а если прибавить причастия, деепричастие и видовую пару «покупать» — счёт идёт на десятки:
| Форма | Примеры |
|---|---|
| Прош. время (м/ж/с/мн) | купил, купила, купило, купили |
| Будущее время | купит, купишь, купят, купите… |
| Причастие | купивший, купившая, купившие |
| Деепричастие | купив |
| Видовая пара | покупать, покупал, покупаю… |
Для поискового движка все эти формы должны находить друг друга. Алгоритм, который хорошо работает для английского (running → run), сталкивается с куда более запутанной системой чередований и приставок в русском.
Встроенный анализатор russian
Elasticsearch поставляет готовый анализатор russian. Его полный состав:
| Шаг | Компонент | Что делает |
|---|---|---|
| Токенизатор | standard | Делит по пробелам и пунктуации |
| Фильтр 1 | lowercase | Всё в нижний регистр |
| Фильтр 2 | russian_stop | Удаляет русские стоп-слова («и», «в», «на», «с»…) |
| Фильтр 3 | russian_keywords | Защищает отдельные слова от стемминга (по умолчанию пуст) |
| Фильтр 4 | russian_stemmer | Snowball-стемминг для русского |
russian_keywords — это keyword_marker фильтр: он позволяет задать список слов, которые стеммер обходит стороной. По умолчанию список пуст, фильтр ничего не делает. Принцип тот же, что описан в Анатомия анализатора: char filters, токенизатор, token filters.
Проверим через _analyze — точно так же, как в статье про английский:
POST /_analyze
{
"analyzer": "russian",
"text": "Пользователи покупали товары в нашем магазине"
}Примерный ответ:
{
"tokens": [
{ "token": "пользовател", "position": 0 },
{ "token": "покупа", "position": 1 },
{ "token": "товар", "position": 2 },
{ "token": "магазин", "position": 5 }
]
}Стоп-слова «в» и «нашем» выброшены (позиции 3 и 4 пропущены). Оставшиеся слова прошли через Snowball: «пользователи» → «пользовател», «покупали» → «покупа». Обратите внимание: «покупа» — не настоящее слово. Алгоритм просто обрезал суффикс по правилу, не зная ничего о смысле.
Главный изъян: запрос по слову «купить» даст другой токен, чем «покупа», — и документ найден не будет. Snowball не видит связь между «купить» и «покупать», потому что приставки меняют форму основы.
Несмотря на это, встроенный russian — нулевая сложность настройки. Для небольшого блога или новостного сайта его часто достаточно. Подключается в маппинге одной строчкой:
PUT /articles
{
"mappings": {
"properties": {
"content": {
"type": "text",
"analyzer": "russian"
}
}
}
}Hunspell: словарная морфология
Hunspell — формат словарей, который используется в LibreOffice, Firefox, macOS для проверки орфографии. В Elasticsearch встроен token filter hunspell, работающий с этими словарями.
В отличие от Snowball, Hunspell смотрит каждое слово в словаре и возвращает его базовую форму (лемму). «Купили», «купишь», «купившая» — при наличии нужных записей в словаре все они приводятся к «купить». Это принципиально другой уровень точности.
Шаг 1. Добавить файлы словаря
Файлы .aff (правила аффиксов) и .dic (список слов) нужно разместить в директории config/hunspell/ru_RU/ на каждой ноде кластера — автоматической синхронизации между нодами нет:
<путь к ES>/config/hunspell/ru_RU/ru_RU.aff
<путь к ES>/config/hunspell/ru_RU/ru_RU.dicФайлы можно взять из пакета LibreOffice или из репозиториев словарей для OpenOffice.
Шаг 2. Создать кастомный анализатор
PUT /products
{
"settings": {
"analysis": {
"filter": {
"ru_stop": {
"type": "stop",
"stopwords": "_russian_"
},
"ru_hunspell": {
"type": "hunspell",
"locale": "ru_RU",
"dedup": true
}
},
"analyzer": {
"russian_hunspell_analyzer": {
"tokenizer": "standard",
"filter": ["lowercase", "ru_stop", "ru_hunspell"]
}
}
}
},
"mappings": {
"properties": {
"description": {
"type": "text",
"analyzer": "russian_hunspell_analyzer"
}
}
}
}"stopwords": "_russian_" — встроенный идентификатор русского списка стоп-слов, тот же набор, что в анализаторе russian. Параметр "dedup": true убирает дубли, если словарь для одного слова возвращает несколько лемм.
Проверяем:
POST /products/_analyze
{
"analyzer": "russian_hunspell_analyzer",
"text": "Пользователи покупали товары в нашем магазине"
}При правильно настроенном словаре результат значительно точнее: «пользователи» → «пользователь», «покупали» → «покупать», «магазине» → «магазин». Теперь запрос «купить» и документ со словом «покупали» имеют шанс встретиться — если словарь содержит нужные формы.
Ограничения Hunspell:
- Файлы словаря нужно распределять по нодам вручную — при Docker/облачном деплое это дополнительная оркестрация.
- Скорость индексации ниже, чем у Snowball: словарный поиск дороже правилового.
- Качество зависит от полноты конкретного словаря.
Сторонние плагины: настоящая лемматизация
Для профессионального поиска на русском существуют плагины на базе словарей проекта AOT (Автоматическая обработка текста). Самый известный для ES 8.x — community-плагин nickyat/elasticsearch-analysis-morphology на GitHub.
Устанавливается стандартной командой:
bin/elasticsearch-plugin install \
https://github.com/nickyat/elasticsearch-analysis-morphology/releases/download/<version>/analysis-morphology-<version>.zipПосле перезапуска нод доступен тип фильтра russian_morphology:
PUT /products
{
"settings": {
"analysis": {
"filter": {
"ru_morph": {
"type": "russian_morphology"
},
"ru_stop": {
"type": "stop",
"stopwords": "_russian_"
}
},
"analyzer": {
"ru_morphology_analyzer": {
"tokenizer": "standard",
"filter": ["lowercase", "ru_stop", "ru_morph"]
}
}
}
}
}Плагин обеспечивает лучшее качество лемматизации из доступных вариантов. Однако это неофициальный инструмент — Elastic его не поддерживает. При обновлении ES нужно ждать выхода совместимой версии плагина. В Elastic Cloud и управляемых кластерах установка сторонних плагинов может быть ограничена или недоступна.
Сравнение трёх подходов
flowchart TD
A["Входное слово: «покупали»"] --> B{Какой анализатор?}
B -->|"russian — Snowball"| C["Токен: покупа"]
B -->|"Hunspell ru_RU"| D["Токен: покупать"]
B -->|"Morphology-плагин"| E["Токен: покупать"]
C --> F["Запрос 'купить' → другой токен\nДокумент НЕ найден ❌"]
D --> G["Запрос 'купить' → та же лемма\nДокумент найден ✓"]
E --> H["Запрос 'купить' → та же лемма\nДокумент найден ✓"]russian (Snowball) | Hunspell ru_RU | Morphology-плагин | |
|---|---|---|---|
| Точность лемматизации | Низкая | Хорошая | Высокая |
| Сложность настройки | Нулевая | Словари на каждой ноде | Установка плагина |
| Скорость индексации | Быстро | Медленнее | Медленнее |
| Официальная поддержка Elastic | ✓ | ✓ | ✗ (community) |
| Работает в Elastic Cloud | ✓ | Зависит от варианта | Часто нет |
Snowball для русского vs английского: в чём разница
Для английского Snowball работает хорошо, потому что морфология бедна: суффиксальных правил хватает для большинства случаев. В русском Snowball срезает окончания по общим правилам, но не видит:
- Чередования в корне:
купить/покупать— разные основы с точки зрения алгоритма; - Приставочные глаголы:
написать,переписать,записать— Snowball не связывает их списать; - Нестандартные формы: супплетивные («плохой» / «хуже»), усечённые основы.
Это же справедливо для других флективных языков с богатой морфологией: немецкого, польского, чешского. Чем сложнее морфология — тем больше выигрыш от словарных подходов по сравнению с Snowball.
Что выбрать на практике
- Быстрый старт / прототип: встроенный
russian. Нет зависимостей, работает из коробки. - Продуктовый поиск по товарам и контенту: Hunspell с
ru_RU— приемлемое качество без внешних зависимостей. - Высокое качество / корпоративный поиск: morphology-плагин, но закладывайте время на поддержку при обновлениях ES.
- Гибридный сценарий: если добавить векторный поиск, требования к точности морфологии снижаются — подробнее в Гибридный поиск: retrievers и RRF.
- Синонимы поверх морфологии: независимо от выбранного анализатора, для бизнес-терминов добавьте фильтр синонимов — об этом в Синонимы и кастомные анализаторы.