Полнотекстовые запросы: match, match_phrase, multi_match
Что делает запрос «полнотекстовым»
Ключевое отличие от точных запросов — анализ ввода. Когда вы пишете match: "Беспроводные наушники", Elasticsearch прогоняет строку запроса через тот же анализатор, что применялся при индексации поля. На выходе — не строка, а набор токенов: нормализованных, приведённых к нижнему регистру, возможно стеммингованных слов. Именно эти токены ищутся в инвертированном индексе.
Это принципиально отличает match от term: term не анализирует ввод и ищет значение буквально. Подробнее о разнице — в Точные совпадения и фильтры: term, range, exists. Как устроен сам анализатор — в Анатомия анализатора: char filters, токенизатор, token filters.
Три запроса этой статьи:
match— основной инструмент полнотекстового поискаmatch_phrase— поиск с сохранением порядка словmulti_match—matchсразу по нескольким полям
match: основной запрос
Минимальная форма:
GET /articles/_search
{
"query": {
"match": {
"title": "elasticsearch поиск"
}
}
}ES анализирует строку "elasticsearch поиск", получает токены elasticsearch и поиск, ищет документы, где в поле title встречается хотя бы один из них. По умолчанию — оператор OR.
Путь от строки запроса до результатов:
flowchart LR
A[Запрос пользователя] --> B[Анализатор поля]
B --> C[Токены: elasticsearch, поиск]
C --> D{operator}
D -->|OR, по умолчанию| E[Хотя бы один токен]
D -->|AND| F[Все токены]
E --> G[Инвертированный индекс]
F --> G
G --> H[Документы + _score]Развёрнутый синтаксис, когда нужны параметры:
GET /articles/_search
{
"query": {
"match": {
"title": {
"query": "elasticsearch поиск",
"operator": "and"
}
}
}
}#### operator: AND vs OR
operator: "or" (по умолчанию) — достаточно одного совпавшего токена. Широкий охват, но и больше нерелевантного шума.
operator: "and" — все токены должны присутствовать в поле. Строже и точнее, но легко пропустить релевантный документ, в котором не хватает одного слова.
Допустим, в индексе три статьи:
| id | title |
|---|---|
| 1 | «Введение в Elasticsearch» |
| 2 | «Полнотекстовый поиск в Elasticsearch» |
| 3 | «Введение в полнотекстовый поиск» |
Запрос "elasticsearch поиск" с operator: "or" вернёт все три (в каждой есть хотя бы одно слово). С operator: "and" — только документ 2, где присутствуют оба.
#### minimum_should_match: тонкая настройка
Компромисс между or и and — параметр minimum_should_match:
GET /articles/_search
{
"query": {
"match": {
"body": {
"query": "быстрый полнотекстовый поиск документов",
"minimum_should_match": "75%"
}
}
}
}Запрос содержит 4 токена. "75%" означает: минимум 3 из 4 должны совпасть. Значение задаётся числом (2), процентом ("75%") или комбинированно ("2<75%" — если токенов меньше 3, нужны все; если 3 и больше — 75%). Процентный формат удобен, когда длина пользовательского ввода непредсказуема.
match_phrase: порядок слов имеет значение
match не обращает внимания на порядок слов: «поиск Elasticsearch» и «Elasticsearch поиск» для него равнозначны. Когда порядок критичен — используйте match_phrase:
GET /articles/_search
{
"query": {
"match_phrase": {
"body": "полнотекстовый поиск"
}
}
}Запрос найдёт только документы, где токены полнотекстовый и поиск стоят рядом и именно в таком порядке. «Поиск полнотекстовый» — уже не совпадёт.
#### slop: допуск на вставные слова
Параметр slop задаёт, сколько «шагов перестановки» допустимо между токенами:
GET /articles/_search
{
"query": {
"match_phrase": {
"body": {
"query": "elasticsearch поиск",
"slop": 1
}
}
}
}При slop: 1 между словами может стоять одно лишнее слово — «elasticsearch быстрый поиск» совпадёт. По умолчанию slop: 0: слова вплотную, без вставок.
Практическое правило: slop: 0 для точных терминов и цитат; slop: 1–2 — когда ищете устойчивые сочетания в живом тексте, где могут встречаться вставные слова.
multi_match: один запрос по нескольким полям
В реальных индексах несколько текстовых полей: title, body, tags. multi_match позволяет искать по всем сразу:
GET /articles/_search
{
"query": {
"multi_match": {
"query": "elasticsearch полнотекстовый поиск",
"fields": ["title", "body", "tags"]
}
}
}По умолчанию режим best_fields: итоговый _score берётся у поля с наибольшим совпадением. Логика такая — если запрос точно попал в заголовок, именно он определяет релевантность документа.
#### Буст отдельных полей
Добавьте ^N к имени поля, чтобы усилить его вклад в _score:
GET /articles/_search
{
"query": {
"multi_match": {
"query": "elasticsearch поиск",
"fields": ["title^3", "body", "tags^2"]
}
}
}Совпадение в title ценится втрое дороже, чем в body; в tags — вдвое. Как именно вычисляется _score — разбирается в Релевантность и BM25: как считается _score, а управление бустом — в Бустинг полей и function_score: управление ранжированием.
#### Режимы type
| type | Логика score | Когда использовать |
|---|---|---|
best_fields (по умолчанию) | Максимальный score среди полей | Поля независимы, одно важнее других |
most_fields | Сумма score всех полей | Поля взаимодополняют (например, title + title.english) |
cross_fields | Поля как единый текст, токены могут быть в разных полях | Данные разбиты по полям: first_name + last_name |
phrase | match_phrase по каждому полю | Поиск точной фразы сразу в нескольких полях |
phrase_prefix | Автодополнение фразы | Поисковые подсказки «по мере ввода» |
Пример cross_fields — поиск имени и фамилии, когда они лежат в разных полях:
GET /users/_search
{
"query": {
"multi_match": {
"query": "Иван Петров",
"fields": ["first_name", "last_name"],
"type": "cross_fields",
"operator": "and"
}
}
}С best_fields запрос провалится: ни одно поле не содержит «Иван Петров» целиком. С cross_fields ES объединяет поля в один текст и ищет иван и петров по всем им вместе.
Ловушка: match по keyword-полю
Если поле имеет тип keyword, анализатор к нему не применяется — ни при индексации, ни при match-запросе. Значение хранится как есть. Запрос match: { "status": "Active" } найдёт строку "Active", но не "active".
Для полей keyword используйте term, а не match. Проверить тип: GET /my_index/_mapping. О типах полей подробнее — в Типы полей: text, keyword, числа и даты.
См. также
- Query DSL: структура запроса и контекст query vs filter
- Точные совпадения и фильтры: term, range, exists
- Составные запросы: bool и комбинирование условий
- Релевантность и BM25: как считается _score
- Бустинг полей и function_score: управление ранжированием
- Анатомия анализатора: char filters, токенизатор, token filters
- Типы полей: text, keyword, числа и даты