20 октября 2019

"ё"... "е"... какая мне разница? Программа должна найти!

 Буква "ё" совсем не важна... Завтра похолодает и мы все передохнем от жары...

    Иногда я завидую англоязычным программистам. Например, на клавиатуре им не надо переключать раскладку. А ещё у них нет буквы "Ё"! Буква "Ё" - это очень своенравная особа. Она обязательна только в собственных именах, а в остальных случаях употребление "Ё" или "Е" определяется выбором автора текста. Но при этом пользователя, который ищет по тексту, не заботит, что написал автор - "Ё" или "Е". А какая ему разница? Программа должна найти!

Посмотрим, как Elasticsearch решит эту проблему.

Попытка №1. Анализатор "russian"

В Elasticsearch есть анализаторы, которые преобразуют исходный текст в набор токенов. Среди стандартных анализаторов есть целый набор анализаторов, предназначенных для анализа конкретного языка. Например, русского - "russian".

Создаем индекс "test" и маппируем ему поле "fairytale" с анализатором "russian":
PUT http://localhost:9200/test
{
  "mappings": {
    "properties": {
      "fairytale": {"type": "text", 
                    "analyzer": "russian"
      }
    }
  }
}
В индексе "test" создаем новый документ:
POST http://localhost:9200/test/_doc
{"fairytale": "В лесу растёт дуб"}
Поиск по точному совпадению по слову "растёт"
POST http://localhost:9200/test/_search
{"query": {"match": {"fairytale": "растёт"}}}
находит наш документ.

А вот поиск по слову "растет"
POST http://localhost:9200/test/_search
{"query": {"match": {"fairytale": "растет"}}}
ни находит ни чего.

Вот вам и "русский" анализатор! Явно делали его нерусские...

Попытка №2. Свой анализатор

Теперь при создании индекса опишем свой анализатор "e_ru" на основе стандартного анализатора "russian", который для нашего поля "fairytale" преобразует "Ё" в "Е":
PUT http://localhost:9200/test_e
{
  "settings": {
    "analysis": {
      "filter": {
        "ru_stop": {
          "type": "stop",
          "stopwords": "_russian_"
        },
        "ru_stemmer": {
          "type": "stemmer",
          "language": "russian"
        }
      },
      "char_filter": {
        "e_char_filter": {
          "type": "mapping",
          "mappings": [ "Ё => Е", "ё => е" ]
        }
      },
      "analyzer": {
        "e_ru": {
          "tokenizer": "standard",
          "filter": [
            "lowercase",
            "ru_stop",
            "ru_stemmer"
          ],
          "char_filter": ["e_char_filter"]
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "fairytale": {
        "type": "text",
        "analyzer": "e_ru"
        }
      }
    }
}

В индексе "test_e" создаем такой же документ как в "test":
POST http://localhost:9200/test_e/_doc
{"fairytale": "В лесу растёт дуб"}
И пытаемся искать. Поиск по слову "растёт"
POST http://localhost:9200/test_e/_search
{"query": {"match": {"fairytale": "растёт"}}}
находит наш документ.

И поиск по слову "растет"
POST http://localhost:9200/test_e/_search
{"query": {"match": {"fairytale": "растет"}}}
тоже находит наш документ.

Как видим, грамотно настроенный "char_filter" в анализаторе может значительно усовершенствовать поиск. Например, приручит букву "Ё", уберет из строки не нужные HTML-тэги или улучшит отчет о прибыли заменив в нем символ ₽ на €.

3 комментария:

  1. Можете подсказать, как сделать тоже самое с яйцо, яицо?

    ОтветитьУдалить
    Ответы
    1. Самое простое, что приходит в голову - добавить "й" в mappings:
      "mappings": [ "Ё => Е", "ё => е" ] заменить на "mappings": [ "Ё => Е", "ё => е", "й => и" ]
      и переиндексировать документ

      Удалить
  2. Анонимный10 июля, 2023 16:25

    Добрый день, подскажите пожалуйста, данный аналайзер будет влиять на сортировку?

    ОтветитьУдалить