Различия между ними и как они себя ведут
Многие люди, которые только начали изучать 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. Спасибо за прочтение!