Агрегация Composite
Агрегация Composite ресурсоёмкая. Проведите тестирование производительности вашего приложения перед внедрением агрегации Composite в производство.
Многоуровневая агрегация, которая создаёт составные группы из разных источников.
В отличие от других multi-bucket агрегаций, вы можете использовать агрегацию composite для эффективной постраничной навигации по всем группам многоуровневой агрегации. Эта агрегация позволяет потоково получить все группы определённой агрегации, подобно тому, как свойство скролла делает это для документов.
Составные группы формируются из комбинаций значений, извлечённых/созданных для каждого документа, и каждая комбинация рассматривается как составная группа.
Например, рассмотрим следующие документы:
{
"keyword": ["foo", "bar"],
"number": [23, 65, 76]
} Используя keyword и number в качестве полей-источников для результатов агрегации, получаем следующие составные группы:
{ "keyword": "foo", "number": 23 }
{ "keyword": "foo", "number": 65 }
{ "keyword": "foo", "number": 76 }
{ "keyword": "bar", "number": 23 }
{ "keyword": "bar", "number": 65 }
{ "keyword": "bar", "number": 76 } Источники значений
Параметр sources определяет поля-источники, которые используются при создании составных групп. Порядок определения sources определяет порядок возвращаемых ключей.
Вы должны использовать уникальное имя при определении sources.
Параметр sources может быть любого из следующих типов:
Термы
Источник значений terms похож на простую агрегацию terms. Значения извлекаются из поля точно так же, как и в агрегации terms.
Пример:
GET /_search
{
"size": 0,
"aggs": {
"my_buckets": {
"composite": {
"sources": [
{ "product": { "terms": { "field": "product" } } }
]
}
}
}
} Как и в агрегации terms, можно использовать вычисляемое поле для создания значений составных групп:
GET /_search
{
"runtime_mappings": {
"day_of_week": {
"type": "keyword",
"script": """
emit(doc['timestamp'].value.dayOfWeekEnum
.getDisplayName(TextStyle.FULL, Locale.ENGLISH))
"""
}
},
"size": 0,
"aggs": {
"my_buckets": {
"composite": {
"sources": [
{
"dow": {
"terms": { "field": "day_of_week" }
}
}
]
}
}
}
} Несмотря на сходство, источник значений terms не поддерживает тот же набор параметров, что и агрегация terms. Для других поддерживаемых параметров источников значений см.:
Гистограмма
Источник значений histogram может применяться к числовым значениям для построения интервалов фиксированного размера над этими значениями. Параметр interval определяет, как следует преобразовывать числовые значения. Например, значение interval, равное 5, преобразует любые числовые значения к ближайшему интервалу. Значение 101 было бы преобразовано в 100, что является ключом для интервала между 100 и 105.
Пример:
GET /_search
{
"size": 0,
"aggs": {
"my_buckets": {
"composite": {
"sources": [
{ "histo": { "histogram": { "field": "price", "interval": 5 } } }
]
}
}
}
} Как и в агрегации histogram, можно использовать вычисляемое поле для создания значений составных групп:
GET /_search
{
"runtime_mappings": {
"price.discounted": {
"type": "double",
"script": """
double price = doc['price'].value;
if (doc['product'].value == 'mad max') {
price *= 0.8;
}
emit(price);
"""
}
},
"size": 0,
"aggs": {
"my_buckets": {
"composite": {
"sources": [
{
"price": {
"histogram": {
"interval": 5,
"field": "price.discounted"
}
}
}
]
}
}
}
} Гистограмма по дате
Источник значений date_histogram аналогичен источнику значений histogram, за исключением того, что интервал задаётся выражением даты/времени:
GET /_search
{
"size": 0,
"aggs": {
"my_buckets": {
"composite": {
"sources": [
{ "date": { "date_histogram": { "field": "timestamp", "calendar_interval": "1d" } } }
]
}
}
}
} В приведённом примере создаётся интервал в день, и все timestamp значения преобразуются в начало ближайшего интервала. Доступные выражения для интервала: year, quarter, month, week, day, hour, minute, second
Значения времени также могут быть указаны с помощью сокращений, поддерживаемых единицами измерения времени. Обратите внимание, что дробные значения времени не поддерживаются, но вы можете это исправить, перейдя к другой единице измерения времени (например, 1.5h можно вместо этого указать как 90m).
Формат
Внутренне дата представлена 64-битным числом, представляющим временную метку в миллисекундах с начала эпохи. Эти временные метки возвращаются в качестве ключей групп. Можно вернуть строку отформатированной даты вместо этого, используя формат, указанный параметром формата:
GET /_search
{
"size": 0,
"aggs": {
"my_buckets": {
"composite": {
"sources": [
{
"date": {
"date_histogram": {
"field": "timestamp",
"calendar_interval": "1d",
"format": "yyyy-MM-dd"
}
}
}
]
}
}
}
} | Поддерживает выразительные форматы дат форматов даты |
Временная зона
Даты и время хранятся в Elasticsearch в формате UTC. По умолчанию все группировки и округления также выполняются в UTC. Параметр time_zone можно использовать для указания того, что группировка должна использовать другую временную зону.
Временные зоны могут быть указаны либо как смещение UTC в формате ISO 8601 (например, +01:00 или -08:00), либо как идентификатор часового пояса, используемый в базе данных TZ, например, America/Los_Angeles.
Смещение
Используйте параметр offset для изменения начального значения каждой группы на заданную положительную (+) или отрицательную (-) продолжительность, например 1h для часа или 1d для дня. См. Единицы измерения времени для других возможных вариантов продолжительности времени.
Например, при использовании интервала day каждая группа работает с полуночи до полуночи. Установка параметра offset в +6h изменяет каждую группу так, что она работает с 6:00 до 6:00:
PUT my-index-000001/_doc/1?refresh
{
"date": "2015-10-01T05:30:00Z"
}
PUT my-index-000001/_doc/2?refresh
{
"date": "2015-10-01T06:30:00Z"
}
GET my-index-000001/_search?size=0
{
"aggs": {
"my_buckets": {
"composite" : {
"sources" : [
{
"date": {
"date_histogram" : {
"field": "date",
"calendar_interval": "day",
"offset": "+6h",
"format": "iso8601"
}
}
}
]
}
}
}
} Вместо одной группы, начинающейся в полночь, вышеупомянутый запрос группирует документы в группы, начинающиеся в 6:00:
{
...
"aggregations": {
"my_buckets": {
"after_key": { "date": "2015-10-01T06:00:00.000Z" },
"buckets": [
{
"key": { "date": "2015-09-30T06:00:00.000Z" },
"doc_count": 1
},
{
"key": { "date": "2015-10-01T06:00:00.000Z" },
"doc_count": 1
}
]
}
}
} Начальное значение offset каждой группы вычисляется после того, как были произведены корректировки time_zone.
Гео-плитка
Источник значений geotile_grid работает с полями geo_point и группирует точки в группы, которые представляют ячейки сетки. Полученная сетка может быть разреженной и содержать только ячейки с соответствующими данными. Каждая ячейка соответствует плитке карты, как используется во многих онлайн-картах. Каждая ячейка имеет метку в формате "{zoom}/{x}/{y}", где zoom равен заданной пользователем точности.
GET /_search
{
"size": 0,
"aggs": {
"my_buckets": {
"composite": {
"sources": [
{ "tile": { "geotile_grid": { "field": "location", "precision": 8 } } }
]
}
}
}
} Точность
Самая высокая точность гео-плитки длиной 29 создаёт ячейки, которые охватывают менее 10 см на 10 см суши. Эта точность идеально подходит для агрегаций Composite, так как не требуется генерировать и загружать в память каждую плитку.
См. документацию по уровням масштаба, чтобы узнать, как точность (zoom) коррелирует с размером на местности. Точность для этой агрегации может быть от 0 до 29 включительно.
Фильтрация по прямоугольнику
Источник гео-плитки можно дополнительно ограничить определённым географическим прямоугольником, что сокращает диапазон используемых плиток. Эти границы полезны, когда требуется высокая точность разбиения только определённой части географической области.
GET /_search
{
"size": 0,
"aggs": {
"my_buckets": {
"composite": {
"sources": [
{
"tile": {
"geotile_grid": {
"field": "location",
"precision": 22,
"bounds": {
"top_left": "52.4, 4.9",
"bottom_right": "52.3, 5.0"
}
}
}
}
]
}
}
}
} Смешивание различных источников значений
Параметр sources принимает массив источников значений. Можно смешивать различные источники значений для создания составных групп. Например:
GET /_search
{
"size": 0,
"aggs": {
"my_buckets": {
"composite": {
"sources": [
{ "date": { "date_histogram": { "field": "timestamp", "calendar_interval": "1d" } } },
{ "product": { "terms": { "field": "product" } } }
]
}
}
}
} Это создаст составные группы из значений, созданных двумя источниками значений, date_histogram и terms. Каждая группа состоит из двух значений, по одному от каждого определённого в агрегации источника значений. Разрешены любые типы комбинаций, и порядок в массиве сохраняется в составных группах.
GET /_search
{
"size": 0,
"aggs": {
"my_buckets": {
"composite": {
"sources": [
{ "shop": { "terms": { "field": "shop" } } },
{ "product": { "terms": { "field": "product" } } },
{ "date": { "date_histogram": { "field": "timestamp", "calendar_interval": "1d" } } }
]
}
}
}
} Порядок
По умолчанию составные корзины сортируются по их естественному порядку. Значения сортируются в порядке возрастания. Когда запрашиваются несколько источников значений, сортировка выполняется по каждому источнику значений. Первое значение составной корзины сравнивается с первым значением другой составной корзины, и если они равны, для определения порядка используются следующие значения в составной корзине. Это означает, что составная корзина [foo, 100] считается меньше, чем [foobar, 0], потому что foo считается меньше, чем foobar. Можно определить направление сортировки для каждого источника значений, установив order в asc (значение по умолчанию) или desc (по убыванию) непосредственно в определении источника значений. Например:
GET /_search
{
"size": 0,
"aggs": {
"my_buckets": {
"composite": {
"sources": [
{ "date": { "date_histogram": { "field": "timestamp", "calendar_interval": "1d", "order": "desc" } } },
{ "product": { "terms": { "field": "product", "order": "asc" } } }
]
}
}
}
} … будет сортировать составную корзину по убыванию при сравнении значений из источника date_histogram и по возрастанию при сравнении значений из источника terms.
Отсутствующая корзина
По умолчанию документы без значения для данного источника игнорируются. Их можно включить в ответ, установив missing_bucket в true (по умолчанию false):
GET /_search
{
"size": 0,
"aggs": {
"my_buckets": {
"composite": {
"sources": [{
"product_name": {
"terms": {
"field": "product",
"missing_bucket": true,
"missing_order": "last"
}
}
}]
}
}
}
} В приведенном выше примере источник product_name создаёт явную корзину null для документов без значения product. Эта корзина размещается в конце.
Вы можете контролировать позицию корзины null, используя необязательный параметр missing_order. Если missing_order равно first или last, корзина null помещается соответственно на первое или последнее место. Если missing_order опущено или равно default, положение корзины определяется порядком сортировки источника order. Если порядок сортировки order равен asc (возрастающий), корзина находится на первом месте. Если порядок сортировки order равен desc (убывающий), корзина находится на последнем месте.
Размер
Параметр size можно установить для определения количества составных корзин, которые должны быть возвращены. Каждая составная корзина рассматривается как отдельная корзина, поэтому установка размера в 10 вернёт первые 10 составных корзин, созданных из источников значений. Ответ содержит значения для каждой составной корзины в массиве, содержащем значения, извлечённые из каждого источника значений. Значение по умолчанию — 10.
Пагинация
Если количество составных корзин слишком велико (или неизвестно), чтобы быть возвращённым в одном ответе, можно разделить получение по нескольким запросам. Поскольку составные корзины по своей природе плоские, запрашиваемое количество size точно соответствует количеству составных корзин, которые будут возвращены в ответе (при условии, что для возврата доступно как минимум size составных корзин). Если необходимо получить все составные корзины, предпочтительнее использовать небольшой размер (например, 100 или 1000) и затем использовать параметр after для получения следующих результатов. Например:
GET /_search
{
"size": 0,
"aggs": {
"my_buckets": {
"composite": {
"size": 2,
"sources": [
{ "date": { "date_histogram": { "field": "timestamp", "calendar_interval": "1d" } } },
{ "product": { "terms": { "field": "product" } } }
]
}
}
}
} … возвращает:
{
...
"aggregations": {
"my_buckets": {
"after_key": {
"date": 1494288000000,
"product": "mad max"
},
"buckets": [
{
"key": {
"date": 1494201600000,
"product": "rocky"
},
"doc_count": 1
},
{
"key": {
"date": 1494288000000,
"product": "mad max"
},
"doc_count": 2
}
]
}
}
} Чтобы получить следующий набор корзин, необходимо повторить тот же агрегирование с параметром after, установленным на значение after_key, возвращённое в ответе. Например, этот запрос использует значение after_key, предоставленное в предыдущем ответе:
GET /_search
{
"size": 0,
"aggs": {
"my_buckets": {
"composite": {
"size": 2,
"sources": [
{ "date": { "date_histogram": { "field": "timestamp", "calendar_interval": "1d", "order": "desc" } } },
{ "product": { "terms": { "field": "product", "order": "asc" } } }
],
"after": { "date": 1494288000000, "product": "mad max" }
}
}
}
} | Должно ограничить агрегирование корзинами, которые сортируются после предоставленных значений. |
after_key обычно является ключом последней возвращённой корзины, но это не гарантируется. Всегда используйте возвращённое значение after_key вместо его вычисления из корзин.
Преждевременное завершение
Для оптимальной производительности сортировка индекса должна быть настроена на индексе таким образом, чтобы она соответствовала частям или полностью порядку источника в составной агрегации. Например, следующая сортировка индекса:
PUT my-index-000001
{
"settings": {
"index": {
"sort.field": [ "username", "timestamp" ],
"sort.order": [ "asc", "desc" ]
}
},
"mappings": {
"properties": {
"username": {
"type": "keyword",
"doc_values": true
},
"timestamp": {
"type": "date"
}
}
}
} | Этот индекс отсортирован сначала по полю | |
| … в порядке возрастания для поля
|
GET /_search
{
"size": 0,
"aggs": {
"my_buckets": {
"composite": {
"sources": [
{ "user_name": { "terms": { "field": "user_name" } } }
]
}
}
}
} |
|
GET /_search
{
"size": 0,
"aggs": {
"my_buckets": {
"composite": {
"sources": [
{ "user_name": { "terms": { "field": "user_name" } } },
{ "date": { "date_histogram": { "field": "timestamp", "calendar_interval": "1d", "order": "desc" } } }
]
}
}
}
} |
| |
|
|
Для оптимизации преждевременного завершения рекомендуется установить track_total_hits в запросе на false. Количество общих совпадений с запросом можно получить при первом запросе, и вычисление этого числа при каждом запросе будет дорогостоящим:
GET /_search
{
"size": 0,
"track_total_hits": false,
"aggs": {
"my_buckets": {
"composite": {
"sources": [
{ "user_name": { "terms": { "field": "user_name" } } },
{ "date": { "date_histogram": { "field": "timestamp", "calendar_interval": "1d", "order": "desc" } } }
]
}
}
}
} Обратите внимание, что порядок источника важен. В приведенном ниже примере переключение user_name с timestamp деактивирует оптимизацию сортировки, так как эта настройка не будет соответствовать спецификации сортировки индекса. Если порядок источников не имеет значения для вашего случая, вы можете следовать этим простым правилам:
- Поместите поля с наибольшей кратностью в начале.
- Убедитесь, что порядок поля соответствует порядку сортировки индекса.
- Поместите многозначные поля в конец, так как они не могут использоваться для преждевременного завершения.
Сортировка индекса может замедлить индексацию. Очень важно протестировать сортировку индекса со своим конкретным случаем использования и набором данных, чтобы убедиться, что она соответствует вашим требованиям. Если это не так, учтите, что агрегации composite также попытаются преждевременно завершить работу на несортированных индексах, если запрос соответствует всем документам (запрос match_all).
Вложенные агрегации
Как и любая multi-bucket агрегация, агрегация composite может содержать вложенные агрегации. Эти вложенные агрегации могут использоваться для вычисления других корзин или статистических данных для каждой составной корзины, созданной этой родительской агрегацией. Например, следующий пример вычисляет среднее значение поля для каждой составной корзины:
GET /_search
{
"size": 0,
"aggs": {
"my_buckets": {
"composite": {
"sources": [
{ "date": { "date_histogram": { "field": "timestamp", "calendar_interval": "1d", "order": "desc" } } },
{ "product": { "terms": { "field": "product" } } }
]
},
"aggregations": {
"the_avg": {
"avg": { "field": "price" }
}
}
}
}
} … возвращает:
{
...
"aggregations": {
"my_buckets": {
"after_key": {
"date": 1494201600000,
"product": "rocky"
},
"buckets": [
{
"key": {
"date": 1494460800000,
"product": "apocalypse now"
},
"doc_count": 1,
"the_avg": {
"value": 10.0
}
},
{
"key": {
"date": 1494374400000,
"product": "mad max"
},
"doc_count": 1,
"the_avg": {
"value": 27.0
}
},
{
"key": {
"date": 1494288000000,
"product": "mad max"
},
"doc_count": 2,
"the_avg": {
"value": 22.5
}
},
{
"key": {
"date": 1494201600000,
"product": "rocky"
},
"doc_count": 1,
"the_avg": {
"value": 10.0
}
}
]
}
}
} Агрегации конвейера
В настоящее время агрегация composite несовместима с агрегациями конвейера, и в большинстве случаев это не имеет смысла. Например, из-за пагинации composite агрегаций, один логический фрагмент (например, один день) может быть распределён по нескольким страницам. Поскольку агрегации конвейера являются чисто пост-обработкой конечного списка корзин, выполнение операции, подобной производной, на странице composite может привести к неточным результатам, поскольку она учитывает только «частичный» результат на этой странице.
Возможно, в будущем будут поддерживаться агрегации конвейера, которые содержатся внутри одной корзины (например, bucket_selector).
© 2023-2025 Elasticsearch
As of September 2024, Elasticsearch is available under a choice of three licenses: the Server Side Public License (SSPL), the Elastic License, or the AGPLv3 (OSI approved).
Elasticsearch and the Elasticsearch logo are trademarks of Elasticsearch B.V., registered in the U.S. and in other countries.
https://www.elastic.co/guide/en/elasticsearch/reference/7.17/search-aggregations-bucket-composite-aggregation.html