Точное совпадение без учета регистра без нормализации в Elasticsearch 6.2

Я просмотрел все статьи и сообщения о выполнении запросов с точным соответствием и без учета регистра, но после реализации они не выполняют то, что я ищу.

Прежде чем пометить этот вопрос как повторяющийся, прочтите сообщение целиком.

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

Я попытался указать анализатор lowercase для своего свойства username и использовать запрос match для реализации этого поведения. Хотя это решает проблему нечувствительного к регистру сопоставления, оно не дает точного сопоставления.

Я рассмотрел возможность использования нормализатора lowercase, но это сделало бы все мои имена пользователей строчными перед индексацией, поэтому, когда я объединяю имена пользователей, они возвращались бы в строчной форме, а это не то, что я хочу. Мне нужно сохранить исходный регистр каждой буквы в имени пользователя.

Я хочу следующее поведение:


Вставка пользователей

POST {elastic}/users/_doc

{
    "email": "[email protected]",
    "username": "UsErNaMe",
    "password": "1234567"
}

Этот документ будет сохранен в индексе под названием users в точности так, как он есть.

Получение пользователя по имени пользователя

GET {frontend}/user/UsErNaMe

должен вернуться

{
    "email": "[email protected]",
    "username": "UsErNaMe",
    "password": "1234567"
}

а также

GET {frontend}/user/username

должен вернуться

{
    "email": "[email protected]",
    "username": "UsErNaMe",
    "password": "1234567"
}

а также

GET {frontend}/user/USERNAME

должен вернуться

{
    "email": "[email protected]",
    "username": "UsErNaMe",
    "password": "1234567"
}

а также

GET {frontend}/user/UsErNaMe $RaNdoM LeTteRs

не должен ничего возвращать.

Спасибо.


person Hid    schedule 18.04.2019    source источник


Ответы (1)


Чтобы добиться точного совпадения без учета регистра, вам необходимо определить собственный анализатор. Анализатору необходимо выполнить два действия:

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

Вышеупомянутые два могут быть достигнуты с помощью:

  1. используйте фильтр lowercase при определении пользовательского анализатора.
  2. установите tokenizer в keyword, это гарантирует, что будет сгенерирован единственный токен входного значения после применения фильтра в нижнем регистре.

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

Итак, чтобы создать индекс, вы можете использовать ниже:

PUT test
{
  "settings": {
    "analysis": {
      "analyzer": {
        "case_insensitive_analyzer": {
          "type": "custom",
          "filter": [
            "lowercase"
          ],
          "tokenizer": "keyword"
        }
      }
    }
  },
  "mappings": {
    "_doc": {
      "properties": {
        "email": {
          "type": "text",
          "fields": {
            "keyword": {
              "type": "keyword"
            }
          }
        },
        "username": {
          "type": "text",
          "analyzer": "case_insensitive_analyzer"
        },
        "password": {
          "type": "keyword"
        }
      }
    }
  }
}

В приведенном выше примере case_insensitive_analyzer - это требуемый анализатор, и, как вы можете видеть, он применяется к полю username.

Итак, когда вы индексируете документ, как показано ниже:

PUT test/_doc/1
{
  "email": "[email protected]",
  "username": "UsErNaMe",
  "password": "1234567"
}

для поля username ввод - UsErNaMe. Анализатор сначала применяет фильтр lowercase на входе UsErNaMe, в результате чего получается значение username. Теперь к этому значению username он применяет keyword токенизатор, который ничего не делает, кроме вывода значения, полученного после применения фильтра (ов), в виде одного токена, то есть username.

Теперь вы можете использовать запрос соответствия, как показано ниже, для поиска по полю имени пользователя:

GET test/_doc/_search
{
  "query": {
    "match": {
      "username": "USERNAME"
    }
  }
}

Использование выше даст вам желаемый результат. Замените USERNAME в запросе выше на username, UsErNaMe или USERname, все будет соответствовать документу. Причина этого в том, что при поиске, если анализатор не указан явно, elasticsearch использует анализатор, примененный к полю при индексировании. В приведенном выше случае при поиске по полю username к входному значению будет применено case_insensitive_analyzer, то есть USERNAME, что приведет к токену username и, следовательно, к совпадению.

person Nishant    schedule 18.04.2019
comment
Спасибо за помощь. Я уже пробовал это и думал, что это не сработало, но оказалось, что я забыл переключиться на запрос соответствия для одного из моих маршрутов в моем сервисе. Как только я внес все необходимые изменения, все заработало отлично. - person Hid; 18.04.2019