Analizando Datos con Agregaciones en Elasticsearch: Guía Completa de Uso
Las agregaciones en Elasticsearch son una herramienta poderosa para obtener insights de tus datos. Este tutorial te guiará a través de los diferentes tipos de agregaciones, desde las más básicas hasta las más complejas, permitiéndote transformar tus datos crudos en información valiosa y actionable.
Las bases de datos NoSQL como Elasticsearch son conocidas por su velocidad y flexibilidad en la búsqueda de texto completo. Sin embargo, su verdadero potencial analítico brilla cuando utilizamos las agregaciones. Las agregaciones nos permiten procesar y analizar grandes volúmenes de datos, extrayendo métricas, estadísticas y agrupaciones de forma eficiente.
En este tutorial, exploraremos en profundidad el mundo de las agregaciones de Elasticsearch. Aprenderemos cómo utilizarlas para generar informes, construir paneles de control y entender mejor la información contenida en nuestros índices.
🚀 ¿Qué son las Agregaciones en Elasticsearch?
Imagina que tienes millones de documentos en Elasticsearch, cada uno representando una venta en tu tienda online. Quieres saber el total de ventas por región, el producto más vendido en el último mes o la cantidad promedio de artículos por pedido. Realizar estas consultas con búsquedas individuales sería ineficiente o imposible. Aquí es donde entran las agregaciones.
Las agregaciones son operaciones que procesan los datos de tus documentos para devolver resultados analíticos. Son similares a las cláusulas GROUP BY y funciones de agregación (SUM, AVG, COUNT, etc.) en SQL, pero con una flexibilidad y escalabilidad mucho mayores, diseñadas para el ecosistema distribuido de Elasticsearch.
🎯 Tipos principales de Agregaciones
Elasticsearch clasifica las agregaciones en cuatro categorías principales:
- Metric Aggregations: Calculan métricas sobre un conjunto de documentos (suma, promedio, mínimo, máximo, recuento, etc.).
- Bucket Aggregations: Agrupan documentos en "cubos" (buckets) basándose en un criterio. Cada bucket puede contener métricas o sub-agregaciones.
- Pipeline Aggregations: Operan sobre la salida de otras agregaciones, permitiendo cálculos más complejos como el promedio de promedios o la diferencia entre totales.
- Matrix Aggregations: Realizan cálculos sobre múltiples campos de documentos y producen una matriz de resultados. Menos comunes, pero útiles para correlaciones multidimensionales.
En este tutorial, nos centraremos principalmente en las Metric y Bucket Aggregations, ya que son las más fundamentales y ampliamente utilizadas.
🛠️ Comenzando: Tu Primeras Agregaciones
Para empezar, necesitamos algunos datos. Vamos a simular un índice de ventas (sales) con documentos que contienen información como el price, product_id, category y region.
📝 Preparando los datos de ejemplo
Primero, creamos un índice simple sin mapeo explícito para que Elasticsearch lo infiera:
PUT /sales
{
"settings": {
"number_of_shards": 1
}
}
Ahora, indexamos algunos documentos de ejemplo:
POST /sales/_bulk
{"index":{"_id":"1"}}
{"price": 100.50, "product_id": "P001", "category": "Electronics", "region": "North", "timestamp": "2023-01-01T10:00:00Z"}
{"index":{"_id":"2"}}
{"price": 50.00, "product_id": "P002", "category": "Books", "region": "South", "timestamp": "2023-01-01T11:00:00Z"}
{"index":{"_id":"3"}}
{"price": 75.25, "product_id": "P001", "category": "Electronics", "region": "North", "timestamp": "2023-01-01T12:00:00Z"}
{"index":{"_id":"4"}}
{"price": 200.00, "product_id": "P003", "category": "Electronics", "region": "West", "timestamp": "2023-01-01T13:00:00Z"}
{"index":{"_id":"5"}}
{"price": 30.00, "product_id": "P002", "category": "Books", "region": "North", "timestamp": "2023-01-01T14:00:00Z"}
{"index":{"_id":"6"}}
{"price": 120.00, "product_id": "P004", "category": "Apparel", "region": "East", "timestamp": "2023-01-01T15:00:00Z"}
{"index":{"_id":"7"}}
{"price": 90.00, "product_id": "P001", "category": "Electronics", "region": "South", "timestamp": "2023-01-01T16:00:00Z"}
{"index":{"_id":"8"}}
{"price": 60.00, "product_id": "P005", "category": "Books", "region": "West", "timestamp": "2023-01-01T17:00:00Z"}
{"index":{"_id":"9"}}
{"price": 150.00, "product_id": "P003", "category": "Electronics", "region": "East", "timestamp": "2023-01-01T18:00:00Z"}
{"index":{"_id":"10"}}
{"price": 80.00, "product_id": "P004", "category": "Apparel", "region": "North", "timestamp": "2023-01-01T19:00:00Z"}
{"index":{"_id":"11"}}
{"price": 110.00, "product_id": "P001", "category": "Electronics", "region": "North", "timestamp": "2023-01-01T20:00:00Z"}
💡 Desactivando los hits para optimizar
Cuando solo estamos interesados en los resultados de las agregaciones, podemos indicar a Elasticsearch que no devuelva los documentos (hits) que coinciden con la consulta. Esto reduce la carga en el clúster y acelera la respuesta.
GET /sales/_search
{
"size": 0,
"aggs": {
"total_sales": {
"sum": {
"field": "price"
}
}
}
}
En la respuesta, verás un hits.total.value que indica cuántos documentos se consideraron para la agregación, pero la lista hits.hits estará vacía. La sección clave es aggregations.
📊 Agregaciones de Métrica (Metric Aggregations)
Estas agregaciones calculan un único valor (o un conjunto de valores) sobre un campo numérico en un grupo de documentos. Son las más sencillas de entender y aplicar.
📈 Sum Aggregation (Suma)
Calcula la suma total de los valores de un campo numérico.
Pregunta: ¿Cuál es el total de ventas de todos los productos?
GET /sales/_search
{
"size": 0,
"aggs": {
"total_revenue": {
"sum": {
"field": "price"
}
}
}
}
Resultado esperado (fragmento):
...
"aggregations": {
"total_revenue": {
"value": 1075.75
}
}
...
⚖️ Avg Aggregation (Promedio)
Calcula el promedio de los valores de un campo numérico.
Pregunta: ¿Cuál es el precio promedio de una venta?
GET /sales/_search
{
"size": 0,
"aggs": {
"average_price": {
"avg": {
"field": "price"
}
}
}
}
📉 Min/Max Aggregations (Mínimo/Máximo)
Encuentra el valor mínimo y máximo de un campo numérico.
Pregunta: ¿Cuál fue la venta más barata y la más cara?
GET /sales/_search
{
"size": 0,
"aggs": {
"min_price": {
"min": {
"field": "price"
}
},
"max_price": {
"max": {
"field": "price"
}
}
}
}
🔢 Value Count Aggregation (Conteo de Valores)
Cuenta el número de documentos que tienen un valor en un campo específico.
Pregunta: ¿Cuántos productos diferentes se han vendido (basado en product_id)?
GET /sales/_search
{
"size": 0,
"aggs": {
"unique_product_count": {
"value_count": {
"field": "product_id"
}
}
}
}
📊 Stats Aggregation (Estadísticas)
Combina varias métricas básicas (min, max, avg, sum, count) en una sola agregación, muy útil para un resumen rápido.
Pregunta: Obtén un resumen estadístico de los precios de venta.
GET /sales/_search
{
"size": 0,
"aggs": {
"price_stats": {
"stats": {
"field": "price"
}
}
}
}
Resultado esperado (fragmento):
...
"aggregations": {
"price_stats": {
"count": 11,
"min": 30.0,
"max": 200.0,
"avg": 97.79545454545455,
"sum": 1075.75
}
}
...
📈 Extended Stats Aggregation (Estadísticas Extendidas)
Similar a stats, pero añade estadísticas más avanzadas como desviación estándar, varianza y suma de cuadrados.
Pregunta: Obtén estadísticas detalladas de los precios, incluyendo desviación estándar.
GET /sales/_search
{
"size": 0,
"aggs": {
"price_extended_stats": {
"extended_stats": {
"field": "price"
}
}
}
}
📦 Agregaciones de Cubo (Bucket Aggregations)
Estas agregaciones no calculan un valor único, sino que agrupan documentos en "cubos" o categorías. Cada cubo puede contener sus propias agregaciones de métrica o incluso sub-cubos, permitiendo análisis jerárquicos.
🏷️ Terms Aggregation (Términos)
Agrupa documentos por valores únicos de un campo (generalmente keyword o campos text con fielddata habilitado, aunque esto último es desaconsejado). Es el equivalente a GROUP BY.
Pregunta: ¿Cuántas ventas hay por categoría de producto?
GET /sales/_search
{
"size": 0,
"aggs": {
"sales_by_category": {
"terms": {
"field": "category.keyword",
"size": 10
}
}
}
}
El size dentro de terms controla cuántos buckets (términos) quieres que Elasticsearch devuelva. Por defecto, son 10. Si necesitas todos, puedes poner un valor muy alto o configurar shard_size.
Resultado esperado (fragmento):
...
"aggregations": {
"sales_by_category": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "Electronics",
"doc_count": 6
},
{
"key": "Books",
"doc_count": 3
},
{
"key": "Apparel",
"doc_count": 2
}
]
}
}
...
🔢 Cardinality Aggregation (Cardinalidad)
Calcula el número aproximado de valores únicos para un campo. Es muy eficiente para contar valores únicos en grandes conjuntos de datos, aunque puede haber un pequeño margen de error (configuración precision_threshold).
Pregunta: ¿Cuántos productos únicos (product_id) se han vendido?
GET /sales/_search
{
"size": 0,
"aggs": {
"unique_product_ids": {
"cardinality": {
"field": "product_id.keyword"
}
}
}
}
⏱️ Date Histogram Aggregation (Histograma de Fechas)
Agrupa documentos en cubos basados en un intervalo de tiempo. Ideal para series temporales y gráficos de tendencias.
Pregunta: ¿Cuántas ventas hubo por hora el 1 de enero de 2023?
GET /sales/_search
{
"size": 0,
"aggs": {
"sales_per_hour": {
"date_histogram": {
"field": "timestamp",
"fixed_interval": "1h",
"format": "yyyy-MM-dd HH"
}
}
}
}
El fixed_interval puede ser 1s, 1m, 1h, 1d, 1w, 1M (mes), 1y (año), etc.
📊 Range Aggregation (Rango)
Agrupa documentos en cubos definidos por rangos de valores para un campo numérico. Útil para clasificar datos en intervalos discretos.
Pregunta: ¿Cuántas ventas están en rangos de precios bajos, medios y altos?
GET /sales/_search
{
"size": 0,
"aggs": {
"price_ranges": {
"range": {
"field": "price",
"ranges": [
{ "to": 50, "key": "Low" },
{ "from": 50, "to": 100, "key": "Medium" },
{ "from": 100, "key": "High" }
]
}
}
}
}
⛓️ Anidando Agregaciones: Un Poder Analítico Inmenso
El verdadero poder de las agregaciones radica en la capacidad de anidarlas. Puedes colocar agregaciones de métrica dentro de agregaciones de cubo, o incluso múltiples capas de agregaciones de cubo.
🌍 Ventas por Región y Categoría, con Total y Promedio
Pregunta: Quiero saber el total de ventas y el precio promedio para cada categoría, agrupado por región.
GET /sales/_search
{
"size": 0,
"aggs": {
"sales_by_region": {
"terms": {
"field": "region.keyword",
"size": 5
},
"aggs": {
"sales_by_category": {
"terms": {
"field": "category.keyword",
"size": 5
},
"aggs": {
"total_category_revenue": {
"sum": {
"field": "price"
}
},
"average_category_price": {
"avg": {
"field": "price"
}
}
}
}
}
}
}
}
Análisis de la Estructura:
sales_by_region(Bucket Aggregation -terms): Crea cubos para cadaregion.sales_by_category(Bucket Aggregation -terms): Anidada dentro de cadaregionbucket, crea cubos para cadacategory.total_category_revenue(Metric Aggregation -sum): Anidada dentro de cadacategorybucket, suma los precios de los documentos en ese cubo.average_category_price(Metric Aggregation -avg): Anidada dentro de cadacategorybucket, calcula el promedio de los precios.
Este ejemplo demuestra cómo puedes construir informes complejos y multidimensionales con una sola consulta a Elasticsearch.
🗓️ Ventas por Día y Producto más Vendido
Pregunta: Quiero ver el total de ventas diarias y, para cada día, cuál fue el product_id más vendido.
GET /sales/_search
{
"size": 0,
"aggs": {
"sales_per_day": {
"date_histogram": {
"field": "timestamp",
"calendar_interval": "1d",
"format": "yyyy-MM-dd"
},
"aggs": {
"daily_revenue": {
"sum": {
"field": "price"
}
},
"top_product_daily": {
"terms": {
"field": "product_id.keyword",
"size": 1
},
"aggs": {
"product_revenue": {
"sum": {
"field": "price"
}
}
}
}
}
}
}
}
Aquí estamos anidando un terms aggregation (top_product_daily) dentro de cada bucket de date_histogram (sales_per_day), y dentro de ese terms aggregation, anidamos un sum (product_revenue). Esto nos permite obtener el producto más vendido (limitando size a 1) y sus ventas para cada día.
⚡ Agregaciones de Pipeline (Pipeline Aggregations)
Las agregaciones de pipeline operan sobre la salida de otras agregaciones. Esto permite realizar cálculos que no son posibles directamente con las métricas o cubos básicos, como calcular diferencias, promedios de promedios o mover promedios.
📉 Moving Average (Promedio Móvil)
Calcula un promedio móvil sobre una serie de datos generada por otra agregación (típicamente un date_histogram). Es útil para suavizar datos y ver tendencias.
Pregunta: Calcula el total de ventas diarias y luego el promedio móvil de 3 días para esas ventas.
GET /sales/_search
{
"size": 0,
"aggs": {
"sales_per_day": {
"date_histogram": {
"field": "timestamp",
"calendar_interval": "1d",
"format": "yyyy-MM-dd"
},
"aggs": {
"daily_revenue": {
"sum": {
"field": "price"
}
},
"three_day_moving_avg": {
"moving_fn": {
"buckets_path": "daily_revenue",
"window": 3,
"script": "MovingFunctions.unweightedAvg(values)"
}
}
}
}
}
}
📈 Derivative Aggregation (Derivada)
Calcula la derivada de una métrica en una serie temporal. Esto es útil para ver la tasa de cambio entre intervalos.
Pregunta: ¿Cuál es la tasa de cambio en las ventas diarias?
GET /sales/_search
{
"size": 0,
"aggs": {
"sales_per_day": {
"date_histogram": {
"field": "timestamp",
"calendar_interval": "1d",
"format": "yyyy-MM-dd"
},
"aggs": {
"daily_revenue": {
"sum": {
"field": "price"
}
},
"daily_revenue_derivative": {
"derivative": {
"buckets_path": "daily_revenue"
}
}
}
}
}
}
🔍 Filtrando Agregaciones
Muchas veces, no querrás agregar todos los documentos de tu índice, sino solo un subconjunto. Puedes aplicar filtros a tus agregaciones de varias maneras.
🕵️♀️ Query con Agregaciones
La forma más común es incluir tus agregaciones en una consulta _search regular que ya tiene una cláusula query.
Pregunta: Solo quiero las ventas de la categoría 'Electronics', y luego agruparlas por región.
GET /sales/_search
{
"size": 0,
"query": {
"term": {
"category.keyword": "Electronics"
}
},
"aggs": {
"electronics_sales_by_region": {
"terms": {
"field": "region.keyword"
},
"aggs": {
"total_region_revenue": {
"sum": {
"field": "price"
}
}
}
}
}
}
En este caso, la query (term en category.keyword) se aplica antes de que se ejecuten las agregaciones, lo que significa que solo los documentos de la categoría 'Electronics' serán considerados para la agregación electronics_sales_by_region.
🧱 Filter Aggregation (Agregación de Filtro)
Si necesitas aplicar diferentes filtros a diferentes ramas de tus agregaciones (y no a la consulta global), puedes usar la agregación filter.
Pregunta: Quiero el total de ventas global, pero también el total de ventas solo de la región 'North' y solo de la región 'South' como agregaciones separadas.
GET /sales/_search
{
"size": 0,
"aggs": {
"global_total_sales": {
"sum": {
"field": "price"
}
},
"north_region_sales": {
"filter": {
"term": {
"region.keyword": "North"
}
},
"aggs": {
"total_north_sales": {
"sum": {
"field": "price"
}
}
}
},
"south_region_sales": {
"filter": {
"term": {
"region.keyword": "South"
}
},
"aggs": {
"total_south_sales": {
"sum": {
"field": "price"
}
}
}
}
}
}
Aquí, global_total_sales considera todos los documentos. north_region_sales y south_region_sales aplican sus respectivos filtros antes de calcular la suma, sin afectar a otras agregaciones en el mismo nivel.
🔳 Filters Aggregation (Agregación de Filtros Múltiples)
Si tienes múltiples filtros discretos que quieres aplicar para crear diferentes cubos, filters es más conciso que múltiples filter aggregations.
Pregunta: Agrupa las ventas en cubos para productos 'P001' y 'P002' y para 'Electronics' y 'Books' simultáneamente.
GET /sales/_search
{
"size": 0,
"aggs": {
"custom_filters": {
"filters": {
"filters": {
"product_P001": {
"term": {
"product_id.keyword": "P001"
}
},
"product_P002": {
"term": {
"product_id.keyword": "P002"
}
},
"category_Electronics": {
"term": {
"category.keyword": "Electronics"
}
},
"category_Books": {
"term": {
"category.keyword": "Books"
}
}
}
},
"aggs": {
"total_sales_in_filter": {
"sum": {
"field": "price"
}
}
}
}
}
}
Cada clave dentro de filters.filters (ej. product_P001) se convierte en un bucket independiente, y las sub-agregaciones se aplican a los documentos que pasan ese filtro.
⚙️ Optimizando tus Agregaciones
Las agregaciones pueden ser costosas en términos de recursos, especialmente con grandes volúmenes de datos. Aquí hay algunos consejos para optimizarlas:
- Usa
size: 0: Como mencionamos, si solo necesitas las agregaciones, suprime los hits. - Campos
.keyword: Paratermsy otras agregaciones de cubos basadas en texto, usa siempre el campo.keyword(o mapea el campo comokeyworddirectamente) para evitar la sobrecarga defielddataen campostext. - Reduce el
sizedeterms: Elsizepor defecto es 10. Si solo necesitas los 5 principales, especifica"size": 5. Si necesitas todos los términos únicos, considera la implicación en rendimiento o usacompositeaggregation para paginación si hay muchísimos. precision_thresholdencardinality: Ajusta este parámetro para encontrar un equilibrio entre precisión y uso de memoria para el conteo de valores únicos.- Filtra primero: Si puedes aplicar un filtro general a tu consulta
_search, hazlo. Cuantos menos documentos necesiten procesar las agregaciones, mejor. - Caché de agregaciones: Elasticsearch cachea los resultados de las agregaciones, lo que puede acelerar consultas repetidas. Asegúrate de que tus índices tengan
index.store.preloadconfigurado si es apropiado para tu carga de trabajo.
🌐 Visualización con Kibana y Agregaciones
Kibana, la herramienta de visualización y gestión de Elasticsearch, se basa en gran medida en las agregaciones. Cada gráfico o tabla que creas en Kibana utiliza una o más agregaciones de Elasticsearch para extraer y mostrar los datos.
Ejemplo en Kibana
- Explorar en Discover: Ve a la sección Discover en Kibana. Selecciona tu índice
sales. Verás todos los documentos. - Crear un "Lens" o "Visualize": Navega a Analytics -> Lens o Visualize Library.
- Configurar un gráfico de barras:
- Arrastra el campo
region.keywordal eje X. Kibana automáticamente creará unatermsaggregation y te mostrará el conteo de documentos por región. - Arrastra el campo
priceal eje Y y selecciona "Sum" como métrica. Kibana agregará unsumaggregation anidado dentro de cada bucket de región, mostrando el total de ventas por región.
- Arrastra el campo
Este proceso es una interfaz gráfica para las mismas consultas de agregación que hemos estado construyendo manualmente.
✅ Conclusión
Las agregaciones son el corazón del análisis de datos en Elasticsearch. Desde obtener métricas simples hasta construir complejos informes multidimensionales y series temporales, dominarlas es fundamental para extraer valor de tus datos.
Hemos cubierto los tipos básicos, la poderosa capacidad de anidamiento y algunas agregaciones de pipeline clave. Con esta base, estás listo para empezar a transformar tus datos crudos en inteligencia accionable.
Experimenta con diferentes combinaciones, anidamientos y filtros. ¡El límite es tu imaginación y la estructura de tus datos!
Tutoriales relacionados
- Explorando el Motor de Búsqueda: Cómo Funcionan las Consultas de Texto Completo en Elasticsearchintermediate20 min
- Configurando Aliases e Index Templates en Elasticsearch para la Gestión de Datos Dinámicosintermediate20 min
- Gestionando Snapshots y Restauración en Elasticsearch: Tu Guía Completa de Respaldointermediate18 min
- Aprende a Diseñar y Optimizar Mappings en Elasticsearch para Datos Estructuradosintermediate15 min
- Optimización del Rendimiento en Elasticsearch: Claves para una Ingesta de Datos Eficienteintermediate15 min
Comentarios (0)
Aún no hay comentarios. ¡Sé el primero!