← Kursa Dön
📄 Text · 30 min

Gerçek Dünya Aggregation Kalıpları

Giriş — Dashboard'ların Arkasındaki Sorgular

Her gün kullandığınız dashboard'ları düşünün. E-ticaret sitesinde sol taraftaki filtreler (kategori, fiyat aralığı, marka, rating), Google Analytics'teki ziyaretçi grafikleri, Kibana'daki log analiz panelleri — bunların hepsi aggregation sorgularının sonucudur.

Bu ders, gerçek dünya senaryolarında en sık kullanılan aggregation kalıplarını gösterecek. Her bir pattern, kopyalayıp kendi projenize uyarlayabileceğiniz tam çalışan örneklerdir. "Aggregation biliyorum ama gerçek projede nasıl kullanacağımı bilmiyorum" diyorsanız, bu ders tam size göre.


Faceted search (yüzlü arama), kullanıcının arama sonuçlarını kategorilere göre daraltmasını sağlar. Amazon, Trendyol, Hepsiburada — hepsinin sol panelindeki filtreler faceted search'tür.

1.1 Test Verisi

PUT products
{
  "mappings": {
    "properties": {
      "name": { "type": "text", "analyzer": "turkish" },
      "category": { "type": "keyword" },
      "brand": { "type": "keyword" },
      "price": { "type": "float" },
      "rating": { "type": "float" },
      "color": { "type": "keyword" },
      "in_stock": { "type": "boolean" },
      "created_at": { "type": "date" }
    }
  }
}

POST products/_bulk
{"index":{}}
{"name":"Samsung Galaxy S24 Ultra","category":"Telefon","brand":"Samsung","price":54999,"rating":4.7,"color":"Siyah","in_stock":true,"created_at":"2024-01-15"}
{"index":{}}
{"name":"iPhone 15 Pro Max","category":"Telefon","brand":"Apple","price":64999,"rating":4.8,"color":"Beyaz","in_stock":true,"created_at":"2024-01-10"}
{"index":{}}
{"name":"Samsung Galaxy Tab S9","category":"Tablet","brand":"Samsung","price":24999,"rating":4.5,"color":"Gri","in_stock":true,"created_at":"2024-02-01"}
{"index":{}}
{"name":"Nike Air Max 270","category":"Ayakkabı","brand":"Nike","price":3499,"rating":4.3,"color":"Siyah","in_stock":true,"created_at":"2024-03-01"}
{"index":{}}
{"name":"Adidas Ultraboost","category":"Ayakkabı","brand":"Adidas","price":4299,"rating":4.6,"color":"Beyaz","in_stock":false,"created_at":"2024-03-15"}
{"index":{}}
{"name":"Samsung Galaxy Buds FE","category":"Kulaklık","brand":"Samsung","price":2499,"rating":4.2,"color":"Siyah","in_stock":true,"created_at":"2024-04-01"}
{"index":{}}
{"name":"Apple AirPods Pro 2","category":"Kulaklık","brand":"Apple","price":7499,"rating":4.9,"color":"Beyaz","in_stock":true,"created_at":"2024-02-15"}
{"index":{}}
{"name":"Sony WH-1000XM5","category":"Kulaklık","brand":"Sony","price":8999,"rating":4.7,"color":"Siyah","in_stock":true,"created_at":"2024-01-20"}

1.2 Faceted Search Sorgusu

GET products/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "name": "samsung" } }
      ]
    }
  },
  "size": 20,
  "aggs": {
    "categories": {
      "terms": { "field": "category", "size": 20 }
    },
    "brands": {
      "terms": { "field": "brand", "size": 30 }
    },
    "price_ranges": {
      "range": {
        "field": "price",
        "ranges": [
          { "key": "0-1000 TL", "to": 1000 },
          { "key": "1000-5000 TL", "from": 1000, "to": 5000 },
          { "key": "5000-10000 TL", "from": 5000, "to": 10000 },
          { "key": "10000-25000 TL", "from": 10000, "to": 25000 },
          { "key": "25000+ TL", "from": 25000 }
        ]
      }
    },
    "rating_ranges": {
      "range": {
        "field": "rating",
        "ranges": [
          { "key": "4+ Yıldız", "from": 4.0 },
          { "key": "3-4 Yıldız", "from": 3.0, "to": 4.0 },
          { "key": "3 Altı", "to": 3.0 }
        ]
      }
    },
    "colors": {
      "terms": { "field": "color", "size": 15 }
    },
    "in_stock_filter": {
      "terms": { "field": "in_stock" }
    },
    "price_stats": {
      "stats": { "field": "price" }
    }
  }
}

