elasticsearch/docs/reference/quickstart/aggs-tutorial.asciidoc
Liam Thompson 600ea9681e
[DOCS][101] Aggregations quickstart tutorial (#116251) (#118469)
(cherry picked from commit 56e1ca52ea)

# Conflicts:
#	docs/reference/quickstart/index.asciidoc
2024-12-12 02:46:33 +11:00

2184 lines
No EOL
54 KiB
Text

[[aggregations-tutorial]]
== Analyze eCommerce data with aggregations using Query DSL
++++
<titleabbrev>Basics: Analyze eCommerce data with aggregations</titleabbrev>
++++
This hands-on tutorial shows you how to analyze eCommerce data using {es} <<search-aggregations,aggregations>> with the `_search` API and Query DSL.
You'll learn how to:
* Calculate key business metrics such as average order value
* Analyze sales patterns over time
* Compare performance across product categories
* Track moving averages and cumulative totals
[discrete]
[[aggregations-tutorial-requirements]]
=== Requirements
You'll need:
. A running instance of <<elasticsearch-intro-deploy,{es}>>, either on {serverless-full} or together with {kib} on Elastic Cloud Hosted/Self Managed deployments.
** If you don't have a deployment, you can run the following command in your terminal to set up a <<run-elasticsearch-locally,local dev environment>>:
+
[source,sh]
----
curl -fsSL https://elastic.co/start-local | sh
----
// NOTCONSOLE
. The {kibana-ref}/get-started.html#gs-get-data-into-kibana[sample eCommerce data] loaded into {es}. To load sample data follow these steps in your UI:
* Open the *Integrations* pages by searching in the global search field.
* Search for `sample data` in the **Integrations** search field.
* Open the *Sample data* page.
* Select the *Other sample data sets* collapsible.
* Add the *Sample eCommerce orders* data set.
This will create and populate an index called `kibana_sample_data_ecommerce`.
[discrete]
[[aggregations-tutorial-inspect-data]]
=== Inspect index structure
Before we start analyzing the data, let's examine the structure of the documents in our sample eCommerce index. Run this command to see the field <<elasticsearch-intro-documents-fields-mappings,mappings>>:
[source,console]
----
GET kibana_sample_data_ecommerce/_mapping
----
// TEST[skip:Using Kibana sample data]
The response shows the field mappings for the `kibana_sample_data_ecommerce` index.
.Example response
[%collapsible]
==============
[source,console-response]
----
{
"kibana_sample_data_ecommerce": {
"mappings": {
"properties": {
"category": {
"type": "text",
"fields": { <1>
"keyword": {
"type": "keyword"
}
}
},
"currency": {
"type": "keyword"
},
"customer_birth_date": {
"type": "date"
},
"customer_first_name": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"customer_full_name": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"customer_gender": {
"type": "keyword"
},
"customer_id": {
"type": "keyword"
},
"customer_last_name": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"customer_phone": {
"type": "keyword"
},
"day_of_week": {
"type": "keyword"
},
"day_of_week_i": {
"type": "integer"
},
"email": {
"type": "keyword"
},
"event": {
"properties": {
"dataset": {
"type": "keyword"
}
}
},
"geoip": {
"properties": { <2>
"city_name": {
"type": "keyword"
},
"continent_name": {
"type": "keyword"
},
"country_iso_code": {
"type": "keyword"
},
"location": {
"type": "geo_point" <3>
},
"region_name": {
"type": "keyword"
}
}
},
"manufacturer": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword"
}
}
},
"order_date": {
"type": "date"
},
"order_id": {
"type": "keyword"
},
"products": {
"properties": { <4>
"_id": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"base_price": {
"type": "half_float"
},
"base_unit_price": {
"type": "half_float"
},
"category": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword"
}
}
},
"created_on": {
"type": "date"
},
"discount_amount": {
"type": "half_float"
},
"discount_percentage": {
"type": "half_float"
},
"manufacturer": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword"
}
}
},
"min_price": {
"type": "half_float"
},
"price": {
"type": "half_float"
},
"product_id": {
"type": "long"
},
"product_name": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword"
}
},
"analyzer": "english"
},
"quantity": {
"type": "integer"
},
"sku": {
"type": "keyword"
},
"tax_amount": {
"type": "half_float"
},
"taxful_price": {
"type": "half_float"
},
"taxless_price": {
"type": "half_float"
},
"unit_discount_amount": {
"type": "half_float"
}
}
},
"sku": {
"type": "keyword"
},
"taxful_total_price": {
"type": "half_float"
},
"taxless_total_price": {
"type": "half_float"
},
"total_quantity": {
"type": "integer"
},
"total_unique_products": {
"type": "integer"
},
"type": {
"type": "keyword"
},
"user": {
"type": "keyword"
}
}
}
}
}
----
<1> `fields`: Multi-field mapping that allows both full text and exact matching
<2> `geoip.properties`: Object type field containing location-related properties
<3> `geoip.location`: Geographic coordinates stored as geo_point for location-based queries
<4> `products.properties`: Nested structure containing details about items in each order
==============
The sample data includes the following <<mapping-types,field data types>>:
* <<text,`text`>> and <<keyword,`keyword`>> for text fields
** Most `text` fields have a `.keyword` subfield for exact matching using <<multi-fields,multi-fields>>
* <<date,`date`>> for date fields
* 3 <<number,numeric>> types:
** `integer` for whole numbers
** `long` for large whole numbers
** `half_float` for floating-point numbers
* <<geo-point,`geo_point`>> for geographic coordinates
* <<object,`object`>> for nested structures such as `products`, `geoip`, `event`
Now that we understand the structure of our sample data, let's start analyzing it.
[discrete]
[[aggregations-tutorial-basic-metrics]]
=== Get key business metrics
Let's start by calculating important metrics about orders and customers.
[discrete]
[[aggregations-tutorial-order-value]]
==== Get average order size
Calculate the average order value across all orders in the dataset using the <<search-aggregations-metrics-avg-aggregation,`avg`>> aggregation.
[source,console]
----
GET kibana_sample_data_ecommerce/_search
{
"size": 0, <1>
"aggs": {
"avg_order_value": { <2>
"avg": { <3>
"field": "taxful_total_price"
}
}
}
}
----
// TEST[skip:Using Kibana sample data]
<1> Set `size` to 0 to avoid returning matched documents in the response and return only the aggregation results
<2> A meaningful name that describes what this metric represents
<3> Configures an `avg` aggregation, which calculates a simple arithmetic mean
.Example response
[%collapsible]
==============
[source,console-result]
----
{
"took": 0,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 4675, <1>
"relation": "eq"
},
"max_score": null,
"hits": [] <2>
},
"aggregations": {
"avg_order_value": { <3>
"value": 75.05542864304813 <4>
}
}
}
----
// TEST[skip:Using Kibana sample data]
<1> Total number of orders in the dataset
<2> `hits` is empty because we set `size` to 0
<3> Results appear under the name we specified in the request
<4> The average order value is calculated dynamically from all the orders in the dataset
==============
[discrete]
[[aggregations-tutorial-order-stats]]
==== Get multiple order statistics at once
Calculate multiple statistics about orders in one request using the <<search-aggregations-metrics-stats-aggregation,`stats`>> aggregation.
[source,console]
----
GET kibana_sample_data_ecommerce/_search
{
"size": 0,
"aggs": {
"order_stats": { <1>
"stats": { <2>
"field": "taxful_total_price"
}
}
}
}
----
// TEST[skip:Using Kibana sample data]
<1> A descriptive name for this set of statistics
<2> `stats` returns count, min, max, avg, and sum at once
.Example response
[%collapsible]
==============
[source,console-result]
----
{
"aggregations": {
"order_stats": {
"count": 4675, <1>
"min": 6.98828125, <2>
"max": 2250, <3>
"avg": 75.05542864304813, <4>
"sum": 350884.12890625 <5>
}
}
}
----
// TEST[skip:Using Kibana sample data]
<1> `"count"`: Total number of orders in the dataset
<2> `"min"`: Lowest individual order value in the dataset
<3> `"max"`: Highest individual order value in the dataset
<4> `"avg"`: Average value per order across all orders
<5> `"sum"`: Total revenue from all orders combined
==============
[TIP]
====
The <<search-aggregations-metrics-stats-aggregation,stats aggregation>> is more efficient than running individual min, max, avg, and sum aggregations.
====
[discrete]
[[aggregations-tutorial-sales-patterns]]
=== Analyze sales patterns
Let's group orders in different ways to understand sales patterns.
[discrete]
[[aggregations-tutorial-category-breakdown]]
==== Break down sales by category
Group orders by category to see which product categories are most popular, using the <<search-aggregations-bucket-terms-aggregation,`terms`>> aggregation.
[source,console]
----
GET kibana_sample_data_ecommerce/_search
{
"size": 0,
"aggs": {
"sales_by_category": { <1>
"terms": { <2>
"field": "category.keyword", <3>
"size": 5, <4>
"order": { "_count": "desc" } <5>
}
}
}
}
----
// TEST[skip:Using Kibana sample data]
<1> Name reflecting the business purpose of this breakdown
<2> `terms` aggregation groups documents by field values
<3> Use <<keyword,`.keyword`>> field for exact matching on text fields
<4> Limit to top 5 categories
<5> Order by number of orders (descending)
.Example response
[%collapsible]
==============
[source,console-result]
----
{
"took": 4,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 4675,
"relation": "eq"
},
"max_score": null,
"hits": []
},
"aggregations": {
"sales_by_category": {
"doc_count_error_upper_bound": 0, <1>
"sum_other_doc_count": 572, <2>
"buckets": [ <3>
{
"key": "Men's Clothing", <4>
"doc_count": 2024 <5>
},
{
"key": "Women's Clothing",
"doc_count": 1903
},
{
"key": "Women's Shoes",
"doc_count": 1136
},
{
"key": "Men's Shoes",
"doc_count": 944
},
{
"key": "Women's Accessories",
"doc_count": 830
}
]
}
}
}
----
// TEST[skip:Using Kibana sample data]
<1> Due to Elasticsearch's distributed architecture, when <<search-aggregations-bucket-terms-aggregation,terms aggregations>> run across multiple shards, the doc counts may have a small margin of error. This value indicates the maximum possible error in the counts.
<2> Count of documents in categories beyond the requested size.
<3> Array of category buckets, ordered by count.
<4> Category name.
<5> Number of orders in this category.
==============
[discrete]
[[aggregations-tutorial-daily-sales]]
==== Track daily sales patterns
Group orders by day to track daily sales patterns using the <<search-aggregations-bucket-datehistogram-aggregation,`date_histogram`>> aggregation.
[source,console]
----
GET kibana_sample_data_ecommerce/_search
{
"size": 0,
"aggs": {
"daily_orders": { <1>
"date_histogram": { <2>
"field": "order_date",
"calendar_interval": "day", <3>
"format": "yyyy-MM-dd", <4>
"min_doc_count": 0 <5>
}
}
}
}
----
// TEST[skip:Using Kibana sample data]
<1> Descriptive name for the time-series aggregation results.
<2> The `date_histogram` aggregration groups documents into time-based buckets, similar to terms aggregation but for dates.
<3> Uses <<calendar_and_fixed_intervals,calendar and fixed time intervals>> to handle months with different lengths. `"day"` ensures consistent daily grouping regardless of timezone.
<4> Formats dates in response using <<mapping-date-format,date patterns>> (e.g. "yyyy-MM-dd"). Refer to <<date-math,date math expressions>> for additional options.
<5> When `min_doc_count` is 0, returns buckets for days with no orders, useful for continuous time series visualization.
.Example response
[%collapsible]
==============
[source,console-result]
----
{
"took": 2,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 4675,
"relation": "eq"
},
"max_score": null,
"hits": []
},
"aggregations": {
"daily_orders": { <1>
"buckets": [ <2>
{
"key_as_string": "2024-11-28", <3>
"key": 1732752000000, <4>
"doc_count": 146 <5>
},
{
"key_as_string": "2024-11-29",
"key": 1732838400000,
"doc_count": 153
},
{
"key_as_string": "2024-11-30",
"key": 1732924800000,
"doc_count": 143
},
{
"key_as_string": "2024-12-01",
"key": 1733011200000,
"doc_count": 140
},
{
"key_as_string": "2024-12-02",
"key": 1733097600000,
"doc_count": 139
},
{
"key_as_string": "2024-12-03",
"key": 1733184000000,
"doc_count": 157
},
{
"key_as_string": "2024-12-04",
"key": 1733270400000,
"doc_count": 145
},
{
"key_as_string": "2024-12-05",
"key": 1733356800000,
"doc_count": 152
},
{
"key_as_string": "2024-12-06",
"key": 1733443200000,
"doc_count": 163
},
{
"key_as_string": "2024-12-07",
"key": 1733529600000,
"doc_count": 141
},
{
"key_as_string": "2024-12-08",
"key": 1733616000000,
"doc_count": 151
},
{
"key_as_string": "2024-12-09",
"key": 1733702400000,
"doc_count": 143
},
{
"key_as_string": "2024-12-10",
"key": 1733788800000,
"doc_count": 143
},
{
"key_as_string": "2024-12-11",
"key": 1733875200000,
"doc_count": 142
},
{
"key_as_string": "2024-12-12",
"key": 1733961600000,
"doc_count": 161
},
{
"key_as_string": "2024-12-13",
"key": 1734048000000,
"doc_count": 144
},
{
"key_as_string": "2024-12-14",
"key": 1734134400000,
"doc_count": 157
},
{
"key_as_string": "2024-12-15",
"key": 1734220800000,
"doc_count": 158
},
{
"key_as_string": "2024-12-16",
"key": 1734307200000,
"doc_count": 144
},
{
"key_as_string": "2024-12-17",
"key": 1734393600000,
"doc_count": 151
},
{
"key_as_string": "2024-12-18",
"key": 1734480000000,
"doc_count": 145
},
{
"key_as_string": "2024-12-19",
"key": 1734566400000,
"doc_count": 157
},
{
"key_as_string": "2024-12-20",
"key": 1734652800000,
"doc_count": 158
},
{
"key_as_string": "2024-12-21",
"key": 1734739200000,
"doc_count": 153
},
{
"key_as_string": "2024-12-22",
"key": 1734825600000,
"doc_count": 165
},
{
"key_as_string": "2024-12-23",
"key": 1734912000000,
"doc_count": 153
},
{
"key_as_string": "2024-12-24",
"key": 1734998400000,
"doc_count": 158
},
{
"key_as_string": "2024-12-25",
"key": 1735084800000,
"doc_count": 160
},
{
"key_as_string": "2024-12-26",
"key": 1735171200000,
"doc_count": 159
},
{
"key_as_string": "2024-12-27",
"key": 1735257600000,
"doc_count": 152
},
{
"key_as_string": "2024-12-28",
"key": 1735344000000,
"doc_count": 142
}
]
}
}
}
----
// TEST[skip:Using Kibana sample data]
<1> Results of our named aggregation "daily_orders"
<2> Time-based buckets from date_histogram aggregation
<3> `key_as_string` is the human-readable date for this bucket
<4> `key` is the same date represented as the Unix timestamp for this bucket
<5> `doc_count` counts the number of documents that fall into this time bucket
==============
[discrete]
[[aggregations-tutorial-combined-analysis]]
=== Combine metrics with groupings
Now let's calculate <<search-aggregations-metrics,metrics>> within each group to get deeper insights.
[discrete]
[[aggregations-tutorial-category-metrics]]
==== Compare category performance
Calculate metrics within each category to compare performance across categories.
[source,console]
----
GET kibana_sample_data_ecommerce/_search
{
"size": 0,
"aggs": {
"categories": {
"terms": {
"field": "category.keyword",
"size": 5,
"order": { "total_revenue": "desc" } <1>
},
"aggs": { <2>
"total_revenue": { <3>
"sum": {
"field": "taxful_total_price"
}
},
"avg_order_value": { <4>
"avg": {
"field": "taxful_total_price"
}
},
"total_items": { <5>
"sum": {
"field": "total_quantity"
}
}
}
}
}
}
----
// TEST[skip:Using Kibana sample data]
<1> Order categories by their total revenue instead of count
<2> Define metrics to calculate within each category
<3> Total revenue for the category
<4> Average order value in the category
<5> Total number of items sold
.Example response
[%collapsible]
==============
[source,console-result]
----
{
"aggregations": {
"categories": {
"buckets": [
{
"key": "Men's Clothing", <1>
"doc_count": 2179, <2>
"total_revenue": { <3>
"value": 156729.453125
},
"avg_order_value": { <4>
"value": 71.92726898715927
},
"total_items": { <5>
"value": 8716
}
},
{
"key": "Women's Clothing",
"doc_count": 2262,
...
}
]
}
}
}
----
// TEST[skip:Using Kibana sample data]
<1> Category name
<2> Number of orders
<3> Total revenue for this category
<4> Average order value for this category
<5> Total quantity of items sold
==============
[discrete]
[[aggregations-tutorial-daily-metrics]]
==== Analyze daily sales performance
Let's combine metrics to track daily trends: daily revenue, unique customers, and average basket size.
[source,console]
----
GET kibana_sample_data_ecommerce/_search
{
"size": 0,
"aggs": {
"daily_sales": {
"date_histogram": {
"field": "order_date",
"calendar_interval": "day",
"format": "yyyy-MM-dd"
},
"aggs": {
"revenue": { <1>
"sum": {
"field": "taxful_total_price"
}
},
"unique_customers": { <2>
"cardinality": {
"field": "customer_id"
}
},
"avg_basket_size": { <3>
"avg": {
"field": "total_quantity"
}
}
}
}
}
}
----
// TEST[skip:Using Kibana sample data]
<1> Daily revenue
<2> Uses the <<search-aggregations-metrics-cardinality-aggregation,`cardinality`>> aggregation to count unique customers per day
<3> Average number of items per order
.Example response
[%collapsible]
==============
[source,console-result]
----
{
"took": 119,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 4675,
"relation": "eq"
},
"max_score": null,
"hits": []
},
"aggregations": {
"daily_sales": {
"buckets": [
{
"key_as_string": "2024-11-14",
"key": 1731542400000,
"doc_count": 146,
"unique_customers": { <1>
"value": 42
},
"revenue": { <2>
"value": 10578.53125
},
"avg_basket_size": { <3>
"value": 2.1780821917808217
}
},
{
"key_as_string": "2024-11-15",
"key": 1731628800000,
"doc_count": 153,
"unique_customers": {
"value": 44
},
"revenue": {
"value": 10448
},
"avg_basket_size": {
"value": 2.183006535947712
}
},
{
"key_as_string": "2024-11-16",
"key": 1731715200000,
"doc_count": 143,
"unique_customers": {
"value": 45
},
"revenue": {
"value": 10283.484375
},
"avg_basket_size": {
"value": 2.111888111888112
}
},
{
"key_as_string": "2024-11-17",
"key": 1731801600000,
"doc_count": 140,
"unique_customers": {
"value": 42
},
"revenue": {
"value": 10145.5234375
},
"avg_basket_size": {
"value": 2.142857142857143
}
},
{
"key_as_string": "2024-11-18",
"key": 1731888000000,
"doc_count": 139,
"unique_customers": {
"value": 42
},
"revenue": {
"value": 12012.609375
},
"avg_basket_size": {
"value": 2.158273381294964
}
},
{
"key_as_string": "2024-11-19",
"key": 1731974400000,
"doc_count": 157,
"unique_customers": {
"value": 43
},
"revenue": {
"value": 11009.45703125
},
"avg_basket_size": {
"value": 2.0955414012738856
}
},
{
"key_as_string": "2024-11-20",
"key": 1732060800000,
"doc_count": 145,
"unique_customers": {
"value": 44
},
"revenue": {
"value": 10720.59375
},
"avg_basket_size": {
"value": 2.179310344827586
}
},
{
"key_as_string": "2024-11-21",
"key": 1732147200000,
"doc_count": 152,
"unique_customers": {
"value": 43
},
"revenue": {
"value": 11185.3671875
},
"avg_basket_size": {
"value": 2.1710526315789473
}
},
{
"key_as_string": "2024-11-22",
"key": 1732233600000,
"doc_count": 163,
"unique_customers": {
"value": 44
},
"revenue": {
"value": 13560.140625
},
"avg_basket_size": {
"value": 2.2576687116564416
}
},
{
"key_as_string": "2024-11-23",
"key": 1732320000000,
"doc_count": 141,
"unique_customers": {
"value": 45
},
"revenue": {
"value": 9884.78125
},
"avg_basket_size": {
"value": 2.099290780141844
}
},
{
"key_as_string": "2024-11-24",
"key": 1732406400000,
"doc_count": 151,
"unique_customers": {
"value": 44
},
"revenue": {
"value": 11075.65625
},
"avg_basket_size": {
"value": 2.0927152317880795
}
},
{
"key_as_string": "2024-11-25",
"key": 1732492800000,
"doc_count": 143,
"unique_customers": {
"value": 41
},
"revenue": {
"value": 10323.8515625
},
"avg_basket_size": {
"value": 2.167832167832168
}
},
{
"key_as_string": "2024-11-26",
"key": 1732579200000,
"doc_count": 143,
"unique_customers": {
"value": 44
},
"revenue": {
"value": 10369.546875
},
"avg_basket_size": {
"value": 2.167832167832168
}
},
{
"key_as_string": "2024-11-27",
"key": 1732665600000,
"doc_count": 142,
"unique_customers": {
"value": 46
},
"revenue": {
"value": 11711.890625
},
"avg_basket_size": {
"value": 2.1971830985915495
}
},
{
"key_as_string": "2024-11-28",
"key": 1732752000000,
"doc_count": 161,
"unique_customers": {
"value": 43
},
"revenue": {
"value": 12612.6640625
},
"avg_basket_size": {
"value": 2.1180124223602483
}
},
{
"key_as_string": "2024-11-29",
"key": 1732838400000,
"doc_count": 144,
"unique_customers": {
"value": 42
},
"revenue": {
"value": 10176.87890625
},
"avg_basket_size": {
"value": 2.0347222222222223
}
},
{
"key_as_string": "2024-11-30",
"key": 1732924800000,
"doc_count": 157,
"unique_customers": {
"value": 43
},
"revenue": {
"value": 11480.33203125
},
"avg_basket_size": {
"value": 2.159235668789809
}
},
{
"key_as_string": "2024-12-01",
"key": 1733011200000,
"doc_count": 158,
"unique_customers": {
"value": 42
},
"revenue": {
"value": 11533.265625
},
"avg_basket_size": {
"value": 2.0822784810126582
}
},
{
"key_as_string": "2024-12-02",
"key": 1733097600000,
"doc_count": 144,
"unique_customers": {
"value": 43
},
"revenue": {
"value": 10499.8125
},
"avg_basket_size": {
"value": 2.201388888888889
}
},
{
"key_as_string": "2024-12-03",
"key": 1733184000000,
"doc_count": 151,
"unique_customers": {
"value": 40
},
"revenue": {
"value": 12111.6875
},
"avg_basket_size": {
"value": 2.172185430463576
}
},
{
"key_as_string": "2024-12-04",
"key": 1733270400000,
"doc_count": 145,
"unique_customers": {
"value": 40
},
"revenue": {
"value": 10530.765625
},
"avg_basket_size": {
"value": 2.0965517241379312
}
},
{
"key_as_string": "2024-12-05",
"key": 1733356800000,
"doc_count": 157,
"unique_customers": {
"value": 43
},
"revenue": {
"value": 11872.5625
},
"avg_basket_size": {
"value": 2.1464968152866244
}
},
{
"key_as_string": "2024-12-06",
"key": 1733443200000,
"doc_count": 158,
"unique_customers": {
"value": 42
},
"revenue": {
"value": 12109.453125
},
"avg_basket_size": {
"value": 2.151898734177215
}
},
{
"key_as_string": "2024-12-07",
"key": 1733529600000,
"doc_count": 153,
"unique_customers": {
"value": 42
},
"revenue": {
"value": 11057.40625
},
"avg_basket_size": {
"value": 2.111111111111111
}
},
{
"key_as_string": "2024-12-08",
"key": 1733616000000,
"doc_count": 165,
"unique_customers": {
"value": 42
},
"revenue": {
"value": 13095.609375
},
"avg_basket_size": {
"value": 2.1818181818181817
}
},
{
"key_as_string": "2024-12-09",
"key": 1733702400000,
"doc_count": 153,
"unique_customers": {
"value": 41
},
"revenue": {
"value": 12574.015625
},
"avg_basket_size": {
"value": 2.2287581699346406
}
},
{
"key_as_string": "2024-12-10",
"key": 1733788800000,
"doc_count": 158,
"unique_customers": {
"value": 42
},
"revenue": {
"value": 11188.1875
},
"avg_basket_size": {
"value": 2.151898734177215
}
},
{
"key_as_string": "2024-12-11",
"key": 1733875200000,
"doc_count": 160,
"unique_customers": {
"value": 42
},
"revenue": {
"value": 12117.65625
},
"avg_basket_size": {
"value": 2.20625
}
},
{
"key_as_string": "2024-12-12",
"key": 1733961600000,
"doc_count": 159,
"unique_customers": {
"value": 45
},
"revenue": {
"value": 11558.25
},
"avg_basket_size": {
"value": 2.1823899371069184
}
},
{
"key_as_string": "2024-12-13",
"key": 1734048000000,
"doc_count": 152,
"unique_customers": {
"value": 45
},
"revenue": {
"value": 11921.1171875
},
"avg_basket_size": {
"value": 2.289473684210526
}
},
{
"key_as_string": "2024-12-14",
"key": 1734134400000,
"doc_count": 142,
"unique_customers": {
"value": 45
},
"revenue": {
"value": 11135.03125
},
"avg_basket_size": {
"value": 2.183098591549296
}
}
]
}
}
}
----
// TEST[skip:Using Kibana sample data]
==============
[discrete]
[[aggregations-tutorial-trends]]
=== Track trends and patterns
You can use <<search-aggregations-pipeline,pipeline aggregations>> on the results of other aggregations.
Let's analyze how metrics change over time.
[discrete]
[[aggregations-tutorial-moving-average]]
==== Smooth out daily fluctuations
Moving averages help identify trends by reducing day-to-day noise in the data.
Let's observe sales trends more clearly by smoothing daily revenue variations, using the <<search-aggregations-pipeline-movfn-aggregation,Moving Function>> aggregation.
[source,console]
----
GET kibana_sample_data_ecommerce/_search
{
"size": 0,
"aggs": {
"daily_sales": {
"date_histogram": {
"field": "order_date",
"calendar_interval": "day"
},
"aggs": {
"daily_revenue": { <1>
"sum": {
"field": "taxful_total_price"
}
},
"smoothed_revenue": { <2>
"moving_fn": { <3>
"buckets_path": "daily_revenue", <4>
"window": 3, <5>
"script": "MovingFunctions.unweightedAvg(values)" <6>
}
}
}
}
}
}
----
// TEST[skip:Using Kibana sample data]
<1> Calculate daily revenue first.
<2> Create a smoothed version of the daily revenue.
<3> Use `moving_fn` for moving window calculations.
<4> Reference the revenue from our date histogram.
<5> Use a 3-day window — use different window sizes to see trends at different time scales.
<6> Use the built-in unweighted average function in the `moving_fn` aggregation.
.Example response
[%collapsible]
==============
[source,console-result]
----
{
"took": 13,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 4675,
"relation": "eq"
},
"max_score": null,
"hits": []
},
"aggregations": {
"daily_sales": {
"buckets": [
{
"key_as_string": "2024-11-14T00:00:00.000Z", <1>
"key": 1731542400000,
"doc_count": 146, <2>
"daily_revenue": { <3>
"value": 10578.53125
},
"smoothed_revenue": { <4>
"value": null
}
},
{
"key_as_string": "2024-11-15T00:00:00.000Z",
"key": 1731628800000,
"doc_count": 153,
"daily_revenue": {
"value": 10448
},
"smoothed_revenue": { <5>
"value": 10578.53125
}
},
{
"key_as_string": "2024-11-16T00:00:00.000Z",
"key": 1731715200000,
"doc_count": 143,
"daily_revenue": {
"value": 10283.484375
},
"smoothed_revenue": {
"value": 10513.265625
}
},
{
"key_as_string": "2024-11-17T00:00:00.000Z",
"key": 1731801600000,
"doc_count": 140,
"daily_revenue": {
"value": 10145.5234375
},
"smoothed_revenue": {
"value": 10436.671875
}
},
{
"key_as_string": "2024-11-18T00:00:00.000Z",
"key": 1731888000000,
"doc_count": 139,
"daily_revenue": {
"value": 12012.609375
},
"smoothed_revenue": {
"value": 10292.3359375
}
},
{
"key_as_string": "2024-11-19T00:00:00.000Z",
"key": 1731974400000,
"doc_count": 157,
"daily_revenue": {
"value": 11009.45703125
},
"smoothed_revenue": {
"value": 10813.872395833334
}
},
{
"key_as_string": "2024-11-20T00:00:00.000Z",
"key": 1732060800000,
"doc_count": 145,
"daily_revenue": {
"value": 10720.59375
},
"smoothed_revenue": {
"value": 11055.86328125
}
},
{
"key_as_string": "2024-11-21T00:00:00.000Z",
"key": 1732147200000,
"doc_count": 152,
"daily_revenue": {
"value": 11185.3671875
},
"smoothed_revenue": {
"value": 11247.553385416666
}
},
{
"key_as_string": "2024-11-22T00:00:00.000Z",
"key": 1732233600000,
"doc_count": 163,
"daily_revenue": {
"value": 13560.140625
},
"smoothed_revenue": {
"value": 10971.805989583334
}
},
{
"key_as_string": "2024-11-23T00:00:00.000Z",
"key": 1732320000000,
"doc_count": 141,
"daily_revenue": {
"value": 9884.78125
},
"smoothed_revenue": {
"value": 11822.033854166666
}
},
{
"key_as_string": "2024-11-24T00:00:00.000Z",
"key": 1732406400000,
"doc_count": 151,
"daily_revenue": {
"value": 11075.65625
},
"smoothed_revenue": {
"value": 11543.4296875
}
},
{
"key_as_string": "2024-11-25T00:00:00.000Z",
"key": 1732492800000,
"doc_count": 143,
"daily_revenue": {
"value": 10323.8515625
},
"smoothed_revenue": {
"value": 11506.859375
}
},
{
"key_as_string": "2024-11-26T00:00:00.000Z",
"key": 1732579200000,
"doc_count": 143,
"daily_revenue": {
"value": 10369.546875
},
"smoothed_revenue": {
"value": 10428.096354166666
}
},
{
"key_as_string": "2024-11-27T00:00:00.000Z",
"key": 1732665600000,
"doc_count": 142,
"daily_revenue": {
"value": 11711.890625
},
"smoothed_revenue": {
"value": 10589.684895833334
}
},
{
"key_as_string": "2024-11-28T00:00:00.000Z",
"key": 1732752000000,
"doc_count": 161,
"daily_revenue": {
"value": 12612.6640625
},
"smoothed_revenue": {
"value": 10801.763020833334
}
},
{
"key_as_string": "2024-11-29T00:00:00.000Z",
"key": 1732838400000,
"doc_count": 144,
"daily_revenue": {
"value": 10176.87890625
},
"smoothed_revenue": {
"value": 11564.700520833334
}
},
{
"key_as_string": "2024-11-30T00:00:00.000Z",
"key": 1732924800000,
"doc_count": 157,
"daily_revenue": {
"value": 11480.33203125
},
"smoothed_revenue": {
"value": 11500.477864583334
}
},
{
"key_as_string": "2024-12-01T00:00:00.000Z",
"key": 1733011200000,
"doc_count": 158,
"daily_revenue": {
"value": 11533.265625
},
"smoothed_revenue": {
"value": 11423.291666666666
}
},
{
"key_as_string": "2024-12-02T00:00:00.000Z",
"key": 1733097600000,
"doc_count": 144,
"daily_revenue": {
"value": 10499.8125
},
"smoothed_revenue": {
"value": 11063.4921875
}
},
{
"key_as_string": "2024-12-03T00:00:00.000Z",
"key": 1733184000000,
"doc_count": 151,
"daily_revenue": {
"value": 12111.6875
},
"smoothed_revenue": {
"value": 11171.13671875
}
},
{
"key_as_string": "2024-12-04T00:00:00.000Z",
"key": 1733270400000,
"doc_count": 145,
"daily_revenue": {
"value": 10530.765625
},
"smoothed_revenue": {
"value": 11381.588541666666
}
},
{
"key_as_string": "2024-12-05T00:00:00.000Z",
"key": 1733356800000,
"doc_count": 157,
"daily_revenue": {
"value": 11872.5625
},
"smoothed_revenue": {
"value": 11047.421875
}
},
{
"key_as_string": "2024-12-06T00:00:00.000Z",
"key": 1733443200000,
"doc_count": 158,
"daily_revenue": {
"value": 12109.453125
},
"smoothed_revenue": {
"value": 11505.005208333334
}
},
{
"key_as_string": "2024-12-07T00:00:00.000Z",
"key": 1733529600000,
"doc_count": 153,
"daily_revenue": {
"value": 11057.40625
},
"smoothed_revenue": {
"value": 11504.260416666666
}
},
{
"key_as_string": "2024-12-08T00:00:00.000Z",
"key": 1733616000000,
"doc_count": 165,
"daily_revenue": {
"value": 13095.609375
},
"smoothed_revenue": {
"value": 11679.807291666666
}
},
{
"key_as_string": "2024-12-09T00:00:00.000Z",
"key": 1733702400000,
"doc_count": 153,
"daily_revenue": {
"value": 12574.015625
},
"smoothed_revenue": {
"value": 12087.489583333334
}
},
{
"key_as_string": "2024-12-10T00:00:00.000Z",
"key": 1733788800000,
"doc_count": 158,
"daily_revenue": {
"value": 11188.1875
},
"smoothed_revenue": {
"value": 12242.34375
}
},
{
"key_as_string": "2024-12-11T00:00:00.000Z",
"key": 1733875200000,
"doc_count": 160,
"daily_revenue": {
"value": 12117.65625
},
"smoothed_revenue": {
"value": 12285.9375
}
},
{
"key_as_string": "2024-12-12T00:00:00.000Z",
"key": 1733961600000,
"doc_count": 159,
"daily_revenue": {
"value": 11558.25
},
"smoothed_revenue": {
"value": 11959.953125
}
},
{
"key_as_string": "2024-12-13T00:00:00.000Z",
"key": 1734048000000,
"doc_count": 152,
"daily_revenue": {
"value": 11921.1171875
},
"smoothed_revenue": {
"value": 11621.364583333334
}
},
{
"key_as_string": "2024-12-14T00:00:00.000Z",
"key": 1734134400000,
"doc_count": 142,
"daily_revenue": {
"value": 11135.03125
},
"smoothed_revenue": {
"value": 11865.674479166666
}
}
]
}
}
}
----
// TEST[skip:Using Kibana sample data]
<1> Date of the bucket is in default ISO format because we didn't specify a format
<2> Number of orders for this day
<3> Raw daily revenue before smoothing
<4> First day has no smoothed value as it needs previous days for the calculation
<5> Moving average starts from second day, using a 3-day window
==============
[TIP]
====
Notice how the smoothed values lag behind the actual values - this is because they need previous days' data to calculate. The first day will always be null when using moving averages.
====
[discrete]
[[aggregations-tutorial-cumulative]]
==== Track running totals
Track running totals over time using the <<search-aggregations-pipeline-cumulative-sum-aggregation,`cumulative_sum`>> aggregation.
[source,console]
----
GET kibana_sample_data_ecommerce/_search
{
"size": 0,
"aggs": {
"daily_sales": {
"date_histogram": {
"field": "order_date",
"calendar_interval": "day"
},
"aggs": {
"revenue": {
"sum": {
"field": "taxful_total_price"
}
},
"cumulative_revenue": { <1>
"cumulative_sum": { <2>
"buckets_path": "revenue" <3>
}
}
}
}
}
}
----
// TEST[skip:Using Kibana sample data]
<1> Name for our running total
<2> `cumulative_sum` adds up values across buckets
<3> Reference the revenue we want to accumulate
.Example response
[%collapsible]
==============
[source,console-result]
----
{
"took": 4,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 4675,
"relation": "eq"
},
"max_score": null,
"hits": []
},
"aggregations": {
"daily_sales": { <1>
"buckets": [ <2>
{
"key_as_string": "2024-11-14T00:00:00.000Z", <3>
"key": 1731542400000,
"doc_count": 146,
"revenue": { <4>
"value": 10578.53125
},
"cumulative_revenue": { <5>
"value": 10578.53125
}
},
{
"key_as_string": "2024-11-15T00:00:00.000Z",
"key": 1731628800000,
"doc_count": 153,
"revenue": {
"value": 10448
},
"cumulative_revenue": {
"value": 21026.53125
}
},
{
"key_as_string": "2024-11-16T00:00:00.000Z",
"key": 1731715200000,
"doc_count": 143,
"revenue": {
"value": 10283.484375
},
"cumulative_revenue": {
"value": 31310.015625
}
},
{
"key_as_string": "2024-11-17T00:00:00.000Z",
"key": 1731801600000,
"doc_count": 140,
"revenue": {
"value": 10145.5234375
},
"cumulative_revenue": {
"value": 41455.5390625
}
},
{
"key_as_string": "2024-11-18T00:00:00.000Z",
"key": 1731888000000,
"doc_count": 139,
"revenue": {
"value": 12012.609375
},
"cumulative_revenue": {
"value": 53468.1484375
}
},
{
"key_as_string": "2024-11-19T00:00:00.000Z",
"key": 1731974400000,
"doc_count": 157,
"revenue": {
"value": 11009.45703125
},
"cumulative_revenue": {
"value": 64477.60546875
}
},
{
"key_as_string": "2024-11-20T00:00:00.000Z",
"key": 1732060800000,
"doc_count": 145,
"revenue": {
"value": 10720.59375
},
"cumulative_revenue": {
"value": 75198.19921875
}
},
{
"key_as_string": "2024-11-21T00:00:00.000Z",
"key": 1732147200000,
"doc_count": 152,
"revenue": {
"value": 11185.3671875
},
"cumulative_revenue": {
"value": 86383.56640625
}
},
{
"key_as_string": "2024-11-22T00:00:00.000Z",
"key": 1732233600000,
"doc_count": 163,
"revenue": {
"value": 13560.140625
},
"cumulative_revenue": {
"value": 99943.70703125
}
},
{
"key_as_string": "2024-11-23T00:00:00.000Z",
"key": 1732320000000,
"doc_count": 141,
"revenue": {
"value": 9884.78125
},
"cumulative_revenue": {
"value": 109828.48828125
}
},
{
"key_as_string": "2024-11-24T00:00:00.000Z",
"key": 1732406400000,
"doc_count": 151,
"revenue": {
"value": 11075.65625
},
"cumulative_revenue": {
"value": 120904.14453125
}
},
{
"key_as_string": "2024-11-25T00:00:00.000Z",
"key": 1732492800000,
"doc_count": 143,
"revenue": {
"value": 10323.8515625
},
"cumulative_revenue": {
"value": 131227.99609375
}
},
{
"key_as_string": "2024-11-26T00:00:00.000Z",
"key": 1732579200000,
"doc_count": 143,
"revenue": {
"value": 10369.546875
},
"cumulative_revenue": {
"value": 141597.54296875
}
},
{
"key_as_string": "2024-11-27T00:00:00.000Z",
"key": 1732665600000,
"doc_count": 142,
"revenue": {
"value": 11711.890625
},
"cumulative_revenue": {
"value": 153309.43359375
}
},
{
"key_as_string": "2024-11-28T00:00:00.000Z",
"key": 1732752000000,
"doc_count": 161,
"revenue": {
"value": 12612.6640625
},
"cumulative_revenue": {
"value": 165922.09765625
}
},
{
"key_as_string": "2024-11-29T00:00:00.000Z",
"key": 1732838400000,
"doc_count": 144,
"revenue": {
"value": 10176.87890625
},
"cumulative_revenue": {
"value": 176098.9765625
}
},
{
"key_as_string": "2024-11-30T00:00:00.000Z",
"key": 1732924800000,
"doc_count": 157,
"revenue": {
"value": 11480.33203125
},
"cumulative_revenue": {
"value": 187579.30859375
}
},
{
"key_as_string": "2024-12-01T00:00:00.000Z",
"key": 1733011200000,
"doc_count": 158,
"revenue": {
"value": 11533.265625
},
"cumulative_revenue": {
"value": 199112.57421875
}
},
{
"key_as_string": "2024-12-02T00:00:00.000Z",
"key": 1733097600000,
"doc_count": 144,
"revenue": {
"value": 10499.8125
},
"cumulative_revenue": {
"value": 209612.38671875
}
},
{
"key_as_string": "2024-12-03T00:00:00.000Z",
"key": 1733184000000,
"doc_count": 151,
"revenue": {
"value": 12111.6875
},
"cumulative_revenue": {
"value": 221724.07421875
}
},
{
"key_as_string": "2024-12-04T00:00:00.000Z",
"key": 1733270400000,
"doc_count": 145,
"revenue": {
"value": 10530.765625
},
"cumulative_revenue": {
"value": 232254.83984375
}
},
{
"key_as_string": "2024-12-05T00:00:00.000Z",
"key": 1733356800000,
"doc_count": 157,
"revenue": {
"value": 11872.5625
},
"cumulative_revenue": {
"value": 244127.40234375
}
},
{
"key_as_string": "2024-12-06T00:00:00.000Z",
"key": 1733443200000,
"doc_count": 158,
"revenue": {
"value": 12109.453125
},
"cumulative_revenue": {
"value": 256236.85546875
}
},
{
"key_as_string": "2024-12-07T00:00:00.000Z",
"key": 1733529600000,
"doc_count": 153,
"revenue": {
"value": 11057.40625
},
"cumulative_revenue": {
"value": 267294.26171875
}
},
{
"key_as_string": "2024-12-08T00:00:00.000Z",
"key": 1733616000000,
"doc_count": 165,
"revenue": {
"value": 13095.609375
},
"cumulative_revenue": {
"value": 280389.87109375
}
},
{
"key_as_string": "2024-12-09T00:00:00.000Z",
"key": 1733702400000,
"doc_count": 153,
"revenue": {
"value": 12574.015625
},
"cumulative_revenue": {
"value": 292963.88671875
}
},
{
"key_as_string": "2024-12-10T00:00:00.000Z",
"key": 1733788800000,
"doc_count": 158,
"revenue": {
"value": 11188.1875
},
"cumulative_revenue": {
"value": 304152.07421875
}
},
{
"key_as_string": "2024-12-11T00:00:00.000Z",
"key": 1733875200000,
"doc_count": 160,
"revenue": {
"value": 12117.65625
},
"cumulative_revenue": {
"value": 316269.73046875
}
},
{
"key_as_string": "2024-12-12T00:00:00.000Z",
"key": 1733961600000,
"doc_count": 159,
"revenue": {
"value": 11558.25
},
"cumulative_revenue": {
"value": 327827.98046875
}
},
{
"key_as_string": "2024-12-13T00:00:00.000Z",
"key": 1734048000000,
"doc_count": 152,
"revenue": {
"value": 11921.1171875
},
"cumulative_revenue": {
"value": 339749.09765625
}
},
{
"key_as_string": "2024-12-14T00:00:00.000Z",
"key": 1734134400000,
"doc_count": 142,
"revenue": {
"value": 11135.03125
},
"cumulative_revenue": {
"value": 350884.12890625
}
}
]
}
}
}
----
// TEST[skip:Using Kibana sample data]
<1> `daily_sales`: Results from our daily sales date histogram
<2> `buckets`: Array of time-based buckets
<3> `key_as_string`: Date for this bucket (in ISO format since no format specified)
<4> `revenue`: Daily revenue for this date
<5> `cumulative_revenue`: Running total of revenue up to this date
==============
[discrete]
[[aggregations-tutorial-next-steps]]
=== Next steps
Refer to the <<search-aggregations,aggregations reference>> for more details on all available aggregation types.