Различия между ними и как они себя ведут

Многие люди, которые только начали изучать Elasticsearch, часто путают типы данных поля Text и Keyword. Разница между ними простая, но очень важная. В этой статье я расскажу о разнице, как их использовать, как они себя ведут и какой из них использовать.

Различия

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

Если вы только начинаете изучать Elasticsearch и все еще не знаете, что такое Inverted Index and Analyzer, я рекомендую сначала прочитать Основное руководство по Elasticsearch.

Как их использовать

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

Это примеры настроек сопоставления для типа Text и Keyword, обратите внимание, что я буду использовать индекс с именем «text-vs-keyword», который я создал ранее для этого примера.

Сопоставление ключевых слов

curl --request PUT \
  --url http://localhost:9200/text-vs-keyword/_mapping \
  --header 'content-type: application/json' \
  --data '{
 "properties": {
  "keyword_field": {
   "type": "keyword"
  }
 }
}'

Отображение текста

curl --request PUT \
  --url http://localhost:9200/text-vs-keyword/_mapping \
  --header 'content-type: application/json' \
  --data '{
 "properties": {
  "text_field": {
   "type": "text"
  }
 }
}'

Несколько полей

curl --request PUT \
  --url http://localhost:9200/text-vs-keyword/_mapping \
  --header 'content-type: application/json' \
  --data '{
 "properties": {
  "text_and_keyword_mapping": {
   "type": "text",
   "fields": {
    "keyword_type": {
     "type":"keyword"
    }
   }
  }
 }
}'

Как они работают

Оба типа полей по-разному индексируются в инвертированном индексе. Разница в процессе индексирования повлияет на то, когда вы выполняете запрос в Elasticsearch.

Например, проиндексируем документ:

curl --request POST \
  --url http://localhost:9200/text-vs-keyword/_doc/example \
  --header 'content-type: application/json' \
  --data '{
 "keyword_field":"The quick brown fox jumps over the lazy dog",
 "text_field":"The quick brown fox jumps over the lazy dog"
}'

После выполнения указанной выше команды curl, если вы получите все документы в индексе, у вас должно быть:

[
      {
        "_index": "text-vs-keyword",
        "_type": "_doc",
        "_id": "example",
        "_score": 1.0,
        "_source": {
          "keyword_field": "The quick brown fox jumps over the lazy dog",
          "text_field": "The quick brown fox jumps over the lazy dog"
        }
      }
    ]

Ключевое слово

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

Итак, в приведенном выше примере, как будет выглядеть строка в перевернутом индексе?

Да, ты прав, все именно так, как ты пишешь.

Текст

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

У Elasticsearch есть API для проверки того, как будет выглядеть текст после процесса анализа, мы можем попробовать это с помощью:

curl --request POST \
  --url http://localhost:9200/text-vs-keyword/_analyze?pretty \
  --header 'content-type: application/json' \
  --data '{
  "analyzer": "standard",
  "text": "The quick brown fox jumps over the lazy dog"
}'

Итак, согласно приведенному выше ответу, именно так должен выглядеть инвертированный индекс для поля text_field

Только немного отличается от keyword, правда? Но вам нужно обратить внимание на то, что он хранит в инвертированном индексе, потому что это сильно повлияет на процесс запроса.

Запрос текста и ключевого слова

Теперь, когда мы понимаем, как ведут себя text и keyword при индексировании, давайте узнаем, как они ведут себя при запросе.

Во-первых, мы должны знать, что есть два типа запросов к строке:

  • Соответствующий запрос
  • Срок запроса

Так же, как Text и Keyword, разница между Match Query и Term Query заключается в том, что запрос в Match Query сначала будет проанализирован на термины, а запрос в Term Query - нет.

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

Запрос поля ключевого слова с помощью Term Query

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

Если мы попробуем с тем же самым запросом:

curl --request POST \
  --url 'http://localhost:9200/text-vs-keyword/_doc/_search?size=0' \
  --header 'content-type: application/json' \
  --data '{
 "query": {
  "term": {
   "keyword_field": "The quick brown fox jumps over the lazy dog"
  }
 }
}'

Elasticsearch вернет результат:

{
        "_index": "text-vs-keyword",
        "_type": "_doc",
        "_id": "example",
        "_score": 0.2876821,
        "_source": {
          "keyword_field": "The quick brown fox jumps over the lazy dog",
          "text_field": "The quick brown fox jumps over the lazy dog"
        }
      }
}

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

curl --request POST \
  --url 'http://localhost:9200/text-vs-keyword/_doc/_search?size=0' \
  --header 'content-type: application/json' \
  --data '{
 "query": {
  "term": {
   "keyword_field": "The"
  }
 }
}'

Он не дал результатов, потому что термин в запросе не соответствует ни одному из терминов в перевернутом индексе.

Запрос поля ключевого слова с помощью Match Query

Давайте сначала попробуем запросить ту же строку «Быстрая коричневая лиса перепрыгивает через ленивую собаку» с Match Query до keyword_mapping и посмотрим, что произойдет:

curl --request POST \
  --url http://localhost:9200/text-vs-keyword/_doc/_search \
  --header 'content-type: application/json' \
  --data '{
 "query": {
  "match": {
   "keyword_field": "The quick brown fox jumps over the lazy dog"
  }
 }
}'