Bu tek sorgu şunları döndürür:

  • Ürün listesi (hits)

  • Kategori filtresi (Telefon: 1, Tablet: 1, Kulaklık: 1)

  • Marka filtresi (Samsung: 3)

  • Fiyat aralıkları (bucket'lar ve sayıları)

  • Rating aralıkları

  • Renk filtresi

  • Stok durumu

  • Fiyat istatistikleri (min, max, avg)

1.3 Post-Filter — Facet Sayılarını Korumak

Normal filter aggregation sayılarını etkiler. Kullanıcı "Samsung" markasını seçtiğinde, diğer marka sayıları da değişir — bu genelde istenmez. post_filter ile arama sonuçlarını filtrelerken aggregation'ları etkilemezsiniz:

GET products/_search
{
  "query": {
    "match": { "name": "kulaklık" }
  },
  "post_filter": {
    "term": { "brand": "Samsung" }
  },
  "aggs": {
    "brands": {
      "terms": { "field": "brand", "size": 20 }
    },
    "categories": {
      "terms": { "field": "category", "size": 20 }
    }
  }
}

post_filter yalnızca hits'i etkiler, aggsetkilemez. Böylece:

  • Ürün listesi: Sadece Samsung kulaklıklar

  • Marka facet: Tüm markalar ve sayıları (Samsung: 1, Apple: 1, Sony: 1) — filtreden etkilenmedi!

⚠️ Dikkat: post_filter sadece top-level aggregation'ları korur. Eğer aggregation'ın da filtrelenmesini istiyorsanız, aggregation içinde filter agg kullanın.


2. Time-Series Analytics

Log analizi, monitoring, IoT verisi — zaman serisi verileri Elasticsearch'ün en güçlü olduğu alandır.

2.1 Saatlik/Günlük/Aylık Analiz

GET server_logs/_search
{
  "size": 0,
  "aggs": {
    "hourly": {
      "date_histogram": {
        "field": "timestamp",
        "calendar_interval": "hour",
        "min_doc_count": 0,
        "extended_bounds": {
          "min": "2024-12-01T00:00:00",
          "max": "2024-12-01T23:59:59"
        }
      },
      "aggs": {
        "avg_response_time": { "avg": { "field": "response_time_ms" } },
        "error_count": {
          "filter": {
            "range": { "status_code": { "gte": 500 } }
          }
        },
        "request_count": { "value_count": { "field": "_id" } },
        "percentile_response": {
          "percentiles": {
            "field": "response_time_ms",
            "percents": [50, 90, 95, 99]
          }
        }
      }
    }
  }
}

min_doc_count: 0 ile boş saatler de gösterilir (veri olmasa bile bucket oluşur). extended_bounds tam 24 saatlik aralığı garanti eder.

2.2 Karşılaştırmalı Time Series

Bu hafta ile geçen haftayı karşılaştırmak:

GET server_logs/_search
{
  "size": 0,
  "aggs": {
    "this_week": {
      "filter": {
        "range": {
          "timestamp": {
            "gte": "now-7d/d",
            "lte": "now/d"
          }
        }
      },
      "aggs": {
        "daily": {
          "date_histogram": {
            "field": "timestamp",
            "calendar_interval": "day"
          },
          "aggs": {
            "requests": { "value_count": { "field": "_id" } },
            "avg_latency": { "avg": { "field": "response_time_ms" } }
          }
        }
      }
    },
    "last_week": {
      "filter": {
        "range": {
          "timestamp": {
            "gte": "now-14d/d",
            "lte": "now-7d/d"
          }
        }
      },
      "aggs": {
        "daily": {
          "date_histogram": {
            "field": "timestamp",
            "calendar_interval": "day"
          },
          "aggs": {
            "requests": { "value_count": { "field": "_id" } },
            "avg_latency": { "avg": { "field": "response_time_ms" } }
          }
        }
      }
    }
  }
}

3. Funnel Analysis

Kullanıcı dönüşüm hunisi: Sayfa ziyareti → Ürün görüntüleme → Sepete ekleme → Ödeme → Sipariş tamamlama.

GET user_events/_search
{
  "size": 0,
  "aggs": {
    "funnel": {
      "filters": {
        "filters": {
          "1_page_view": {
            "term": { "event_type": "page_view" }
          },
          "2_product_view": {
            "term": { "event_type": "product_view" }
          },
          "3_add_to_cart": {
            "term": { "event_type": "add_to_cart" }
          },
          "4_checkout": {
            "term": { "event_type": "checkout_start" }
          },
          "5_purchase": {
            "term": { "event_type": "purchase" }
          }
        }
      },
      "aggs": {
        "unique_users": {
          "cardinality": {
            "field": "user_id"
          }
        }
      }
    }
  }
}

Sonuç:

{
  "buckets": {
    "1_page_view": { "doc_count": 50000, "unique_users": { "value": 12000 } },
    "2_product_view": { "doc_count": 25000, "unique_users": { "value": 8000 } },
    "3_add_to_cart": { "doc_count": 5000, "unique_users": { "value": 3000 } },
    "4_checkout": { "doc_count": 2500, "unique_users": { "value": 1500 } },
    "5_purchase": { "doc_count": 1200, "unique_users": { "value": 800 } }
  }
}

Dönüşüm oranları: 12000 ziyaretçi → 800 satın alma = %6.7 dönüşüm oranı. En büyük kayıp "ürün görüntüleme → sepete ekleme" aşamasında.

3.1 Zaman Bazlı Funnel

GET user_events/_search
{
  "size": 0,
  "aggs": {
    "weekly_funnel": {
      "date_histogram": {
        "field": "timestamp",
        "calendar_interval": "week"
      },
      "aggs": {
        "views": {
          "filter": { "term": { "event_type": "product_view" } },
          "aggs": { "users": { "cardinality": { "field": "user_id" } } }
        },
        "carts": {
          "filter": { "term": { "event_type": "add_to_cart" } },
          "aggs": { "users": { "cardinality": { "field": "user_id" } } }
        },
        "purchases": {
          "filter": { "term": { "event_type": "purchase" } },
          "aggs": { "users": { "cardinality": { "field": "user_id" } } }
        }
      }
    }
  }
}

Haftalık funnel: Her haftanın dönüşüm oranını takip edebilirsiniz. Bir kampanya yaptığınız hafta dönüşüm artıyor mu?


4. Top-N Per Category

Her kategorideki en iyi N ürünü bulmak. SQL'deki ROW_NUMBER() OVER (PARTITION BY category) karşılığı.

GET products/_search
{
  "size": 0,
  "aggs": {
    "by_category": {
      "terms": { "field": "category", "size": 10 },
      "aggs": {
        "top_3_products": {
          "top_hits": {
            "size": 3,
            "sort": [{ "rating": "desc" }],
            "_source": ["name", "brand", "price", "rating"]
          }
        },
        "avg_price": { "avg": { "field": "price" } },
        "product_count": { "value_count": { "field": "_id" } }
      }
    }
  }
}

Sonuç yapısı:

Telefon (2 ürün, ort. 59999 TL)
  1. iPhone 15 Pro Max — 4.8 ⭐ — 64999 TL
  2. Samsung Galaxy S24 Ultra — 4.7 ⭐ — 54999 TL

Kulaklık (3 ürün, ort. 6332 TL)
  1. Apple AirPods Pro 2 — 4.9 ⭐ — 7499 TL
  2. Sony WH-1000XM5 — 4.7 ⭐ — 8999 TL
  3. Samsung Galaxy Buds FE — 4.2 ⭐ — 2499 TL

4.1 Top-N with Filters

Her kategoride stokta olan en pahalı 3 ürün:

GET products/_search
{
  "size": 0,
  "aggs": {
    "by_category": {
      "terms": { "field": "category" },
      "aggs": {
        "in_stock_only": {
          "filter": { "term": { "in_stock": true } },
          "aggs": {
            "top_expensive": {
              "top_hits": {
                "size": 3,
                "sort": [{ "price": "desc" }],
                "_source": ["name", "price"]
              }
            }
          }
        }
      }
    }
  }
}

5. Moving Average ve Trend Analizi

5.1 Moving Average

Pipeline aggregation ile hareketli ortalama hesaplamak — trend çizgisi oluşturmak için:

GET orders/_search
{
  "size": 0,
  "aggs": {
    "monthly_sales": {
      "date_histogram": {
        "field": "order_date",
        "calendar_interval": "month"
      },
      "aggs": {
        "total_revenue": { "sum": { "field": "total_amount" } }
      }
    },
    "revenue_moving_avg": {
      "moving_avg": {
        "buckets_path": "monthly_sales>total_revenue",
        "window": 3,
        "model": "simple"
      }
    }
  }
}

⚠️ Not: moving_avg aggregation Elasticsearch 8.x'te deprecated olmuştur. Yerine moving_fn kullanın:

GET orders/_search
{
  "size": 0,
  "aggs": {
    "monthly_sales": {
      "date_histogram": {
        "field": "order_date",
        "calendar_interval": "month"
      },
      "aggs": {
        "total_revenue": {
          "sum": { "field": "total_amount" }
        },
        "revenue_moving_avg": {
          "moving_fn": {
            "buckets_path": "total_revenue",
            "window": 3,
            "script": "MovingFunctions.unweightedAvg(values)"
          }
        },
        "revenue_trend": {
          "moving_fn": {
            "buckets_path": "total_revenue",
            "window": 5,
            "script": "MovingFunctions.linearWeightedAvg(values)"
          }
        }
      }
    }
  }
}

MovingFunctions built-in fonksiyonları:

  • unweightedAvg(values) — Basit hareketli ortalama

  • linearWeightedAvg(values) — Doğrusal ağırlıklı (son değerler daha ağır)

  • ewma(values, alpha) — Üstel ağırlıklı hareketli ortalama

  • holt(values, alpha, beta) — Holt's double exponential smoothing

  • holtWinters(values, alpha, beta, gamma, period, ...) — Mevsimsel trend

5.2 Cumulative Sum — Kümülatif Toplam

GET orders/_search
{
  "size": 0,
  "aggs": {
    "monthly_sales": {
      "date_histogram": {
        "field": "order_date",
        "calendar_interval": "month"
      },
      "aggs": {
        "monthly_revenue": { "sum": { "field": "total_amount" } },
        "cumulative_revenue": {
          "cumulative_sum": {
            "buckets_path": "monthly_revenue"
          }
        }
      }
    }
  }
}

Yıllık hedefi takip etmek için mükemmel: her ay kümülatif gelir artışını görürsünüz.

5.3 Derivative — Değişim Oranı

GET orders/_search
{
  "size": 0,
  "aggs": {
    "monthly_sales": {
      "date_histogram": {
        "field": "order_date",
        "calendar_interval": "month"
      },
      "aggs": {
        "monthly_revenue": { "sum": { "field": "total_amount" } },
        "revenue_change": {
          "derivative": {
            "buckets_path": "monthly_revenue"
          }
        }
      }
    }
  }
}

Önceki aya göre gelir değişimi: pozitif → büyüme, negatif → düşüş.


6. Percentile Monitoring ve SLA

6.1 Response Time Percentile'ları

GET api_logs/_search
{
  "size": 0,
  "aggs": {
    "response_percentiles": {
      "percentiles": {
        "field": "response_time_ms",
        "percents": [50, 75, 90, 95, 99, 99.9]
      }
    },
    "response_ranks": {
      "percentile_ranks": {
        "field": "response_time_ms",
        "values": [100, 200, 500, 1000]
      }
    }
  }
}

percentiles: P50 = 45ms, P95 = 230ms, P99 = 890ms. SLA'nız "P99 < 1000ms" ise, şu an karşılıyorsunuz.

percentile_ranks: 200ms'den hızlı yanıt oranı %88, 500ms'den hızlı %96. Yani isteklerin %96'sı yarım saniyeden kısa sürede yanıtlanıyor.

6.2 SLA Dashboard Verisi

GET api_logs/_search
{
  "size": 0,
  "query": {
    "range": {
      "timestamp": { "gte": "now-24h" }
    }
  },
  "aggs": {
    "hourly_sla": {
      "date_histogram": {
        "field": "timestamp",
        "calendar_interval": "hour"
      },
      "aggs": {
        "total_requests": {
          "value_count": { "field": "_id" }
        },
        "successful_requests": {
          "filter": {
            "range": { "status_code": { "lt": 500 } }
          }
        },
        "error_requests": {
          "filter": {
            "range": { "status_code": { "gte": 500 } }
          }
        },
        "p50_latency": {
          "percentiles": {
            "field": "response_time_ms",
            "percents": [50]
          }
        },
        "p99_latency": {
          "percentiles": {
            "field": "response_time_ms",
            "percents": [99]
          }
        },
        "sla_compliance": {
          "filter": {
            "bool": {
              "must": [
                { "range": { "response_time_ms": { "lte": 1000 } } },
                { "range": { "status_code": { "lt": 500 } } }
              ]
            }
          }
        }
      }
    }
  }
}

Her saat için: toplam istek, başarılı/hatalı istek, P50/P99 latency, SLA uyum oranı. Bir dashboard widget'ı tek bu sorguyla beslenir.

6.3 Uptime Hesaplama

GET health_checks/_search
{
  "size": 0,
  "aggs": {
    "daily_uptime": {
      "date_histogram": {
        "field": "timestamp",
        "calendar_interval": "day"
      },
      "aggs": {
        "total_checks": { "value_count": { "field": "_id" } },
        "successful_checks": {
          "filter": { "term": { "status": "up" } }
        },
        "uptime_pct": {
          "bucket_script": {
            "buckets_path": {
              "success": "successful_checks._count",
              "total": "total_checks"
            },
            "script": "params.total > 0 ? (params.success / params.total) * 100 : 0"
          }
        }
      }
    }
  }
}

bucket_script pipeline aggregation ile yüzde hesaplama. Her gün için uptime yüzdesi: %99.95, %100, %99.87...


7. Gerçek Dünya E-Commerce Dashboard

Tam bir e-ticaret dashboard'unun tüm widget'larını tek sorguda çekelim:

GET orders/_search
{
  "size": 0,
  "query": {
    "range": { "order_date": { "gte": "now-30d" } }
  },
  "aggs": {
    "total_revenue": { "sum": { "field": "total_amount" } },
    "total_orders": { "value_count": { "field": "_id" } },
    "avg_order_value": { "avg": { "field": "total_amount" } },
    "unique_customers": { "cardinality": { "field": "customer_id" } },

    "daily_trend": {
      "date_histogram": {
        "field": "order_date",
        "calendar_interval": "day"
      },
      "aggs": {
        "revenue": { "sum": { "field": "total_amount" } },
        "orders": { "value_count": { "field": "_id" } },
        "moving_avg_revenue": {
          "moving_fn": {
            "buckets_path": "revenue",
            "window": 7,
            "script": "MovingFunctions.unweightedAvg(values)"
          }
        }
      }
    },

    "top_categories": {
      "terms": { "field": "category.keyword", "size": 5 },
      "aggs": {
        "revenue": { "sum": { "field": "total_amount" } },
        "avg_order": { "avg": { "field": "total_amount" } }
      }
    },

    "top_products": {
      "terms": { "field": "product_name.keyword", "size": 10, "order": { "revenue": "desc" } },
      "aggs": {
        "revenue": { "sum": { "field": "total_amount" } },
        "quantity": { "sum": { "field": "quantity" } }
      }
    },

    "order_value_distribution": {
      "histogram": {
        "field": "total_amount",
        "interval": 500,
        "min_doc_count": 1
      }
    },

    "payment_methods": {
      "terms": { "field": "payment_method.keyword" }
    },

    "regional_breakdown": {
      "terms": { "field": "region.keyword", "size": 10 },
      "aggs": {
        "revenue": { "sum": { "field": "total_amount" } },
        "orders": { "value_count": { "field": "_id" } }
      }
    }
  }
}

Bu tek sorgu şu widget'ları besler:

  • KPI Kartları: Toplam gelir, sipariş sayısı, ortalama sipariş değeri, müşteri sayısı

  • Trend Grafiği: Günlük gelir + 7 günlük hareketli ortalama

  • Top Kategoriler: En çok satan 5 kategori

  • Top Ürünler: Gelire göre en iyi 10 ürün

  • Sipariş Dağılımı: Histogram (kaç sipariş hangi fiyat aralığında)

  • Ödeme Yöntemleri: Kredi kartı, havale, kapıda ödeme oranları

  • Bölgesel Dağılım: Şehir/bölge bazında gelir ve sipariş


8. Yaygın Hatalar

Hata 1: Faceted Search'te post_filter Unutmak

// ❌ Marka filtrelenince diğer markaların sayısı 0 olur
{
  "query": { "bool": { "filter": [{ "term": { "brand": "Samsung" } }] } },
  "aggs": { "brands": { "terms": { "field": "brand" } } }
}
// Sadece Samsung gösterilir, Apple: 0, Sony: 0

// ✅ post_filter ile filtrele — agg'lar etkilenmez
{
  "query": { "match_all": {} },
  "post_filter": { "term": { "brand": "Samsung" } },
  "aggs": { "brands": { "terms": { "field": "brand" } } }
}

Hata 2: Pipeline Agg'da Yanlış Path

// ❌ buckets_path yanlış
"moving_fn": { "buckets_path": "monthly_revenue" }
// Eğer "monthly_revenue" başka bir agg'ın altındaysa hata verir

// ✅ Tam path verin: "parent_agg>child_metric"
"moving_fn": { "buckets_path": "monthly_sales>total_revenue" }

Hata 3: min_doc_count Unutmak

// ❌ Veri olmayan zaman dilimleri atlanır — grafikte boşluk
"date_histogram": { "field": "timestamp", "calendar_interval": "hour" }

// ✅ Boş bucket'ları göster
"date_histogram": {
  "field": "timestamp",
  "calendar_interval": "hour",
  "min_doc_count": 0,
  "extended_bounds": {
    "min": "2024-12-01T00:00:00",
    "max": "2024-12-01T23:59:59"
  }
}

9. Best Practices

UygulamaNeden
size: 0 kullanın (hits gerekmiyorsa)Ağ ve bellek tasarrufu
post_filter ile facet sayılarını koruyunKullanıcı deneyimi bozulmasın
min_doc_count: 0 + extended_bounds kullanınZaman serisi grafiklerinde boşluk olmasın
Pipeline agg'ları sibling olarak tanımlayınMetric agg ile aynı seviyede olmalı
bucket_script ile yüzde hesaplayınDönüşüm oranı, uptime gibi metriklerde
Dashboard'ı tek sorguda besleyinAğ çağrısı sayısını minimuma indirin
moving_fn kullanın (moving_avg deprecated)ES 8.x uyumluluğu

Özet

  • Faceted search post_filter ile yapılır — aggregation sayıları korunurken hits filtrelenir

  • Time-series date_histogram + min_doc_count: 0 + extended_bounds ile boşluksuz zaman serisi

  • Funnel analysis filters aggregation + cardinality ile dönüşüm hunisi — drop-off noktaları belirlenir

  • Top-N per category terms + top_hits ile her gruptaki en iyi N öğe

  • Moving average moving_fn + MovingFunctions ile trend analizi

  • Percentile monitoring percentiles + percentile_ranks ile SLA takibi

  • Dashboard tek sorguyla tüm widget verisini çekin — KPI, trend, dağılım, top-N hepsi bir arada