Как работает поиск записей

Примечание

Учитывайте, что данные необходимо своевременно переиндексировать для верной работы поиска. Переиндексация необходима, если есть значимые изменения в структуре данных (модели данных), и если было загружено много записей

Механизм поиска

При поиске через основную строку поиска происходит следующее:

Шаг 1. Клиент (frontend) посылает на сервер (backend) запрос с полями для поиска (searchFields), текстом для поиска (text), именем реестра/справочника для поиска (entity) и текущим временем (asOf):

  • Клиент выбирает следующие поля для поиска: все атрибуты, помеченные как "Поисковый"; etalon ID; даты создания и обновления; границы периода актуальности.

Шаг 2. Сервер добавляет к запросу условие, по которому asOf должно быть в пределах периода актуальности, а так же условие, что запись и период актуальности не удалены (активны).

Шаг 3. Поисковые поля разбиваются на две части, создаются несколько подзапросов поиска, при этом для нахождения записи необходимо удовлетворение условий по крайней мере одного запроса:

  • Не строковые – создается match query (если одно поле) или multi-match query (если несколько полей). Необходимо полное совпадение введенного текста со значением в поле:

    • Для логических полей – true/false;

    • Для численных полей – с точкой в качестве разделителя целой и дробной части;

  • Строковые - создается match query (если одно поле) или multi-match query (если несколько полей), где введенный текст анализируется analyzer'ом standard (документация актуальна и для Opensearch) и разбивается на токены, при этом каждый из токенов должен найтись хотя бы в одном из искомых полей, т.е. совпасть с одним из токенов в нем (для атрибута с именем attrName поиск будет идти по полю с именем attrName). Все приведенные ниже параметры см. в разделе Параметры системы.

    • Учитывается параметр нечеткого поиска org.unidata.mdm.search.fuzziness (неотрицательное целое число, по умолчанию 1) – допустимая разница между токенами для поиска и токенами полей. Разница (расстояние Левенштейна) измеряется в количестве операций добавления, удаления и замены символа, необходимых для получения одного токена из другого;

    • Учитывается параметр нечеткого поиска org.unidata.mdm.search.fuzziness.prefix.length (неотрицательное целое число, по умолчанию 4) – количество символов в начале токена, обязательных для совпадения;

    • Количество возможных токенов, совпадающих по нечеткому поиску, ограничено числом 50, это число зашито в коде сервера (ограничение производительности);

Примечание:

  • После импорта из не системного источника данных, поиск по системному EtalonID до первого обновления записи работать не будет. При этом EtalonID будет отображаться в карточке записи.

Нечеткий поиск

При включенной опции org.unidata.mdm.search.fuzziness.with.wildcard (включена по умолчанию, см. раздел Параметры системы) добавляется wildcard query для каждого поля (для анализируемых атрибутов используется поле attrName.$nan):

  • Если в тексте поиска одно слово, то необходимо удовлетворение тексту поля условию "текст*", где текст – текст поиска, * – любой текст (в версиях 5.х и до версии 6.5 поиск был по "текст");

  • Если в тексте поиска несколько слов, то необходимо удовлетворение тексту поля всех условий "слово", где слово – каждое слово, * – любой текст.

При стандартных настройках для нахождения записи без дополнительных критериев поиска необходимо выполнение всех трех условий:

  • Запись должна быть активна (т.е. не удалена);

  • Период актуальности записи должен быть активен (т.е. не удален) и попадать в текущее время;

  • Выполнение хотя бы одного из условий:

    • Полное совпадение текста по хотя бы одному из не строковых поисковых полей;

    • Нечеткое совпадение текста по хотя бы одному из строковых поисковых полей;

    • wildcard совпадение текста по хотя бы одному из строковых поисковых полей.

Примечания:

  • Опция "Морфологический поиск" у атрибута позволяет задать дополнительный критерий – поиск по атрибуту с типом поиска "Морфологический". На поиск по основной строке поиска эта опция не влияет.

  • Опция "Регистронезависимый поиск" у атрибута влияет только на wildcard часть запроса поиска и на поиск по атрибуту (дополнительный критерий).

  • Символы "*", "?" интерпретируются как обычные символы.

Пример 1

Предположим, что у записи есть один простой строковый атрибут strAttr со значением "Обычная радость".

  • В поле strAttr.$default будут содержаться токены "обычная" и "радость";

  • В поле strAttr будут содержаться токены "о", "об", "обы", "обыч", "обычн", "обычна", "обычная", "р", "ра", "рад", "радо", "радос", "радост", "радость";

  • Поле strAttr.$nan будет иметь значение "Обычная радость".

При поиске:

  • "рад" найдется запись по совпадению текста с полем strAttr с токеном "рад";

  • "обычно" найдется запись по нечеткому совпадению текста с полем strAttr с токенами "обычн", "обычна".

Пример 2

Текст Токарный ** анализируется стандартным анализатором OpenSearch: разбивается на токены, очищается от мусора. Таким образом, поиск идет только по слову "токарный".