Результат должен быть:

{
 "_index": "text-vs-keyword",
 "_type": "_doc",
 "_id": "example",
 "_score": 0.2876821,
 "_source": {
  "keyword_field": "The quick brown fox jumps over the lazy dog",
  "text_field": "The quick brown fox jumps over the lazy dog"
 }
}

Подождите, он не должен дать никакого результата, потому что термины, полученные в результате анализа запроса, не полностью соответствуют фразе «Быстрая коричневая лиса перепрыгивает через ленивую собаку» в перевернутом указателе, но почему это дает результат?

Правильно, запрос был проанализирован, потому что мы использовали Match Query, но вместо стандартного анализатора Elasticsearch использовал index-time анализатор, который был сопоставлен с типом данных поля Keyword. Поскольку анализатор, сопоставленный с типом данных поля Keyword, является Term Analyzer, Elasticsearch ничего не изменил в запросе.

А теперь попробуем со стандартным анализатором:

curl --request POST \
  --url http://localhost:9200/text-vs-keyword/_doc/_search \
  --header 'content-type: application/json' \
  --data '{
 "query": {
  "match": {
   "keyword_field": {
    "query": "The quick brown fox jumps over the lazy dog",
    "analyzer":"standard"
   }
  }
 }
}'

Никакого результата не происходит, потому что он анализирует запрос по терминам, и ничто не является точным совпадением с термином в инвертированном индексе.

Запрос типа текста с помощью Term Query

В проиндексированном документе текстового типа будет много терминов, как мы видели в предыдущем разделе. Чтобы показать, как запрос сопоставляется с терминами в перевернутом индексе, давайте попробуем два запроса. Первый запрос отправляет все предложение в Elasticsearch;

curl --request POST \
  --url 'http://localhost:9200/text-vs-keyword/_doc/_search?pretty=' \
  --header 'content-type: application/json' \
  --data '{
 "query": {
  "term": {
   "text_field": "The quick brown fox jumps over the lazy dog"
  }
 }
}'

второй только "The."

curl --request POST \
  --url 'http://localhost:9200/text-vs-keyword/_doc/_search?pretty=' \
  --header 'content-type: application/json' \
  --data '{
 "query": {
  "term": {
   "text_field": "The"
  }
 }
}'

Оба запроса не дают результатов.

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

Второй запрос тоже не дал результата. В проиндексированном документе есть «The», но помните, что анализатор использует слово в нижнем регистре, поэтому в инвертированном индексе оно сохраняется как «the»

Давайте снова попробуем Term Query с «the»:

curl --request POST \
  --url 'http://localhost:9200/text-vs-keyword/_doc/_search?pretty=' \
  --header 'content-type: application/json' \
  --data '{
 "query": {
  "term": {
   "text_field": "the"
  }
 }
}'

Ага! он дал результат, потому что запрошенное «the» является точным совпадением с «the» в инвертированном индексе.

Запрос типа текста с запросом на совпадение

Теперь пришло время для типа текста с Match Query, поскольку он анализирует оба типа, и их легко получить для получения результатов. Давайте сначала попробуем с двумя запросами

Первый запрос отправит «The» в Elasticsearch, мы знаем, что с term query он не дает результата, но как насчет match query?

Второй запрос отправит «собака LAZ споткнулась о БЫСТРУЮ коричневую собаку», некоторые слова находятся в инвертированном индексе, некоторые нет. Будет ли Elasticsearch давать какие-либо результаты?

curl --request POST \
  --url 'http://localhost:9200/text-vs-keyword/_doc/_search?pretty=' \
  --header 'content-type: application/json' \
  --data '{
 "query": {
  "match": {
   "text_field": "The"
  }
 }
}'
curl --request POST \
  --url 'http://localhost:9200/text-vs-keyword/_doc/_search?pretty=' \
  --header 'content-type: application/json' \
  --data '{
 "query": {
  "match": {
   "text_field": "the LAZ dog tripped over th QUICK brown dog"
  }
 }
}'

Ага! Оба они дали результат

{
        "_index": "text-vs-keyword",
        "_type": "_doc",
        "_id": "example",
        "_score": 0.39556286,
        "_source": {
          "keyword_field": "The quick brown fox jumps over the lazy dog",
          "text_field": "The quick brown fox jumps over the lazy dog"
        }
      }

Первый запрос дал результат, потому что «The» в запросе был проанализирован и стал «the», что является точным совпадением с тем, что указано в инвертированном индексе.

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

Если обратить внимание на результат, там есть поле _score. От того, сколько терминов запроса полностью совпадает с термином в перевернутом индексе, зависит оценка, но давайте отложим расчет оценки на другой день.

Когда использовать одно или другое

Используйте тип данных поля keyword, если:

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

Используйте тип данных текстового поля, если:

  • Вы хотите создать автозаполнение
  • Вы хотите создать поисковую систему

Заключение

Понимание того, как работают типы данных полей text и keyword, - одна из вещей, которую вы захотите изучить в Elasticsearch. Разница кажется простой, но будет иметь большое значение.

Вы захотите понять и выбрать тип данных поля, подходящий для вашего варианта использования, если вам нужны оба типа данных поля, вы можете использовать функцию Multi Fields при создании сопоставления.

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