match против term: анализируемые и точные поля

Разбираем главную развилку поиска в Elasticsearch — когда брать match, а когда term, и почему путаница тут источник большинства ошибок.

match анализирует запрос (разбивает на термы, приводит к нижнему регистру) и ищет в анализируемом поле; term ищет точное, неизменённое значение.

Две природы полей

Вспомним маппинг: поле text при индексации проходит через анализатор и хранится как набор термов; поле keyword хранится как есть, целиком. Запросы должны соответствовать этой природе: для текста — анализируемый match, для точных значений — буквальный term.

match — для полнотекстового поиска

Запрос match прогоняет вашу строку через тот же анализатор, что и поле. Запрос «Кофемашины DeLonghi» превратится в термы кофемашин, delonghi и найдёт документы с этими словами в любом регистре и форме.

{
  "query": {
    "match": { "title": "Кофемашины DeLonghi" }
  }
}

По умолчанию слова объединяются по OR: подойдут документы хотя бы с одним термом. Их можно объединить по AND:

{
  "query": {
    "match": {
      "title": { "query": "кофемашина delonghi", "operator": "and" }
    }
  }
}

term — для точных значений

Запрос term берёт значение буквально, без анализа, и ищет точное совпадение терма. Идеален для keyword-полей: статус, тег, id, email.

{
  "query": {
    "term": { "status": "published" }
  }
}

Классическая ловушка

Если применить term к полю text, поиск часто ничего не находит. Почему: в индексе поле text хранится как термы после анализа (например, «Кофемашина» стала кофемашина в нижнем регистре). А term сравнивает с тем, что вы написали, буквально — «Кофемашина» с большой буквы не совпадёт с термом кофемашина. Поэтому term по text — почти всегда ошибка.

ЗапросАнализирует ввод?Для каких полей
matchдаtext
termнетkeyword, числа, даты, boolean

Как работает под капотом

При match ES берёт строку запроса, пропускает через анализатор поля, получает список термов и для каждого делает лукап в инвертированном индексе, затем объединяет постинг-листы (по OR или AND). При term анализа нет: строка сразу используется как терм для лукапа. Вот почему регистр и форма слова критичны для term и неважны для match.

Частые ошибки

  • term по text-полю. Самая частая причина «поиск ничего не находит». Лечится переходом на match или на keyword-поле.
  • match по keyword с расчётом на анализ. Поле keyword не анализируется при индексации, поэтому match по нему по сути сведётся к точному сравнению — пользы от анализатора не будет.

Итоги

  • match анализирует запрос и предназначен для полнотекстового поиска по text.
  • term ищет точное значение без анализа — для keyword, чисел, дат, boolean.
  • term по полю text почти всегда даёт пустой результат из-за расхождения регистра и форм.
Проверьте себя
1. В чём ключевое различие match и term?
Amatch быстрее term
Bmatch анализирует запрос (разбивает на термы, нижний регистр), term ищет точное неизменённое значение
Cterm работает только с числами
DМежду ними нет разницы
2. Почему term по полю text часто ничего не находит?
Atext-поля нельзя искать
BВ индексе text хранится как термы после анализа (нижний регистр и т.п.), а term сравнивает буквально с введённым значением
Cterm требует числовое поле
Dterm всегда возвращает пустой результат