Как читать ответ _search
Разбираем JSON-ответ, который Elasticsearch возвращает на запрос _search, поле за полем.
Ответ _search — это JSON, где найденные документы лежат в
hits.hits, отсортированные по релевантности_score.
Зачем разбираться в ответе
Чтобы строить поиск, нужно уметь читать, что вернул ES: сколько всего нашлось, какие документы попали в выдачу, насколько они релевантны и почему. Без этого вы не сможете ни отладить запрос, ни показать результаты пользователю.
Структура ответа
{
"took": 5,
"timed_out": false,
"hits": {
"total": { "value": 42, "relation": "eq" },
"max_score": 3.21,
"hits": [
{
"_index": "products",
"_id": "1",
"_score": 3.21,
"_source": { "title": "Кофемашина", "price": 24990 }
}
]
}
}Поля верхнего уровня
| Поле | Смысл |
took | сколько миллисекунд занял запрос |
timed_out | уложился ли в таймаут |
hits.total.value | сколько всего документов подошло |
hits.max_score | максимальная релевантность в выдаче |
hits.hits | массив самих найденных документов |
Внутри каждого hit
_index,_id— где лежит документ и его идентификатор;_score— оценка релевантности; чем больше, тем выше документ в выдаче;_source— исходный JSON документа (то, что вы проиндексировали).
total.relation — важная тонкость
Поле total содержит не только value, но и relation. По умолчанию ES считает точное число попаданий лишь до 10000, дальше ставит relation: "gte" («не меньше чем»). То есть {"value": 10000, "relation": "gte"} значит «нашлось 10000 или больше». Это сделано ради скорости — точный подсчёт миллионов попаданий дорог. Если нужна точная цифра, есть параметр track_total_hits: true.
Как работает под капотом
Координирующий узел собирает топ результатов с каждого шарда, объединяет их, пересортировывает по _score и берёт нужное окно (from/size). По умолчанию возвращаются первые 10 документов. _score вычисляется по формуле релевантности (BM25) — об этом следующий раздел. Поэтому один и тот же набор документов на разные запросы получает разный _score: оценка зависит от запроса, а не от документа самого по себе.
Частые ошибки
- Принимать total.value за точное число. Без
track_total_hitsоно может быть «10000+», смотрите наrelation. - Искать данные документа не там. Содержимое документа — в
hits.hits[i]._source, а не в корне ответа. - Ждать все результаты сразу. Возвращаются только первые
size(по умолчанию 10); остальное — через пагинацию.
Итоги
- Найденные документы — в
hits.hits, их содержимое — в_source, релевантность — в_score. total.valueсrelation: "gte"означает «не меньше», точное число — черезtrack_total_hits.- По умолчанию возвращаются 10 самых релевантных документов.