
Привет, Хабр!
Когда ваш поиск начинает обслуживать пользователей, говорящих на разных языках, кажется, что начинается некий лингвистический хаос. Однако с Elasticsearch это не так уж сложно — если знаешь, как правильно настроить токенизацию, стемминг и аннотирование документов под каждый язык. Сегодня мы рассмотрим тему многоязычного поиска: разберём, как Elasticsearch понимает текст на разных языках, как обрабатывает сложные случаи вроде китайских иероглифов или немецких сложных слов, и как сделать так, чтобы результаты поиска были релевантны для каждого пользователя, независимо от его языка.
Токенизация текста
Токенизация — это процесс разбиения текста на минимальные значимые единицы, или токены. Обычно в английском тексте такими токенами будут слова, отделённые пробелами. В идеальном мире на этом всё бы закончилось, но мы живём в мире с китайскими иероглифами, сложносоставными немецкими словами и агглютинативными языками вроде финского. Короче, просто разрезать текст на пробелах — это не всегда решение.
Проблемы токенизации в разных языках
Английский: Кажется, всё просто, но тут всплывают вопросы: что делать с пунктуацией? Как обработать сокращения? И что делать со словами вроде «isn't»?
Китайский: Здесь токенизация становится игрой в угадайку, потому что иероглифы идут подряд без пробелов. Как разделить слова, если пробелов нет?
Немецкий: Встречал слова вроде Donaudampfschifffahrtsgesellschaftskapitän? Это слово из 40+ букв — и вот его надо как‑то правильно разрезать на части.
Русский: Проблемы падежей, склонений и приставок. Слово «машина» может быть «машин», «машинами», «машине» — и каждая форма должна попадать в результат поиска.
Теперь разберёмся, как это решить с помощью анализаторов и фильтров в Elasticsearch.
Elasticsearch использует анализаторы для токенизации и обработки текста. Эти анализаторы — не волшебные существа, которые знают все языки мира, а набор фильтров, которые применяются по цепочке к тексту. Каждый язык имеет свои нюансы, и правильный выбор анализатора — ключ к успеху.
Токенизация для английского текста
Начнём с простого. Для английского языка можно использовать базовый Standard Analyzer, который разделяет текст на слова, игнорируя пунктуацию и приводя всё к нижнему регистру. Но иногда этого недостаточно.
Пример настройки анализатора:
{ "settings": { "analysis": { "analyzer": { "english_custom": { "type": "standard", "stopwords": "_english_", "filter": ["lowercase", "porter_stem"] } } } } }
Здесь мы добавили porter_stem фильтр, который сокращает слова до их основы (например, «running» станет «run»).
Токенизация для китайского текста
В китайском нет пробелов, а каждый иероглиф может быть самостоятельным словом или частью большого составного слова. Для китайского текста Elasticsearch предлагает использовать Smart Chinese Analyzer.
Пример:
{ "settings": { "analysis": { "analyzer": { "chinese": { "type": "smartcn" } } } } }
Этот анализатор использует встроенные словари для токенизации текста. Smart Chinese Analyzer автоматически разделит иероглифы на слова.
Токенизация для немецкого текста
Теперь немецкий. Токенизация в немецком может стать настоящей проблемой из-за составных слов. Классический пример: Donaudampfschifffahrtsgesellschaftskapitän. Для работы с такими словами можно использовать German Analyzer, который включает фильтры для стемминга и обработки сложных слов.
Пример настройки:
{ "settings": { "analysis": { "analyzer": { "german_custom": { "type": "standard", "stopwords": "_german_", "filter": ["lowercase", "german_normalization", "german_stem"] } } } } }
Тут у нас german_normalization, который обрабатывает такие особенности языка, как умлауты (ä, ö, ü), и german_stem, который сокращает слова до их корня, сохраняя смысл.
Токенизация для русского текста
Для русского языка проблемы, как я уже упомянул, возникают с падежами и склонениями. Здесь нам на помощь приходит Russian Analyzer, который умеет обрабатывать морфологию русского языка.
Пример:
{ "settings": { "analysis": { "analyzer": { "russian_custom": { "type": "standard", "stopwords": "_russian_", "filter": ["lowercase", "russian_morphology", "russian_stop"] } } } } }
Здесь фильтр russian_morphology работает с падежами и словоформами, а russian_stop фильтрует ненужныеслова вроде «и», «в», «на».
Как не облажаться с токенизацией
Даже если ты считаешь, что выбрал правильный анализатор, всегда тестируй его на реальном тексте. Некоторые анализаторы могут работать отлично на одних данных и ужасно на других.
Иногда тебе придётся создавать свои собственные цепочки анализаторов, комбинируя несколько фильтров. Например, если стандартный анализатор для русского языка не справляетс��, можно добавить кастомный фильтр для обработки каких-то специфических слов.
Не забывайте про языковые особенности. Например, в немецком умлауты могут быть заменены на гласные без точек, а в русском необходимо учитывать, что буква «ё» и «е» могут быть взаимозаменяемы в поиске.
Stemming и Lemmatization
А сейчас поговорим о том, как не потерять смысл в многоязычном поиске, когда есть такие слова, как «идти», «идёт», «шел», и все они должны быть поняты Elasticsearch как одно и то же понятие. Именно здесь помогают stemming и lemmatization — два мощных инструмента, которые помогают поисковому движку делать текстовое сравнение умнее.
Разберемся в терминах:
Stemming — это процесс, который отрезает окончания слов, оставляя их основу (stem). Например, «playing», «played» и «plays» будут сведены к «play». Метод грубый, но быстрый.
Lemmatization — более интеллектуальный процесс. Лемматизация приводит слово к его базовой форме (lemma) с учётом грамматического контекста. Например, слова «better» и «good» сведутся к одной лемме — «good». Более сложная задача, но результат точнее.
В разных языках лемматизация и стемминг работают по-разному. Что эффективно для английского языка, не всегда сработает для русского или немецкого. Основная проблема — грамматические различия между языками. Например:
В русском языке слово «идти» может изменяться по времени, числу и падежам.
В немецком сложные слова объединяются в одну строку.
В китайском нет морфологии, но важны контекстные лексические значения.
Stemming и lemmatization в Elasticsearch
Elasticsearch имеет встроенные анализаторы и фильтры для стемминга и лемматизации на разных языках. =
Stemming для английского языка
Для английского стемминг можно настроить с использованием Porter Stemming Algorithm — он преобразует слова к их основным формам.
Пример настройки:
{ "settings": { "analysis": { "filter": { "english_stemmer": { "type": "stemmer", "language": "english" } }, "analyzer": { "english_analyzer": { "tokenizer": "standard", "filter": [ "lowercase", "english_stemmer" ] } } } } }
Этот пример настроит фильтр стемминга для английского языка. Когда ты отправляешь текст, вроде «running», «ran», или «runs», анализатор приводит все эти слова к «run».
Stemming для немецкого языка
Для немецкого в есть German Normalization Filter и German Light Stemmer.
Пример настройки:
{ "settings": { "analysis": { "filter": { "german_normalization": { "type": "german_normalization" }, "german_stemmer": { "type": "stemmer", "language": "light_german" } }, "analyzer": { "german_analyzer": { "tokenizer": "standard", "filter": [ "lowercase", "german_normalization", "german_stemmer" ] } } } } }
Фильтр german_normalization обрабатывает буквы с умлаутами и заменяет ß на «ss», что улучшает корректность токенизации.
Лемматизация для русского языка
С русским языком сложнее: из-за развитой морфологии стемминг не всегда даёт точные результаты. Поэтому для русского языка часто лучше использовать лемматизацию. В Elasticsearch для русского языка можно воспользоваться специальным фильтром морфологии.
Пример настройки:
{ "settings": { "analysis": { "filter": { "russian_morphology": { "type": "russian_morphology" } }, "analyzer": { "russian_analyzer": { "tokenizer": "standard", "filter": [ "lowercase", "russian_morphology" ] } } } } }
Здесь мы используем russian_morphology — этот фильтр обрабатывает различные формы слов, приводя их к одной основе Например, «идти», «идёт» и «шел» будут рассматриваться как одно и то же слово.
Лемматизация для испанского языка
Испанский язык имеет свои особенности. Для него также применим лемматизатор, который приводит к нормальной форме глаголы и существительные.
Пример настройки:
{ "settings": { "analysis": { "filter": { "russian_stop": { "type": "stop", "stopwords": "_russian_" } }, "analyzer": { "russian_analyzer_with_stop": { "tokenizer": "standard", "filter": [ "lowercase", "russian_morphology", "russian_stop" ] } } } } }
Здесь мы используем лёгкий стеммер для испанского, который обрабатывает флективные формы, такие как «hablando» (говорящий) и «hablar» (говорить).
Настройка стоп-слов для многоязычного поиска
Стоп-слова — это слова, которые не несут смысловой нагрузки для поиска (например, предлоги и союзы). В разных языках свои стоп-слова. Elasticsearch имеет встроенные списки стоп-слов для популярных языков, которые можно настроить по своему усмотрению.
Пример настройки анализатора со стоп-словами:
{ "settings": { "analysis": { "filter": { "russian_stop": { "type": "stop", "stopwords": "_russian_" } }, "analyzer": { "russian_analyzer_with_stop": { "tokenizer": "standard", "filter": [ "lowercase", "russian_morphology", "russian_stop" ] } } } } }
Здесь фильтр russian_stop автоматически удаляет из текста русские предлоги и другие часто встречающиеся слова, которые не нужны для индексации.
Итак, если хочешь сделать многоязычный поиск умнее — настройка стемминга и лемматизации станет первым шагом на этом пути.
Аннотирование и метаданные
Сейчас поговорим про то, что многие недооценивают при работе с Elasticsearch, особенно в многоязычных проектах — это аннотирование документов и управление метаданными. Правильная аннотация и работа с метаданными не просто помогают в поиске, они обеспечивают релевантность и ускоряют доступ к информации, особенно в контексте многоязычных проектов.
Если твои пользователи из разных регионов и говорят на разных языках, ты обязан учитывать это в индексации и настройке поиска. Допустим, твой поиск должен обрабатывать документы на русском, английском и китайском. Для каждого языка у тебя должны быть свои настройки токенизации, фильтров, и самое главное — идентификация языка. Без грамотной работы с метаданными ты либо потеряешь релевантность, либо будешь перегружать поиск ненужной информацией.
Аннотирование — это процесс добавления метаданных в документы, которые помогают Elasticsearch правильно обрабатывать и индексировать их. Это как прицепить ярлык к каждому документу, чтобы Elasticsearch знал, как с ним об��ащаться. Метаданные могут включать информацию о языке, типе контента, дате создания и многом другом.
Поле _lang: как определить язык документа
Первое, с чем сталкиваются при создании многоязычного поиска, — это необходимость определять язык документа. Для этого можно использовать специальное поле _lang, которое помогает Elasticsearch выбрать правильные анализаторы и фильтры для токенизации и стемминга.
Пример структуры данных с полем _lang:
{ "title": "Добро пожаловать", "body": "Это пример русского текста.", "_lang": "ru" }
Здесь явно указываем, что язык документа — русский. Это важно, потому что для каждого языка Elasticsearch использует свои токенизаторы, стеммеры и фильтры.
Рассмотрим, как можно настроить индекс, который будет учитывать языковую аннотацию. Допустим, есть данные на нескольких языках, и хочется, чтобы Elasticsearch применял разные анализаторы в зависимости от значения поля _lang.
Пример конфигурации индекса:
{ "settings": { "analysis": { "analyzer": { "default": { "type": "custom", "tokenizer": "standard", "filter": ["lowercase"] }, "english_analyzer": { "type": "custom", "tokenizer": "standard", "filter": ["lowercase", "english_stemmer"] }, "russian_analyzer": { "type": "custom", "tokenizer": "standard", "filter": ["lowercase", "russian_morphology"] } } } }, "mappings": { "properties": { "title": { "type": "text", "fields": { "english": { "type": "text", "analyzer": "english_analyzer" }, "russian": { "type": "text", "analyzer": "russian_analyzer" } } }, "body": { "type": "text", "fields": { "english": { "type": "text", "analyzer": "english_analyzer" }, "russian": { "type": "text", "analyzer": "russian_analyzer" } } }, "_lang": { "type": "keyword" } } } }
Здесь задаём разные анализаторы для английского и русского языков. Поле _lang используется как ключ для определения языка документа, что помогает Elasticsearch автоматически применять нужный анализатор.
Как работать с метаданными
Само поле _lang — это лишь один из примеров. Можно расширять аннотирование, добавляя метаданные о типе контента, версии документа и времени создания. Это позволяет строить более сложные запросы и фильтровать данные на основе этих параметров.
Вот несколько метаданных, которые помогут в многоязычном поиске:
_lang (язык) — как мы уже говорили, это обязательное поле для многоязычных проектов.
version (версия) — полезно для поиска по версиям документа. Например, если ты хранишь черновики и финальные версии.
timestamp (время создания) — важно для фильтрации документов по дате, особенно когда нужно искать самые свежие материалы на заданном языке.
Пример структуры с дополнительными метаданными:
{ "title": "Welcome", "body": "This is an example of English text.", "_lang": "en", "version": "1.0", "timestamp": "2024-09-20T12:00:00" }
Теперь рассмотрим, как правильно настроить индекс Elasticsearch для работы с метаданными. Предположим, есть документы на разных языках, и нужно, чтобы поиск был быстрым и эффективным, а релевантность результатов сохранялась на высоте.
Пример настройки индекса:
{ "settings": { "index": { "number_of_shards": 3, "number_of_replicas": 1 }, "analysis": { "analyzer": { "default": { "type": "custom", "tokenizer": "standard", "filter": ["lowercase"] } } } }, "mappings": { "properties": { "title": { "type": "text" }, "body": { "type": "text" }, "_lang": { "type": "keyword" }, "version": { "type": "keyword" }, "timestamp": { "type": "date", "format": "yyyy-MM-dd'T'HH:mm:ss" } } } }
Здесь указываем, что поле _lang должно быть проиндексировано как keyword (точное значение).
Теперь, когда документы содержат метаданные, можно использовать их для фильтрации и сортировки. Например, хочется получить все русскоязычные документы, отсортированные по дате создания:
Пример запроса:
{ "query": { "bool": { "must": [ { "match": { "_lang": "ru" } } ] } }, "sort": [ { "timestamp": { "order": "desc" } } ] }
Этот запрос вернёт все документы на русском языке, отсортированные по времени создания (от более новых к старым).
Так что не ленитесь настраивать метаданные правильно — это тот случай, когда вложенные усилия окупаются качественным и быстрым поиском.
Заключение
В этой статье мы разобрали, как грамотно настраивать анализаторы для разных языков, использовать метаданные для фильтрации и оптимизировать процесс обработки документов. Помните, что поиск — это не просто индексация данных, это создание комфортного интерфейса между пользователем и информацией.
Надеюсь, данное руководство поможет вам сделать этот процесс более удобным.
Больше про инфраструктуру приложений коллеги из OTUS рассказывают в рамках практических онлайн-курсов. С полным списком курсов можете ознакомиться в каталоге.
