Elasticsearch подготавливает входящие текстовые данные для эффективного хранения и поиска. Текстовые поля проходят процесс анализа, формально называемый Text Analysis, в ходе которого содержимое текстовых полей разбивается на отдельные лексемы, которые затем обогащаются, преобразуются, синонимизируются, вычленяются и заносятся в инвертированные индексы.
Понимание процесса анализа текста поможет нам устранить проблемы, связанные с поиском. Изучение механики анализаторов и их строительных блоков поможет нам понять, как работать с анализаторами, а также как создавать собственные анализаторы для различных языков (если это необходимо).
В этой статье мы рассмотрим основы анализа текста и анализаторы (строительные блоки анализа текста), а затем подробно рассмотрим стандартный анализатор. Мы также рассмотрим несколько примеров, включая создание собственного анализатора.
Анализ текста
На процесс анализа текста возлагаются две функции: токенизация и нормализация.
- Токенизация - процесс разбиения текстового содержимого на отдельные слова путем вставки разделителя пробелов, буквы, шаблона или других критериев. Этот процесс выполняется компонентом, называемым токенизатором, единственная задача которого - разбить содержимое на отдельные слова, называемые токенами, следуя определенным правилам при разбиении содержимого на слова.
- Нормализация - это процесс, в ходе которого лексемы (слова) преобразуются, изменяются и обогащаются в виде стемминга, синонимов, стоп-слов и других характеристик. Стемминг - это операция, при которой слова сокращаются (стеммируются) до их корневых слов: например, "game" является корневым словом для "gaming", "gamer" включает форму множественного числа "gamers".
Анализ текста осуществляется с помощью так называемых анализаторов. Обе эти функции, токенизация и нормализация, выполняются модулем анализатора. Для этого анализатор использует один токенизатор и ноль или более фильтров токенов.
Elasticsearch предоставляет набор готовых анализаторов, которые подходят для большинства распространенных случаев использования. Помимо обычных стандартных анализаторов и анализаторов ключевых слов, наиболее заметными являются: простой, стоп, пробельный, шаблонный, языковой и некоторые другие анализаторы. Существуют также анализаторы для конкретных языков, таких как английский, немецкий, испанский, французский, хинди и так далее.
Несмотря на то, что этот процесс анализа можно настраивать, готовые анализаторы вполне подходят для большинства случаев. Если нам понадобится настроить новый анализатор, мы можем сделать это, смешав и подобрав один токенизатор и фильтры символов и лексем (компоненты, из которых состоит анализатор) из заданного каталога.
Анализаторы
Модуль анализатора состоит, по сути, из трех компонентов - фильтров символов, токенизатора и фильтров токенов, как показано на рисунке ниже. Эти три компонента образуют конвейер, через который проходит каждое из текстовых полей для обработки текста. В то время как фильтры символов и лексем являются необязательными в конвейере анализа текста, токенизатор является обязательным компонентом.
Давайте вкратце рассмотрим эти компоненты.
Фильтры символов
Задача символьного фильтра - удалить нежелательные символы из входной текстовой строки.
Elasticsearch предоставляет три символьных фильтра из коробки: html_strip, маппинг и pattern_replace.
Эти символьные фильтры являются необязательными. Например, когда текст
<\h1>ElasticSearch is awesome<\h1>
пропускается через фильтр символов html_strip, теги h1 будут удалены. Фильтр символов маппинга помогает сопоставить некоторый текст с альтернативным (например: h2o - вода, hcl - соляная кислота и т. д.), а фильтр pattern_replace сопоставляет текст с регулярным выражением (regex) и заменяет его на эквивалент (например, сопоставляет электронное письмо на основе regex и извлекает домен организации).
Токенизаторы
Токенизаторы разбивают текст на слова с помощью разделителей, таких как пробелы, знаки препинания или какие-то границы слов. Например, если фразу "ElasticSearch is AWESOME!!!" пропустить через токенизатор (пока мы рассматриваем стандартный токенизатор), результатом будет набор лексем: "elasticsearch", "is", "awesome". Стандартный токенизатор будет применять набор правил, основанных на грамматике. Токенизатор является обязательным компонентом конвейера - поэтому каждый анализатор должен иметь один и только один токенизатор.
Elasticsearch предоставляет несколько таких токенайзеров, которые помогают разделить входящий текст на отдельные лексемы. Затем эти слова можно пропустить через фильтры токенов для дальнейшей нормализации. По умолчанию Elasticsearch использует стандартный токенизатор, который разбивает слова на основе грамматики и пунктуации. В дополнение к стандартному токенизатору существует несколько готовых токенизаторов: стандартный, ключевых слов, N-грамм, шаблонов, пробельных символов, строчных символов и ряд других токенизаторов.
Фильтры токенов
Фильтры токенов работают с токенами, полученными токенизатором, для дальнейшей обработки. Например, токен может изменять регистр, создавать синонимы, определять корень слова (stemming), создавать n-граммы, шинглы и так далее. Фильтры токенов необязательны. Они могут быть как нулевыми, так и многими, связанными с модулем анализатора. Elasticsearch предоставляет длинный список фильтров токенов из коробки.
Встроенные анализаторы
Elasticsearch предоставляет более полудюжины готовых анализаторов, которые мы можем использовать на этапе анализа текста. Этих анализаторов, скорее всего, будет достаточно для базовых случаев, но если возникнет необходимость создать собственный анализатор, это можно сделать, инстанцировав новый модуль анализатора с необходимыми компонентами, входящими в состав этого модуля. В таблице ниже перечислены анализаторы, которые предоставляет нам Elasticsearch:
Анализатор | Описание |
Standard | Это стандартный анализатор, который выполняет токенизацию входного текста на основе грамматики, пунктуации и пробельных символов. Выходные лексемы приводятся к нижнему регистру и удаляются стоп-слова. |
Simple | Простой анализатор разделяет входной текст на любые небуквенные символы, такие как пробелы, тире, цифры и т. д. При этом выходные лексемы также переводятся в нижний регистр. |
Stop | Вариант простого анализатора с английскими стоп-словами, включенными по умолчанию. |
Whitespace | Работа анализатора пробельных символов заключается в токенизации входного текста на основе разделителей пробельных символов. |
Keyword | Анализатор ключевых слов не изменяет входной текст. Значение поля сохраняется как есть. |
Language | Как следует из названия, языковые анализаторы помогают работать с человеческими языками. Существуют десятки языковых анализаторов, таких как английский, испанский, французский, русский, хинди и так далее, для работы с разными языками. |
Pattern | Шаблонный анализатор разделяет лексемы на основе регулярного выражения (regex). По умолчанию все символы, не являющиеся словами, помогают разделить предложение на лексемы. |
Fingerprint | Анализатор отпечатков сортирует и удаляет дублирующиеся лексемы, чтобы получить единую конкатенированную лексему. |
Стандартный анализатор является анализатором по умолчанию и широко используется при анализе текста. В следующем разделе мы рассмотрим стандартный анализатор на примере.
Стандартный анализатор
Стандартный анализатор - это анализатор по умолчанию, в задачу которого входит токенизация предложений на основе пробельных символов, пунктуации и грамматики. Конвейер состоит из фильтров символов, одного стандартного токенизатора и двух фильтров лексем: строчных и стоп-слов. Однако по умолчанию фильтр стоп-слов отключен (пример включения стоп-слов мы рассмотрим далее в статье). На рисунке ниже показаны компоненты стандартного анализатора:
Как показано на рисунке, стандартный анализатор состоит из стандартного токенизатора и двух фильтров лексем: фильтра строчных и стоп лексем без фильтра символов. Ниже приведен пример того, что происходит, когда текстовое поле, содержащее следующее содержимое, обрабатывается текстовыми анализаторами:
“ElasticSearch is so cool and AWESOME !!!“
Текст подвергается токенизации, и список лексем разбивается на части, как показано здесь в сжатом виде:
["elasticsearch", "is", "so", "cool",""""", "and", "awesome",""""""]
Как видно из результатов, лексемы приведены к нижнему регистру, а восклицательные знаки были удалены. Эмодзи были сохранены как текстовые данные. Приведенный выше текст анализируется стандартным анализатором (по умолчанию), который выделяет слова на основе пробелов и удаляет небуквенные символы, например знаки препинания.
Мы можем проверить анализатор в действии, используя API _analyze, как показано в следующем коде:
1 2 3 4 | GET _analyze { "text": "ElasticSearch is so cool and AWESOME !!!" } |
Этот код анализирует заданный текст с помощью стандартного анализатора (помните, стандартный анализатор - это анализатор по умолчанию) и выдает результат, как показано на рисунке ниже:
Слова были разделены на основе пробельных символов и небукв (пунктуации), что является результатом работы стандартного токенизатора. Затем лексемы проходят через фильтр строчных лексем.
Мы можем добавить конкретный анализатор на этапе тестирования анализа текста, добавив в код дополнительный атрибут analyzer. Например, предыдущий запрос можно переписать, указав конкретный анализатор. Вот обновленный запрос с явным указанием анализатора:
1 2 3 4 5 | GET _analyze { "analyzer": "standard", "text": "ElasticSearch is so cool and AWESOME !!!" } |
Как вы можете себе представить, мы можем заменить значение анализатора на выбранное нами в зависимости от того, какой анализатор мы хотим протестировать, например: "стандартный" или "ключевое слово", например.
Несмотря на то, что стандартный анализатор объединен с фильтром стоп-слов, по умолчанию фильтр стоп-слов отключен. Однако мы можем включить его, настроив его свойства, о чем пойдет речь в следующем разделе.
Фильтр английских стоп-слов
Elasticsearch позволяет настроить несколько параметров, таких как фильтр стоп-слов, путь к стоп-словам и максимальная длина лексем в стандартном анализаторе во время создания индекса. Давайте рассмотрим пример включения английских стоп-слов в стандартном анализаторе.
Мы можем сделать это, добавив фильтр во время создания индекса:
1 2 3 4 5 6 7 8 9 10 11 12 13 | PUT my_index_with_stopwords { "settings": { "analysis": { "analyzer": { "standard_with_stopwords":{ "type":"standard", "stopwords":"_english_" } } } } } |
Блок настроек используется для установки анализатора по нашему выбору. Поскольку мы модифицируем встроенный стандартный анализатор, мы даем ему уникальное имя (standard_with_stop words - это имя анализатора в нашем случае) и устанавливаем ему стандартный тип (то есть стандартный анализатор) с включенными английскими стоп-словами (stop words имеет значение _english_).
Теперь, когда мы создали индекс со стандартным анализатором, настроенным на стоп-слова, любой индексируемый текст будет проходить через этот модифицированный анализатор (при условии, что поле было предварительно объявлено с этим анализатором на этапе маппинга). Чтобы проверить это, мы можем вызвать конечную точку _analyze на индексе, как показано здесь в листинге кода:
1 2 3 4 5 | POST my_index_with_stopwords/_analyze { "text": "ElasticSearch is so cool and AWESOME !!!", "analyzer": "standard_with_stopwords" } |
Как вы можете видеть в выводе этого вызова ниже, такие распространенные слова, как "is" и "and", были удалены нашим анализатором слов standard_with_stop:
["elasticsearch", "so", "cool","""""","awesome",""""""]
Фильтр стоп-слов в немецком языке
Мы можем изменить стоп-слова для выбранного нами языка. Например, создайте новый индекс, аналогичный индексу my_index_with_stopwords, но измените стоп-слова с _english_ на _german_:
1 2 3 4 5 6 7 8 9 10 11 12 13 | PUT my_index_with_stopwords_german { "settings": { "analysis": { "analyzer": { "standard_with_stopwords_german":{ "type":"standard", "stopwords":"_german_" } } } } } |
Мы можем проверить текст с помощью вышеупомянутого анализатора standard_with_stopwords_german:
1 2 3 4 5 | GET my_index_with_stopwords_german/_analyze { "text": ["ElasticSearch ist genial"], "analyzer": "standard_with_stopwords_german" } |
В результате будет удалено слово ist (ist - распространенное стоп-слово, равное "is" в английском языке) и получены только две лексемы: "ElasticSearch", "genial" (= awesome).
Стоп-слова из файла
В предыдущих примерах мы дали анализатору подсказку, какие стоп-слова он должен использовать, английские или немецкие, и т. д., упомянув готовый фильтр стоп-слов. Мы также можем пойти дальше, предоставив стоп-слова через явный файл (а не полагаясь на встроенные стоп-слова).
Допустим, мы не хотим, чтобы пользователи вводили в наше приложение бранные слова. Мы можем создать файл со всеми бранными словами из черного списка и добавить путь к нему в качестве параметра к стандартному анализатору. Файл должен находиться относительно папки config в домашнем каталоге Elastisearch. В следующем листинге индекс создается с помощью анализатора, принимающего файл со стоп-словами:
1 2 3 4 5 6 7 8 9 10 11 12 13 | PUT index_with_swear_stopwords { "settings": { "analysis": { "analyzer": { "swearwords_analyzer":{ "type":"standard", "stopwords_path":"swearwords.txt" } } } } } |
Мы создаем индекс с параметром stopwords_path, указывающим на файл swearwords.txt. Предполагается, что этот файл находится в директории внутри папки конфигурации Elasticsearch. Файл состоит из черного списка ругательств:
- file:swearwords.txt
- damn
- bugger
- bloody hell
- what the hell
- sucks
Как только индекс будет готов с поддержкой приема стоп-слов из файла, мы готовы запустить анализатор с пользовательскими бранными словами:
1 2 3 4 5 | POST index_with_swear_stopwords/_analyze { "text": ["Damn, that sucks!"], "analyzer": "swearwords_analyzer" } |
Этот код должен остановить первое и последнее слова, проходящие через процесс индексации, потому что эти два слова были в нашем черном списке бранных слов.
До сих пор мы подробно работали со стандартным анализатором. Остальные встроенные анализаторы работают по аналогичной схеме. Elasticsearch предоставляет большую гибкость при работе с анализаторами: если готовые анализаторы вам не подходят, вы можете создавать свои собственные анализаторы. Эти собственные анализаторы могут представлять собой смесь существующих компонентов из обширной библиотеки компонентов Elasticsearch.
Пользовательские анализаторы
Создать пользовательский анализатор не так сложно, как кажется: достаточно создать и задать "пользовательский" тип анализатора в процессе создания индекса в настройках конфигурации. Как правило, мы можем создать пользовательский анализатор с любым количеством фильтров (как символьных, так и токенов), но только с одним токенизатором.
Например, приведенный ниже код создает пользовательский анализатор с одним фильтром символов html_strip, одним фильтром токенов верхнего регистра и использует готовый стандартный токенизатор:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | PUT my_index_with_custom_analyzer { "settings": { "analysis": { "analyzer": { "my_custom_analyzer":{ "type":"custom", "char_filter":["html_strip"], "tokenizer":"standard", "filter":["uppercase"] } } } } } |
В определении my_custom_analyzer задается тип custom, а затем указываются необходимые компоненты, из которых состоит анализатор. После создания индекса мы можем протестировать анализатор, прежде чем вводить его в действие. Следующий фрагмент кода представляет собой тестовый сценарий:
1 2 3 4 5 | POST my_index_with_custom_analyzer/_analyze { "text": "<H1>elasticsearch is AwEsOmE</H1>", "analyzer": "my_custom_analyzer" } |
Эта программа выдает три лексемы: ["ELASTICSEARCH", "IS", "AWESOME"], что указывает на то, что наш фильтр html_strip удалил HTML-теги H1, прежде чем позволить стандартному токенизатору разделить поле на четыре лексемы на основе разделителя пробелов. Наконец, лексемы были приведены к верхнему регистру, когда они проходили через фильтр лексем верхнего регистра.
Расширенная настройка
Хотя стандартные конфигурации компонентов анализатора чаще всего работают, иногда нам может понадобиться создать анализатор с нестандартными конфигурациями компонентов, входящих в его состав. Скажем, мы хотим использовать фильтр маппинга символов, который будет сопоставлять символы & и < и > с символами меньше и больше соответственно и так далее.
Предположим, что нам требуется разработать пользовательский анализатор, который будет анализировать текст на предмет английских алфавитов, чтобы получить расширенное слово - например: a - apple, b - bat и т. д. В дополнение к расширению этих алфавитов до слов мы также хотим получить краевые n-граммы.
Давайте сначала напишем код для нашего пользовательского анализатора с расширенной конфигурацией. После этого мы обсудим различные компоненты. Следующий листинг демонстрирует код для создания индекса с настройками анализа.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | PUT index_with_alpabet_words_custom_analyzer { "settings": { "analysis": { "analyzer": { "alphabet_word_custom_analyzer":{ "type":"custom", "char_filter":["alphabet_word_mapper"], "tokenizer":"standard", "filter":["lowercase", "edge_ngrammer"] } }, "char_filter": { "alphabet_word_mapper":{ "type":"mapping", "mappings":[ "a => apple", "b => ball", "c => cat" ] } }, "filter": { "edge_ngrammer":{ "type":"edge_ngram", "min_gram":1, "max_gram":5 } } } } } |
Код в листинге немного сложен, однако разобраться в нем легко и просто. В первой части, где мы определяем пользовательский анализатор, мы предоставляем список фильтров (как символьных, так и лексемных, если необходимо) и токенизатор. Вы можете представить это как точку входа в определение анализатора.
Во второй части кода определяются фильтры, на которые ссылались ранее. Например, alphabet_word_mapper, который определяется в новой секции char_filter, использует тип маппинга в качестве типа фильтра с набором маппингов. В маппинге мы расширяем и отображаем каждую из букв: a -> apple, b -> bat и так далее.
То же самое относится и к блоку фильтра, который определяет фильтр edge_ngrammer. Фильтр edge_ngram выводит edge_ngrams из лексем. Так, например, если на вход подано слово "a", то фильтр символов (alphabet_word_mapper) маппирует его в "apple" в соответствии с настройками фильтра. Затем он проходит через стандартный токенизатор и, наконец, попадает в фильтр лексем: edge_ngrammer. Задача edge_ngrammer - предоставить edge_ngrams лексем, поэтому в данном случае он выдает: a, ap, app, appl, apple.
Мы можем выполнить тест и попробовать его в действии:
1 2 3 4 5 | POST index_with_alpabet_words_custom_analyzer/_analyze { "text": "a", "analyzer": "alphabet_word_custom_analyzer" } |
Это выводит edge_ngram'ы яблока (a, ap, app, appl, apple).
Заключение
- Анализ текста - это процесс анализа текстовых полей с помощью встроенных или пользовательских анализаторов. Нетекстовые поля не анализируются.
- Анализатор состоит из фильтров символов, токенизатора и фильтров лексем. Каждый анализатор должен имет один и только один токенизатор, в то время как фильтры символов и токенов необязательны и могут быть более одного.
- Стандартный анализатор - это анализатор по умолчанию. В нем нет фильтра символов, есть стандартный токенизатор и два фильтра лексем (строчные и стоп); стоп-фильтр по умолчанию отключен.
- Фильтры символов помогают удалить ненужные символы из поля ввода. Есть три готовых символьных фильтра: html_strip, mapping и pattern_replace.
- Токенизаторы действуют на поля, которые были обработаны символьными фильтрами, или на необработанные поля. Elasticsearch предоставляет несколько готовых токенизаторов.
Фильтры токенов работают с токенами, которые выдает токенизатор. - Если встроенные анализаторы не удовлетворяют нашим потребностям, мы можем создать новый анализатор, взяв существующие токенизаторы с символьными или токенными фильтрами.