Начиная с Elasticsearch 7.14 появилась новая функция match_only_text, которая позволяет сэкономить до 10 % дискового пространства на наборах данных для логирования.
При определении сопоставлений можно принять тривиальное решение о том, какое поле задать - "keyword" или "еуче", в зависимости от того, как мы будем его запрашивать.
Поля keyword
Мы используем поля keyword, когда хотим искать точные совпадения и когда хотим отфильтровать документы, например, показать пользователю поле выбора с вариантами (например, status = "done"). Это также подходит для таких операций, как агрегирование или сортировка, когда мы уже заранее знаем точные значения.
Преимущество таких полей в том, что они быстро ищутся и занимают минимум места в памяти.
Поле text
С другой стороны, текстовые поля позволяют выполнять полнотекстовые запросы. К ним относятся: неточные совпадения, поиск части слов в поле, содержащем предложение, поиск без учета регистра, нечеткий поиск. Попадания в текстовые поля могут иметь разные оценки в зависимости от того, насколько релевантен документ для искомого термина поискового запроса. Поскольку поля ключевых слов используются в основном для структурированного поиска (т. е. совпадение должно быть точным, как при поиске по принципу "да/нет"), все результаты одинаково релевантны. При полнотекстовом запросе каждый результат оценивается в зависимости от того, насколько он релевантен запросу.
Недостатком этого типа полей является то, что поиск выполняется медленнее и используется больше дискового пространства, чем при использовании полей по ключевым словам.
match_only_text
До сих пор не существовало промежуточного звена между полями с ключевыми словами и текстовыми полями. Например, раньше не было решения для выполнения полнотекстовых запросов по полю, но без генерации баллов релевантности для результатов. Если бы мы планировали сортировать результаты по другому критерию, а не по баллу, нам все равно пришлось бы тратить дополнительное время и дисковое пространство, которое потребовалось бы для оценки релевантности.
Самый распространенный пример - сообщения журнала. Если мы хотим найти все сообщения журнала ошибок со словами "null pointer" (нулевой или указатель), а затем отсортировать их по дате журнала, нам не нужен скоринг (какие журналы "лучше подходят").
Именно в этом случае мы могли бы использовать новый тип поля match_only_text.
Тип поля match_only_text не будет сохранять данные, связанные с частотой терминов и их позицией на диске, которые необходимы для определения релевантности каждого документа в наборе результатов. Вместо этого будет задан плоский балл.
Рассмотрим другой пример:
У нас есть система электронной коммерции, и мы проиндексировали тысячи оценок товаров в этом формате:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | { "product_id": 1, "stars": 1, "message": "customer service is bad, I will not order again", "date": "2021-08-10" } { "product_id": 1, "stars": 5, "message": "nice product, great customer service", "date": "2021-08-13" } |
Мы хотим узнать эволюцию удовлетворенности обслуживанием клиентов, а затем сгенерировать красивую линейную диаграмму Kibana, отражающую эту тенденцию.
Ключевым словом является "эволюция" - это означает, что мы сортируем наши результаты по дате. У оценок нет поля "категория", поэтому единственный способ получить только оценки, связанные с обслуживанием клиентов, - это выполнить полнотекстовый поиск, но, как мы узнали, нам не нужна функция релевантности. Поэтому мы проиндексируем наше поле сообщения как поле типа match_only_text.
Сначала мы установим мапинг:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | PUT match-only-text-test { "mappings": { "properties": { "@timestamp": { "type": "date" }, "product_id": { "type": "long" }, "stars": { "type": "byte" }, "message": { "type": "match_only_text" }, "date": { "type": "keyword" } } } } |
Обратите внимание:
- Поле stars задано как байт, это самое маленькое числовое поле.
- Поле даты установлено как ключевое слово, потому что мы используем его для отображения. Для сортировки мы используем @timestamp, это поле автоматически генерируется, если вы импортируете данные с помощью Kibana CSV Import.
Теперь наш индекс оптимизирован. Мы можем выполнять полнотекстовые запросы, используя минимум места.
Давайте запросим наши данные:
1 2 3 4 5 6 7 8 | GET match-only-text-test/_search { "query": { "match": { "message": "customer service" } } } |
Запросы, которым нужна позиция, например match_phrase, поддерживаются match_only_text, но при этом данные о позиции будут генерироваться на лету, подобно runtime_fields, что приводит к более медленным результатам, чем обычное текстовое поле, в обмен на пространство для производительности.
Давайте рассмотрим пример запроса:
1 2 3 4 5 6 7 8 | GET match-only-text-test/_search { "query": { "match_phrase": { "message": "customer service" } } } |
Данные о позициях нужны, чтобы возвращать только документы с "customer service" без каких-либо слов между ними.
Заключение
С помощью функции match_only_text вы можете сэкономить до 10 % дискового пространства, просто изменив тип поля в мапингах. Только убедитесь, что вам не важна оценка документа, или, другими словами, порядок релевантности между возвращенными документами.
При использовании типа поля match_text_only учитывайте следующие ограничения:
- Стандартный анализатор установлен по умолчанию и не может быть изменен.
- Не поддерживаются пространственные запросы. Вместо них можно использовать интервальные запросы.
Если вы заботитесь только о совпадении текста в неструктурированном поле и в дальнейшем будете сортировать данные по различным параметрам, то использование match_only_text, скорее всего, будет лучшим вариантом.