А уже к этому поиску с условием ИЛИ добавляется wildcard поиск: (атрибут1 = ~токарный~ И атрибут1 = ~**~) ИЛИ (атрибут2 = ~токарный~ И атрибут2 = ~**~) ИЛИ т.д.

где знаком ~ обозначены любые окружающие символы.

За счет части wildcard релевантность у "Токарный ** станок" будет выше, т.е. в результате поиска будет выше, чем "Токарный ++ станок" и "Токарный станок".

Морфологический поиск

Примечание

Опция "Морфологический поиск" у атрибута позволяет задать дополнительный критерий – поиск по атрибуту с типом поиска "Морфологический". На поиск по основной строке поиска эта опция не влияет.

Морфологический поиск позволяет при поиске по значению атрибута производить поиск по словам с одинаковой основой (часть слова без окончания).

При вводе нескольких слов необходимо нахождение всех слов в атрибуте. Например, если у записи в атрибуте записано значение "Обычная радость", то:

  • При поиске "обычный", "обычному", "радостям", "обычные радости" эта запись найдется;

  • При поиске "обычненький", "радостный", "обычненькая радость" эта запись не найдется.

Кроме того, опция org.unidata.mdm.search.fuzziness.with.wildcard влияет на морфологический поиск. Если она включена, то вместо нахождения всех поисковых слов по основе слова допустимо:

  • Если в тексте поиска одно слово, то удовлетворение значения атрибута условию "текст", где текст – текст поиска, * – любой текст;

  • Если в тексте поиска несколько слов, то удовлетворение значения атрибута всех условий "слово", где слово – каждое слово, * – любой текст.

Морфологический поиск зависит от установленных словарей Hunspell (в Elasticsearch они предустановлены, в Opensearch необходима самостоятельная установка).

Проверить, к какому слову или к каким словам сводится интересующее слово или предложение, можно следующим образом:

  • Получите индекс с соответствующим analyzer'ом одним из следующих способов:

    • Используйте существующий индекс реестра/справочника с атрибутом, у которого включен морфологический поиск (по умолчанию для реестра funnyRegister имя индекса будет иметь вид default_default_funnyregister);

    • Создайте такой индекс следующим запросом PUT localhost:9200/test_index_name. Пример запроса:

      {
          "settings": {
              "analysis" : {
                  "analyzer" : {
                      "unidata_morph_analyzer" : {
                          "tokenizer" : "standard",
                          "filter" : [ "lowercase", "hunspell_ru_RU" ]
                      }
                  },
                  "filter" : {
                      "hunspell_ru_RU" : {
                          "type" : "hunspell",
                          "locale" : "ru_RU",
                          "dedup" : false,
                          "longest_only": true
                      }
                  }
              }
          }
      }
      
    • Выполните запрос POST localhost:9200/test_index_name/_analyze:

      {
        "text": "слово или предложение для проверки",
        "analyzer": "unidata_morph_analyzer"
      }
      

См. также инструкцию по настройке библиотеки Hunspell.

Механизм индексации

Подробнее о том, что такое analyzer и его составные части character filter, tokenizer и token filter см. в официальной документации Elasticsearch. Документация также актуальна и для Opensearch.

Кастомные token filter'ы:

  • autocomplete_filter – edge_ngram с параметрами min_gram = 1 и max_gram = 55

  • hunspell_ru_RU, hunspell_en_US – hunspell с параметрами dedup = true, longest_only = true

analyzer'ы для индексации:

  • unidata_default_analyzer

    • tokenizer – standard (либо whitespace, если на реестре/справочнике указан custom property с именем "tokenize_on_chars" и значением "whitespace")

    • token filters – lowercase, autocomplete_filter

  • unidata_search_analyzer

    • tokenizer – standard (либо whitespace, если на реестре/справочнике указан custom property с именем "tokenize_on_chars" и значением "whitespace")

    • token filters – lowercase

  • unidata_morph_analyzer

    • tokenizer – standard (либо whitespace, если на реестре/справочнике указан custom property с именем "tokenize_on_chars" и значением "whitespace")

    • token filters – lowercase, hunspell_ru_RU, hunspell_en_US

Принцип использования

analyzer'ы используются при индексировании строковых атрибутов (кроме кодовых атрибутов).

Индексируются те атрибуты, которые отмечены как "Поисковый", "Отображаемый", "Главный отображаемый" или "Уникальный", а также если являются кодовыми.

Индексация для строкового атрибута с именем attrName происходит несколькими способами в поля с различными именами:

  • attrName – text, используется unidata_default_analyzer

  • attrName.$default – text, используется unidata_search_analyzer

  • attrName.$nan – keyword, если у атрибут включена опция "Регистронезависимый поиск", то дополнительно используется lowercase normalizer

  • Если у атрибута включена опция "Морфологический поиск": attrName.$morph - text, используется unidata_morph_analyzer.

Другие строковые поля (etalon ID, кодовые строковые атрибуты и др.) индексируются как keyword (без анализа, т.е. как есть). Другие строковые поля (etalon ID, кодовые строковые атрибуты и др.) индексируются как keyword (без анализа, т.е. как есть).