Создание поисковой системы, позволяющей использовать синонимы, - один из самых эффективных способов повысить релевантность результатов поиска для ваших конечных пользователей. Вы можете обнаружить, что пользователи жалуются на то, что не получают результатов, которые они себе представляют, потому что вместо точного ключевого слова в результатах поиска они использовали синоним слова и не получили желаемых результатов.
Например, представим, что вы создали поисковую систему для мебельного магазина. Вы импортировали эти 3 документа:
1 2 3 4 5 6 7 8 9 10 11 12 | { "id":"1" "product":"red leather sofa" } { "id":"2" "product":"1950 FABRIC couch " } { "id":"3" "product":"corner sectional, leather, extra large" } |
Когда пользователь ищет слово "sofa", он получит только второй результат и не увидит два других релевантных продукта. Здесь и кроется важность включения синонимов в OpenSearch; мы хотим, чтобы пользователь, набравший "couch", "sofa" или "sectional", увидел все три результата без необходимости самостоятельно придумывать все возможные синонимы. Наше решение: фильтр синонимов.
Обзор анализа текста
Прежде чем мы перейдем к рассмотрению особенностей работы фильтра синонимов, давайте пройдемся по некоторым исходным данным, чтобы лучше понять, что мы делаем.
OpenSearch предлагает широкий набор функций для индексирования данных. Помимо того, что OpenSearch является отличным местом для хранения ваших данных, он выполняет некоторые базовые анализаторы данных за кулисами, чтобы сделать их пригодными для поиска. Анализаторы - это инструкции, которые даются OpenSearch по поводу того, как должны индексироваться/храниться ваши текстовые поля.
По умолчанию каждый индекс имеет анализатор по умолчанию, который используется, если не указан ни один, называемый стандартным анализатором, который подходит для многих случаев. Прекрасная особенность OpenSearch заключается в том, что вы можете создать и указать собственный анализатор, соответствующий вашим потребностям.
Анализатор состоит из двух основных компонентов: токенизатора и нуля или более фильтров токенов.
Токенизатор
Токенизатор определяет, каким образом OpenSearch возьмет набор слов и разделит его на отдельные термины, называемые "токенами". Наиболее распространенным токенизатором является токенизатор пробелов, который разбивает набор слов на пробелы. Например, такое поле, как "красный кожаный диван", будет проиндексировано в OpenSearch как 3 лексемы: "red", "leather" и "sofa". Или "1950 FABRIC couch" будет проиндексирован как "1950", "FABRIC" и "couch". Если пользователь введет любой из этих терминов, будет возвращен документ с любым из этих терминов.
Без токенизаторов все многословные поля сохранялись бы в виде полного предложения, для возврата результата которого требовалось бы точное совпадение, например, при поиске именно термина "красный кожаный диван". Не волнуйтесь, этот процесс токенизации происходит за кулисами, и вы по-прежнему будете видеть свой документ именно в том виде, в котором вы его импортировали, а не в виде кусков текста. OpenSearch предлагает множество различных типов токенизаторов: токены, которые создаются при смене регистра (нижний на верхний), при переходе от одного класса символов к другому (буквы к цифрам) и т. д...
Фильтр токенов
После создания токена он проходит через фильтры анализатора. Фильтр токенов - это операция, выполняемая над токенами, которая изменяет их тем или иным способом. Например, вы можете преобразовать все лексемы в строчные ("FABRIC" в "fabric"), удалить пробелы из каждой лексемы (изменив лексему "red leather sofa" на "redleathersofa") или взять каждую лексему и очистить ее до стеблей.
Что такое стебли? В английском языке вы не хотите, чтобы слова "walk", "walks", "walking" и "walked" индексировались как разные слова, поэтому фильтр лексем со стеблями возьмет все четыре этих слова и превратит их в "walk". Опять же, это не изменяет и не редактирует импортированный документ, вы по-прежнему будете видеть его в том виде, в котором импортировали, но при поиске все версии этого слова будут автоматически индексироваться как один и тот же стемпинг. Вы можете наложить столько фильтров маркеров, сколько захотите, чтобы они соответствовали вашему сценарию использования, единственное, на что следует обратить внимание, - это то, что фильтры маркеров соединяются и применяются в том же порядке, в каком они были указаны.
Токен-фильтры - это именно то место, где вы можете добавить поддержку синонимов в свои документы с помощью фильтра синонимов.
Как внедрить синонимы в OpenSearch с помощью фильтра токенов синонимов
Фильтр синонимов возьмет каждый токен, созданный в процессе токенизации, о котором говорилось выше, и проверит, соответствует ли он какому-либо термину, который вы определили в списке синонимов. Если да, то он сопоставит синонимы с этими маркерами. Если вы импортируете документ с "красным кожаным диваном" с помощью токенизатора пробелов, вы получите токены "красный", "кожаный" и "диван". Если вы добавите фильтр токенов синонимов, чтобы сопоставить слово "диван" со словами "couch" и "sectional", то, когда OpenSearch создаст токен "диван", он также создаст токены для синонимов, так что при вводе слова "диван" будет возвращен документ, который вы хранили с "red leather sofa".
Не вдаваясь в подробности, чтобы не потеряться в деталях, вы можете решить, будут ли эти маркеры создаваться в момент индексации документов или на лету в момент поиска. Другими словами, для анализаторов времени индексации вы можете сохранить слово "диван" как "couch" и "sectional" в качестве дополнительного маркера в индексе (занимая немного больше места для хранения), или, наоборот, для анализаторов времени поиска вы можете сохранить слово "диван" только как "sofa" в индексе, но когда пользователь ищет слово "диван", оно расширяется до слов "couch" и "sectional" во время поиска.
Одно из главных преимуществ использования анализаторов времени поиска по сравнению с анализаторами времени индекса заключается в том, что вы можете редактировать и добавлять синонимы на лету, не переиндексируя все документы. Переиндексация может стать головной болью, если вам придется повторять ее каждый раз, когда вы захотите внести изменения в список синонимов. Как правило, лучше всего использовать анализаторы синонимов во время поиска, а не во время индексации.
В связке для конкретного поля вы можете определить, как вы хотите сохранить лексемы в индексе (анализаторы времени индекса) и как вы хотите, чтобы термины анализировались при поиске пользователем (анализаторы времени поиска). По умолчанию они одинаковы.
Давайте рассмотрим пример:
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 33 34 35 36 37 38 | PUT /furniture-index { "settings": { "index": { "analysis": { "analyzer": { "my_synonym_analyzer": { "tokenizer": "whitespace", "filter": [ "lowercase", "my_synonym_token_filter" ] } }, "filter": { "my_synonym_token_filter": { "type": "synonym", "synonyms": [ "couch, sectional, sofa", "big, large => lrg", "small, tiny => sm, smll" ] } } } } }, "mappings": { "properties": { "product": { "type": "text", "analyzer": "standard", "search_analyzer": "my_synonym_analyzer" } } } } |
Сверху вниз мы создаем my_synonym_analyzer
, который будет выполнять токенизацию на белом пространстве, и фильтр токенов, который сначала переводит поиск в нижний регистр, а затем смотрит на my_synonym_token_filter
, чтобы перебрать потенциальные синонимы.
Как видите, в my_synonym_token_filter
синонимы определены тремя способами: первый способ делает три термина "couch", "sectional" и "sofa" эквивалентными, так что если пользователь ищет любое из этих трех слов, все остальные появятся.
Второй способ - однонаправленный: если пользователь набирает "большой" или "большой", будут возвращены результаты с "lrg", но если набрать "lrg", "большой" или "большой" не будет возвращен. При вводе "small" будут возвращены любые результаты с "sm" ИЛИ "smll". В конце команды мы задаем сопоставление для поля product, чтобы анализатором времени индекса был предопределенный стандартный
анализатор, а пользовательским анализатором времени поиска - my_synonym_analyzer
.
Давайте добавим несколько примеров документов:
1 2 3 4 5 6 7 | POST furniture-index/_bulk {"index":{"_id":"1"}} {"product":"smll red leather sofa"} {"index":{"_id":"2"}} {"product":"sm 1950 FABRIC couch"} {"index":{"_id":"3"}} {"product":"lrg corner sectional, leather"} |
Давайте запустим пример поиска:
1 2 3 4 5 6 7 8 | GET furniture-index/_search { "query": { "match": { "product": "sofa" } } } |
Все 3 продукта вернулись!
Однако, как правило, лучше всего использовать отдельный документ, который хранится локально на узлах, а не определять синонимы в строке, как мы делали выше.
Создайте документ под названием "my_list_synonyms.txt" в директории config
вашего экземпляра OpenSearch (допустимые форматы этого файла - Solr и WordNet):
1 2 3 | couch, sectional, sofa big, large => lrg small, tiny => sm, smll |
Для этого нам нужно изменить настройки индекса в my_synonym_token_filter
, добавив путь к файлу синонимов, который вы создали:
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 33 34 35 36 | PUT /furniture-index { "settings": { "index": { "analysis": { "analyzer": { "my_synonym_analyzer": { "tokenizer": "whitespace", "filter": [ "lowercase" , "my_synonym_token_filter" ] } }, "filter": { "my_synonym_token_filter": { "type": "synonym", "synonyms_path": "my_list_synonyms.txt" "updateable": true } } } } }, "mappings": { "properties": { "product": { "type": "text", "analyzer": "standard", "search_analyzer": "my_synonym_analyzer" } } } } |
Теперь, когда мы хотим внести изменения, поскольку мы установили фильтр на "updateable": true
, когда нам нужно внести изменения в синонимы, мы отредактируем файл и отправим эти две команды:
1 2 | POST /furniture-index/_reload_search_analyzers POST /furniture-index/_cache/clear?request=true |
Это перезагрузит все изменения, которые мы внесли в синонимы!
Если вы используете версию старше 7.3, вам придется временно закрыть, а затем снова открыть индекс, в который вы хотите перезагрузить файл синонимов, а не отправлять две вышеуказанные команды. При повторном открытии индекс перезагрузит новый файл синонимов.
Если в вашем кластере несколько узлов, обязательно обновите файл синонимов на каждом узле, чтобы обеспечить согласованный результат.
Заключение
Чтобы обеспечить пользователям релевантный поиск, требуется множество тонких настроек, и синонимы - отличный способ предоставить пользователям тот опыт, на который вы рассчитываете. Как и в случае с любой другой системой, важно постоянно тестировать и вносить изменения в систему, чтобы она оставалась актуальной и работала так, как вы задумали.