Агрегации отвечают на вопросы о группах совпавших документов. Bucket-агрегация формирует группы; метрическая вычисляет показатель внутри текущей группы. Верхнеуровневый query ограничивает набор для всех обычных агрегаций. size: 0 убирает документы из выдачи, но не отключает вычисление статистики.
Имена агрегаций задаёт автор запроса: они становятся ключами ответа. Используйте имена из условия, поскольку это часть контракта. Тренажёр поддерживает aggs и aggregations, но допускает не более трёх уровней агрегаций и бюджет 1000 buckets.
- Совпадения query
- Категория A
- avg(price) внутри A
- Категория B
- avg(price) внутри B
- Категория A
Дочерняя метрика видит только документы родительского bucket. Параллельные соседи не фильтруют друг друга.
Примеры выполнены на публичном наборе products в Elasticsearch 9.4.3. Ответы — сокращённые фрагменты JSON: время, сведения о шардах и индексе опущены. Числовые оценки иллюстративны и могут меняться. Примеры объясняют способ решения; значения в заданиях могут отличаться.
Количество по брендам
terms-агрегация создаёт buckets для точных индексированных значений. Здесь группировка идёт по category вместо бренда. У каждого bucket есть key и doc_count. size внутри terms управляет числом buckets, а корневой size — числом документов. Это разные параметры с одинаковым именем.
Для групп по целому значению нужны keyword-поля: category или brand.keyword. По умолчанию terms предпочитает группы с большим счётчиком, разрешая равенство через ключ. На промышленном индексе с несколькими шардами возможны особенности точности top terms; здесь один primary shard, а size примеров достаточен для всех значений.
Разберём другой сценарий
{
"size": 0,
"aggs": {
"categories": {
"terms": {
"field": "category",
"size": 10
}
}
}
}Ответ Elasticsearch
{
"hits": {
"total": {
"value": 300,
"relation": "eq"
},
"hits": []
},
"aggregations": {
"categories": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "Accessories",
"doc_count": 75
},
{
"key": "Audio",
"doc_count": 75
},
{
"key": "Computers",
"doc_count": 75
},
{
"key": "Smartphones",
"doc_count": 75
}
]
}
}
}Как читать результат
Документы не возвращаются. categories.buckets содержит ключи и счётчики набора, а hits.total по-прежнему показывает общее число совпавших документов.
Средняя цена
avg вычисляет среднее индексированных числовых значений текущей области. Пример считает среднюю цену только для Audio благодаря верхнеуровневому query. В ответе метрики находится value, а не список товаров или счётчик группы. Без query показатель относился бы ко всему индексу.
Документы без поля метрики не добавляют значение в обычное среднее. Если числовых значений нет, value равен null, а не нулю. Десятичные результаты — числовые вычисления: несущественные различия floating-point не меняют смысл.
Разберём другой сценарий
{
"size": 0,
"query": {
"term": {
"category": "Audio"
}
},
"aggs": {
"audio_average": {
"avg": {
"field": "price"
}
}
}
}Ответ Elasticsearch
{
"hits": {
"total": {
"value": 75,
"relation": "eq"
},
"hits": []
},
"aggregations": {
"audio_average": {
"value": 859.4133333333333
}
}
}Как читать результат
audio_average.value обобщает все совпавшие товары Audio, а не первую страницу. Имя метрики выбрано для этого примера.
Границы цен
min и max — независимые метрические агрегации, поэтому располагаются соседями в aggs. Пример ограничивает набор Accessories и запрашивает обе границы цены. Метрика наследует текущую область: сосед не меняет набор документов другой метрики.
Это наблюдаемые значения, а не границы range-фильтра. Если query допускает цену ниже 800, максимальная фактическая цена может быть меньше 800. На пустом наборе метрики равны null: не заменяйте отсутствие значения выдуманным нулём.
Разберём другой сценарий
{
"size": 0,
"query": {
"term": {
"category": "Accessories"
}
},
"aggs": {
"lowest": {
"min": {
"field": "price"
}
},
"highest": {
"max": {
"field": "price"
}
}
}
}Ответ Elasticsearch
{
"hits": {
"total": {
"value": 75,
"relation": "eq"
},
"hits": []
},
"aggregations": {
"lowest": {
"value": 99
},
"highest": {
"value": 2499
}
}
}Как читать результат
Оба value описывают один набор Accessories. Их можно получить без запроса и сортировки отдельных документов.
Агрегация товаров в наличии
filter-агрегация создаёт один bucket документов, проходящих вложенный запрос. Ограничение наследуют только её дочерние агрегации. В примере hits.total по-прежнему учитывает все товары, но out_of_stock содержит недоступные и его среднее описывает только их.
Верхнеуровневый query ограничил бы все агрегации и поисковый результат. Выбирайте область по бизнес-вопросу: локальное сравнение требует filter-bucket, общее ограничение для всего — query. filter-bucket содержит doc_count, помогающий проверить набор метрики.
Разберём другой сценарий
{
"size": 0,
"aggs": {
"out_of_stock": {
"filter": {
"term": {
"in_stock": false
}
},
"aggs": {
"average_price": {
"avg": {
"field": "price"
}
}
}
}
}
}Ответ Elasticsearch
{
"hits": {
"total": {
"value": 300,
"relation": "eq"
},
"hits": []
},
"aggregations": {
"out_of_stock": {
"doc_count": 100,
"average_price": {
"value": 724.3
}
}
}
}Как читать результат
Сравните hits.total.value и out_of_stock.doc_count. Они описывают разные наборы, потому что ограничение действует локально внутри агрегации.
Метрики по брендам
Bucket может содержать дочерние aggs. Сгруппируйте категории и вычислите average_price для каждой. Получатся отдельные средние, а не один глобальный показатель рядом со всеми группами. Ответ повторяет дерево запроса: читайте его от родительского ключа к потомкам.
Вложенность агрегаций не равна nested query по полю с mapping nested. Товары содержат обычные скалярные поля: для дерева категория → метрика nested-mapping не нужен. Соблюдайте лимиты и оценивайте умножение числа buckets перед добавлением новой группировки.
Разберём другой сценарий
{
"size": 0,
"aggs": {
"categories": {
"terms": {
"field": "category",
"size": 10
},
"aggs": {
"average_price": {
"avg": {
"field": "price"
}
}
}
}
}
}Ответ Elasticsearch
{
"hits": {
"total": {
"value": 300,
"relation": "eq"
},
"hits": []
},
"aggregations": {
"categories": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "Accessories",
"doc_count": 75,
"average_price": {
"value": 859.4
}
},
{
"key": "Audio",
"doc_count": 75,
"average_price": {
"value": 859.4133333333333
}
},
{
"key": "Computers",
"doc_count": 75,
"average_price": {
"value": 589.2
}
},
{
"key": "Smartphones",
"doc_count": 75,
"average_price": {
"value": 589.88
}
}
]
}
}
}Как читать результат
Каждый bucket категории содержит собственный average_price.value. Положение метрики внутри bucket указывает, какой набор она описывает.
Краткая памятка
- Корневой size управляет документами; terms.size — группами.
- Для групп по значениям используйте keyword, для числовых метрик — числа.
- query ограничивает все обычные агрегации; filter-агрегация — своих потомков.
- Метрика должна быть внутри bucket, документы которого описывает.
- Пустая числовая метрика равна null, а не нулю.
Официальная документация
Готовы применить знания?
Подтвердите прочтение, чтобы сохранить своё место. Чтение и решение заданий учитываются отдельно в этом браузере с помощью гостевой cookie.