Elasticsearch 입문 18편 Pipeline Aggregations. derivative·moving_avg·cumulative·bucket_script·bucket_sort.
이 글은 Elasticsearch 입문에서 운영까지 시리즈 38편 중 18편이에요. 16편(Metric) 과 17편(Bucket) 에서 숫자 한 개를 계산 하고 문서를 그룹으로 묶는 두 갈래를 다뤘다면, 이번 18편은 그 둘이 이미 만들어 놓은 결과 위에서 한 번 더 굴리는 자리예요. Pipeline Aggregations — 시계열 분석·이동 평균·누적 합·버킷 정렬·필터링 같은 2차 가공 을 한 요청 안에서 끝내는 무기.
이 글은 Elasticsearch 8.x 공식 docs 의 Pipeline aggregations 챕터를 한국어 학습 노트로 풀어쓴 자료예요.
date_histogram 위에 derivative·moving_fn·cumulative_sum 을 직접 얹어 보면 *어떤 자리에서 어떤 결과가 나오는지* 가 머리에 훨씬 잘 박혀요.
Pipeline Aggregations 가 자리잡는 곳
Metric 과 Bucket 만으로도 일별 매출 합 이나 카테고리별 평균 가격 같은 1차 통계는 다 잡혀요. 그런데 운영 화면이 진짜 원하는 숫자는 1차 통계의 변화 인 경우가 많아요. "오늘 매출이 어제 대비 얼마 늘었나", "7일 이동 평균이 30일 이동 평균을 위로 뚫었나", "누적 가입자가 100만을 언제 넘었나", "버킷 100개 중 매출 상위 10개만 보여 줘" — 이런 자리.
전통적으로는 클라이언트가 1차 집계 응답을 받아서 자바·Python 코드로 한 번 더 굴리거나, Kibana TSVB 같은 시각화 도구에 위임하거나, 별도 OLAP 로 넘기는 식이었어요. Pipeline Aggregations 는 그 후처리 를 ES 안으로 끌고 들어와요. 한 요청에 date_histogram → avg → derivative → moving_avg 까지 체인으로 묶을 수 있으니, 백엔드 코드는 응답을 그대로 화면에 그리기만 하면 돼요.
핵심 특성을 한 줄로 압축하면 — Pipeline agg 는 문서를 직접 읽지 않아요. 이미 계산된 다른 agg 의 결과 를 입력으로 받아 한 번 더 굴리는 후처리 노드 예요. 그래서 모든 pipeline agg 의 핵심 파라미터가 buckets_path — 어느 agg 결과를 입력으로 쓸지 가리키는 경로 입니다.
Pipeline 의 두 갈래 — Parent vs Sibling
Pipeline aggregations 는 어디에 매달려서 어디 결과를 보느냐 로 두 종류로 나뉘어요. 시작부터 이 둘을 구분해서 잡아 두는 게 buckets_path 사고 의 절반을 미리 막아 줘요.
| 분류 | 위치 | 입력 | 출력 | 대표 agg |
|---|---|---|---|---|
| Parent | 부모 agg 안에 | 부모의 각 버킷 결과 | 부모의 각 버킷에 새 값 추가 | derivative · moving_fn · cumulative_sum · bucket_script · bucket_selector · bucket_sort · serial_diff |
| Sibling | 부모 agg 옆에 | 부모 전체 버킷 결과 | 한 개의 새 결과 | avg_bucket · sum_bucket · min_bucket · max_bucket · stats_bucket · percentiles_bucket · extended_stats_bucket |
Parent 는 각 버킷마다 새 숫자를 한 개씩 만들어요. 예 — 일별 매출 30개 버킷이 있으면 일별 매출 변화량 30개 를 만들어서 같은 자리에 박아 줘요.
Sibling 은 전체 버킷을 한 번에 요약한 한 개의 숫자 를 만들어요. 예 — 일별 매출 30개 버킷 옆에 30일 평균 한 개를 추가로 만들어요.
GET /orders/_search
{
"size": 0,
"aggs": {
"sales_per_day": {
"date_histogram": { "field": "ordered_at", "calendar_interval": "day" },
"aggs": {
"daily_revenue": { "sum": { "field": "amount" } },
"revenue_delta": {
"derivative": { "buckets_path": "daily_revenue" }
}
}
},
"avg_daily_revenue": {
"avg_bucket": { "buckets_path": "sales_per_day>daily_revenue" }
}
}
}
이 예제 하나가 모든 자리를 보여 줘요. revenue_delta 는 Parent — sales_per_day 안에 들어가서 각 버킷에 변화량을 박아요. avg_daily_revenue 는 Sibling — sales_per_day 옆에 나란히 서서 30일 평균 한 개를 만들어요. > 는 경로 구분자 예요. sales_per_day>daily_revenue 는 sales_per_day 안의 daily_revenue 를 의미 합니다.
derivative — 변화량 계산
derivative (도함수) 는 연속된 두 버킷 사이의 차이 를 돌려 줘요. 시계열 분석의 출발점.
"aggs": {
"sales_per_day": {
"date_histogram": { "field": "ordered_at", "calendar_interval": "day" },
"aggs": {
"daily_revenue": { "sum": { "field": "amount" } },
"revenue_delta": {
"derivative": { "buckets_path": "daily_revenue" }
},
"revenue_delta_2nd": {
"derivative": { "buckets_path": "revenue_delta" }
}
}
}
}
응답은 이런 모양.
{
"buckets": [
{ "key_as_string": "2026-05-01", "daily_revenue": { "value": 1000000 } },
{ "key_as_string": "2026-05-02", "daily_revenue": { "value": 1200000 },
"revenue_delta": { "value": 200000 } },
{ "key_as_string": "2026-05-03", "daily_revenue": { "value": 1100000 },
"revenue_delta": { "value": -100000 },
"revenue_delta_2nd": { "value": -300000 } }
]
}
첫 번째 버킷에는 비교할 직전 버킷 이 없어서 revenue_delta 가 없어요. 그래서 클라이언트는 첫 버킷 누락 을 항상 처리할 준비를 해야 해요. 2차 미분(revenue_delta_2nd) 까지 보면 가속도 — 매출 증가 속도가 빨라지는지 느려지는지 도 잡혀요.
unit 파라미터로 단위 시간당 변화량 도 계산돼요. 예 — "unit": "hour" 를 주면 day 단위로 묶은 버킷이라도 시간당 변화량으로 환산 해 줘요. SLO 모니터링에서 "시간당 에러 건수" 자리에 자주 등장합니다.
"revenue_delta_per_hour": {
"derivative": {
"buckets_path": "daily_revenue",
"unit": "hour"
}
}
moving_avg · moving_fn — 이동 평균
moving_fn 은 최근 N개 버킷 위에 임의의 스크립트를 굴리는 도구예요. 가장 흔한 자리가 이동 평균 — 최근 7일 평균 매출 같은 자리.
여기서 한 번 잡고 갈 게 있어요. 옛날에 moving_avg 라는 전용 agg 가 있었는데 ES 6.4 부터 deprecated 됐고, ES 8.x 에서는 moving_fn 으로 모두 통일됐어요. 검색하다 보면 옛 문서에 moving_avg 가 그대로 남아 있는데, 신규 코드는 무조건 moving_fn — 잘못 베끼면 deprecation warning 폭주 하거나 8.x 에서 동작 안 합니다.
"aggs": {
"sales_per_day": {
"date_histogram": { "field": "ordered_at", "calendar_interval": "day" },
"aggs": {
"daily_revenue": { "sum": { "field": "amount" } },
"ma_7d": {
"moving_fn": {
"buckets_path": "daily_revenue",
"window": 7,
"shift": 0,
"script": "MovingFunctions.unweightedAvg(values)"
}
}
}
}
}
핵심 파라미터 4개.
buckets_path— 어느 metric 을 입력으로 쓸지.window— 몇 개 버킷을 묶을지.window: 7이면 최근 7개 버킷.shift— 현재 버킷을 윈도우에 포함할지 조절. 기본 0 — 현재 버킷 이전 까지만.shift: 1을 주면 현재 포함.script— 윈도우 위에서 굴릴 함수.
미리 만들어 둔 MovingFunctions 정적 헬퍼가 있어요. 자주 쓰는 셋.
MovingFunctions.unweightedAvg(values) // 단순 평균
MovingFunctions.linearWeightedAvg(values) // 최근 값에 가중치
MovingFunctions.ewma(values, alpha) // 지수 가중 (Exponential Weighted)
MovingFunctions.holt(values, alpha, beta) // 추세 반영 (level + trend)
MovingFunctions.holtWinters(values, alpha, beta, gamma, period, pad, multiplicative) // 추세 + 계절성
MovingFunctions.stdDev(values, avg) // 표준편차
MovingFunctions.sum(values) // 합
MovingFunctions.min(values) // 최솟값
MovingFunctions.max(values) // 최댓값
선택 기준은 데이터 특성 으로 정해요. 완만한 시계열 이면 unweightedAvg, 최근 값이 더 중요 하면 ewma 또는 linearWeightedAvg, 추세가 있는 매출·DAU 면 holt, 주간 계절성이 있는 (e.g. 주말이 평일보다 큼) 자리면 holtWinters — 이 순서가 거친 가이드예요.
"ma_ewma": {
"moving_fn": {
"buckets_path": "daily_revenue",
"window": 14,
"script": "MovingFunctions.ewma(values, 0.3)"
}
}
window 가 클수록 부드러워지지만 변화 감지가 늦어져요. 자유롭게 직접 스크립트도 짤 수 있어서 최근 7일 중 평일만 평균 같은 커스텀도 가능 — 다만 painless 스크립트라 디버깅이 까다로워 자주 만지는 자리는 아니에요.
cumulative_sum · cumulative_cardinality — 누적
cumulative_sum 은 지금 버킷까지의 누적 합 을 돌려 줘요. 누적 매출·누적 가입자·누적 다운로드 같은 자리.
"aggs": {
"users_per_day": {
"date_histogram": { "field": "joined_at", "calendar_interval": "day" },
"aggs": {
"daily_signup": { "value_count": { "field": "user_id" } },
"total_users": {
"cumulative_sum": { "buckets_path": "daily_signup" }
}
}
}
}
응답을 그려 보면 매일의 가입자 옆에 지금까지 총합 이 한 칸씩 늘면서 같이 박혀요. 클라이언트는 그래프 두 줄 을 한 응답으로 같이 그릴 수 있는 거예요.
cumulative_cardinality 는 누적 cardinality 를 돌려 주는 변종이에요. 이게 실용 가치가 큰 자리가 하나 있는데 — 새로 들어온 유저 수 추적이에요.
"aggs": {
"events_per_day": {
"date_histogram": { "field": "event_at", "calendar_interval": "day" },
"aggs": {
"users_so_far": {
"cardinality": { "field": "user_id" }
},
"total_unique_users": {
"cumulative_cardinality": { "buckets_path": "users_so_far" }
},
"new_users_today": {
"derivative": { "buckets_path": "total_unique_users" }
}
}
}
}
cardinality 는 그 버킷 안의 unique 유저 수 인데, cumulative_cardinality 는 처음부터 그 버킷까지의 총 unique 수 를 돌려 줘요. 그 위에 derivative 를 얹으면 — 그날 처음 본 신규 유저 수 가 나와요. retention·growth 분석의 핵심 한 줄.
다만 cumulative_cardinality 는 내부적으로 HyperLogLog 상태 를 버킷마다 들고 다녀야 해서 메모리 부담이 큽니다. 30편(Monitoring) 에서 다시 언급.
bucket_script · bucket_selector — 계산식·필터링
bucket_script 는 한 버킷 안의 metric 들을 변수로 받아 직접 계산식을 짜는 자리예요. SQL 로 치면 SELECT col_a / col_b AS ratio 같은 자리.
"aggs": {
"sales_per_day": {
"date_histogram": { "field": "ordered_at", "calendar_interval": "day" },
"aggs": {
"total_revenue": { "sum": { "field": "amount" } },
"total_cost": { "sum": { "field": "cost" } },
"profit_margin": {
"bucket_script": {
"buckets_path": {
"rev": "total_revenue",
"cost": "total_cost"
},
"script": "(params.rev - params.cost) / params.rev * 100"
}
}
}
}
}
buckets_path 가 map 으로 바뀐 게 핵심이에요. 여러 metric 을 변수로 받아서 painless 스크립트 안에서 params.rev, params.cost 로 꺼내 쓰는 식. 변환율·이익률·CTR·ROAS 같이 비율 이 들어가는 자리 거의 다.
bucket_selector 는 조건을 만족하는 버킷만 남기는 필터 예요. SQL 의 HAVING 자리.
"aggs": {
"categories": {
"terms": { "field": "category", "size": 1000 },
"aggs": {
"total_revenue": { "sum": { "field": "amount" } },
"min_50m": {
"bucket_selector": {
"buckets_path": { "rev": "total_revenue" },
"script": "params.rev > 50000000"
}
}
}
}
}
카테고리 매출이 5천만 원 넘는 것만 으로 잘라 줘요. terms agg 의 size 로는 상위 N개 만 가능한데, bucket_selector 를 쓰면 조건 기반 필터 가 가능해져요.
다만 주의 — bucket_selector 는 집계가 끝난 뒤 에 필터링이라, terms agg 가 size 1000 으로 1000개 버킷을 만든 다음 그중 일부만 남기는 흐름이에요. terms agg 의 size 가 작으면 애초에 큰 버킷이 잘려서 selector 가 뒤늦게 도착해도 의미 없을 수 있어요. 이게 terms + bucket_selector 사고 의 단골 원인.
bucket_sort + percentiles_bucket · stats_bucket — 정렬·전체 통계
bucket_sort 는 버킷을 metric 기준으로 정렬하고 잘라내는 parent pipeline 이에요. terms agg 의 정렬은 count 또는 서브 metric 한 개 만 가능한데, bucket_sort 는 여러 metric 조합·복합 정렬 까지 됩니다.
"aggs": {
"categories": {
"terms": { "field": "category", "size": 1000 },
"aggs": {
"total_revenue": { "sum": { "field": "amount" } },
"avg_price": { "avg": { "field": "amount" } },
"top10_by_revenue": {
"bucket_sort": {
"sort": [{ "total_revenue": { "order": "desc" } }],
"size": 10,
"from": 0
}
}
}
}
}
전체 카테고리 1000개를 매출 내림차순으로 정렬한 뒤 상위 10개만 돌려 주는 자리. 페이지네이션도 from + size 로 가능해서 집계 결과 페이지네이션 의 표준 패턴이에요. from 을 키워서 2페이지·3페이지 도 가능 — 다만 deep pagination 함정은 search 와 같아서 수천 단위 from 은 피해야 해요.
percentiles_bucket 과 stats_bucket 은 sibling 으로, 전체 버킷의 분포 한 줄 을 만들어 줘요.
"aggs": {
"sales_per_day": {
"date_histogram": { "field": "ordered_at", "calendar_interval": "day" },
"aggs": {
"daily_revenue": { "sum": { "field": "amount" } }
}
},
"daily_revenue_percentiles": {
"percentiles_bucket": {
"buckets_path": "sales_per_day>daily_revenue",
"percents": [25, 50, 75, 95, 99]
}
},
"daily_revenue_stats": {
"stats_bucket": {
"buckets_path": "sales_per_day>daily_revenue"
}
}
}
percentiles_bucket 은 일별 매출 30개의 p25·p50·p75·p95·p99 를 돌려 줘요. "평소 어느 정도 매출이 나오나" 의 분포를 한 응답에 같이 받기. 다만 — percentiles (metric agg) 의 T-Digest 와 다르게 percentiles_bucket 은 입력 버킷 갯수가 적으면 정확도가 낮아요. 30개 버킷이면 어느 정도 OK, 7개 버킷이면 큰 의미 없어요.
stats_bucket 은 min · max · avg · sum · count 다섯 개를 한 번에 — 자주 쓰는 sibling 정리 자리. extended_stats_bucket 을 쓰면 표준편차·variance·sum_of_squares 까지 받아요.
serial_diff · extended_stats_bucket — 차분·통계
serial_diff 는 lag 만큼 떨어진 두 버킷의 차이 를 돌려 줘요. derivative 가 바로 직전 버킷 과의 차이라면, serial_diff 는 N개 떨어진 버킷 과의 차이 — 주간 비교·월간 비교 자리.
"sales_per_day": {
"date_histogram": { "field": "ordered_at", "calendar_interval": "day" },
"aggs": {
"daily_revenue": { "sum": { "field": "amount" } },
"wow_change": {
"serial_diff": {
"buckets_path": "daily_revenue",
"lag": 7
}
}
}
}
오늘 매출 − 7일 전 매출 — Week over Week (WoW) 변화량 이 한 줄로 잡혀요. lag: 30 이면 Month over Month (MoM) 변화량. 마케팅·BI 대시보드의 KPI 카드 자리에 자주 등장.
lag 를 생략하면 1 로 잡혀서 derivative 와 거의 같아져요. 그래서 lag 를 명시 하지 않으면 의도가 흐려져요 — 자주 만나는 코드 리뷰 지적.
extended_stats_bucket 은 전체 버킷 분포의 자세한 통계 를 sibling 으로 돌려 줘요.
"daily_revenue_extended": {
"extended_stats_bucket": {
"buckets_path": "sales_per_day>daily_revenue",
"sigma": 2
}
}
응답에 avg · min · max · sum · count · std_deviation · std_deviation_bounds(upper·lower) · variance · sum_of_squares 까지 들어가요. 이상치 탐지 (avg ± 2·sigma 밖) 자리에 자주 써요.
자주 만나는 사고
사고 1 — buckets_path 경로 오타
원인 — buckets_path 가 string 이라 ES 가 컴파일 단계에서 잡아 주지 못해요. daily_revennue (오타) 라고 박아도 400 에러 X, 그냥 결과가 비어서 옴 — 또는 NaN 으로 옴.
해결 — > 와 metric 이름 을 frontmatter 처럼 상수로 추출 해서 한 곳에서 관리해요. sales_per_day>daily_revenue 같은 경로는 코드의 enum 으로 잡아 두는 게 운영 안전성에서 큰 차이. 직접 손으로 친 경로는 반드시 Kibana Dev Tools 에서 한 번 테스트.
사고 2 — moving_avg deprecated 후 moving_fn 미전환
원인 — ES 5~6 시절 코드를 8.x 로 옮기면서 moving_avg 가 그대로 남아 있으면 deprecation warning 으로 시작해서, ES 9.x 또는 strict 모드 매니지드 서비스 에서는 400 에러 로 거절돼요.
해결 — moving_avg 의 model: simple 은 MovingFunctions.unweightedAvg(values), linear 는 linearWeightedAvg(values), ewma 는 ewma(values, alpha), holt 는 holt(values, alpha, beta), holt_winters 는 holtWinters(...) — 변환 표를 보고 한 번에 옮겨요. 옛 글·옛 Q&A 를 그대로 베끼지 않는 게 중요.
사고 3 — bucket_script 의 script 폭증
원인 — bucket_script 가 버킷 갯수만큼 스크립트를 컴파일·실행해서, terms agg 의 size 가 10,000 이면 스크립트 10,000번 호출 이에요. painless 캐시 가 막혀 있거나 식이 복잡하면 집계 자체보다 script overhead 가 더 비싸지는 자리.
해결 — 식을 단순하게 유지, painless 가 아닌 순수 expression script 로 바꾸기 어려우면 terms agg 의 size 를 작게 — 또는 bucket_selector 로 미리 filter 해서 script 실행 횟수를 줄이는 패턴.
사고 4 — percentile_bucket 정확도 착각
원인 — percentiles (metric) 의 T-Digest 가 수백만 문서 위에서도 p99 가 ±1% 안에 들어오는 것에 익숙해진 사람이, percentiles_bucket 도 비슷한 정확도로 기대해요. 실제로는 입력 버킷 갯수 = 표본 크기 라, 7개 버킷 위의 p99 같은 건 통계적으로 의미 없는 숫자가 나옵니다.
해결 — percentiles_bucket 은 최소 30~50개 버킷 이상에서만 쓰는 게 안전해요. 주간 데이터 4개 버킷의 p95 같은 자리는 percentiles_bucket 대신 사이드 metric agg 로 풀거나 그냥 min/max 표시.
사고 5 — serial_diff lag 미설정
원인 — serial_diff 가 lag 기본값이 1 이라, 주간 비교 의도로 박았는데 일간 비교 로 동작해서 모든 KPI 카드가 derivative 와 똑같이 나옴. 코드 리뷰에서 "왜 이 카드만 따로 있어요" 로 잡히는 경우.
해결 — 무조건 lag 명시. 주간 = 7, 월간 = 30, 분기 = 90 — 의도가 글로 적혀 있도록.
사고 6 — bucket_selector 가 terms size 보다 늦게 도착
원인 — terms agg 가 상위 size 개 만 가져온 뒤에 bucket_selector 가 동작해요. 그래서 상위 10개 만 가져온 뒤 매출 1억 이상만 남겨 라고 박으면, 진짜 1억 넘는 카테고리가 11번째에 있었어도 이미 잘려서 보이지 않아요.
해결 — bucket_selector 와 함께 쓰는 terms agg 는 size 를 충분히 키워 두거나(예 1,000~10,000), 카테고리 갯수가 정해져 있으면 size 를 전체 갯수 로 잡아요. terms agg 의 정확도 vs 비용 trade-off 는 17편(Bucket) 에서.
사고 7 — pipeline agg 결과를 다시 Bucket 의 정렬 키로 쓰려는 시도
원인 — "매출 derivative 가 큰 순으로 카테고리 정렬" 을 하려고 terms agg 의 order 에 pipeline 결과 를 박으면 — 작동 안 함. terms 의 order 는 metric agg 결과 만 허용해요.
해결 — bucket_sort 를 부모 agg 안에 Parent pipeline 으로 넣어서 정렬해요. terms 가 일단 size 1000 으로 다 모은 뒤 → bucket_sort 가 derivative 기준으로 정렬·자르기 — 이 두 단계 분리가 표준.
운영 권장 패턴
운영 대시보드 자리에서는 date_histogram + metric + derivative + moving_fn 4단 콤보를 템플릿 처럼 굳혀 두는 게 좋아요. 일별 값·전일 대비 변화·7일 이동 평균 세 줄이 한 응답에 같이 와서, 프론트는 그냥 차트만 그리면 됨.
이상치 탐지는 extended_stats_bucket 의 std_deviation_bounds 를 쓰는 패턴이 script 안 짜고 한 줄로 끝 나서 깔끔해요. sigma: 2 로 박아 두고, 일별 값이 upper · lower 밖을 벗어나면 알람 으로 묶는 식.
성능을 신경 써야 하면 — pipeline agg 는 coordinator 노드 메모리·CPU 를 소비해요. 데이터 노드에서 분산되어 돌지 않고 coordinator 한 곳 에 모인 결과 위에서 돌기 때문. 그래서 버킷 수가 수만 단위로 폭증하는 자리에서는 pipeline agg 를 피하거나 bucket_selector 로 미리 잘라 줘야 안전합니다.
Painless 스크립트는 작게 유지. moving_fn 의 script 가 복잡해지면 moving_fn 빼고 metric 결과만 받아 클라이언트 코드로 처리 하는 게 디버깅·운영 측면에서 오히려 깔끔할 때가 많아요. 집계 안에서 모든 걸 끝내려는 욕심 이 사고의 시작.
Kibana Lens·TSVB 의 Time Series Visual Builder 가 내부적으로 pipeline agg 를 자동 생성해 줘요. 직접 짜기 전에 Kibana 에서 한 번 만들어 보고 생성된 쿼리 를 그대로 복사해 오면 오타·deprecated 사고 가 거의 안 납니다. 33편(Kibana·ELK) 에서 깊이.
시험 직전 한 번 더 — 압축 노트
- Pipeline agg = 다른 agg 결과를 입력으로 받는 2차 가공. 문서를 직접 안 봄.
- 모든 핵심 파라미터 =
buckets_path(경로 string,>가 구분자). - 두 갈래: Parent (부모 안, 버킷마다 한 개) vs Sibling (부모 옆, 전체에 한 개).
- derivative = 직전 버킷과의 차이. 첫 버킷은 없음.
unit으로 단위 시간당 환산. - moving_avg ⇒ ES 6.4 deprecated, 8.x 는 moving_fn.
MovingFunctions.unweightedAvg·linearWeightedAvg·ewma·holt·holtWinters·stdDev. window크기 = 부드러움 vs 변화 감지 속도 trade-off.- cumulative_sum = 누적 합. cumulative_cardinality = 누적 unique. 그 위 derivative = 신규 유저 수.
- bucket_script = 버킷 안 metric 들로 계산식.
buckets_path가 map. - bucket_selector = SQL
HAVING. termssize미리 키워야 함. - bucket_sort = parent pipeline, 복합 정렬 +
from·size페이지네이션. - percentiles_bucket = 전체 버킷 분포 분위수. 30~50개 버킷 이상에서만 의미.
- stats_bucket · extended_stats_bucket = sibling 통계.
extended= 표준편차·variance. - serial_diff = lag 만큼 떨어진 두 버킷 차이. 주간·월간 비교. lag 명시 필수.
- 7대 사고 = buckets_path 오타 · moving_avg 미전환 · bucket_script 폭증 · percentile_bucket 정확도 · serial_diff lag · bucket_selector vs terms size · pipeline 결과 정렬.
- pipeline agg = coordinator 메모리 소비. 수만 버킷 위에서는 신중.
- 시계열 4단 콤보 = date_histogram + metric + derivative + moving_fn — 운영 표준.
시리즈 다른 편
- 이전 글 = 17편 Aggregations Bucket — terms·date_histogram·range·filters
- 이전 글 = 16편 Aggregations Metric — sum·avg·percentiles·cardinality
- 15편 = Compound Queries — bool·dis_max·function_score
- 14편 = Term-level Queries — term·terms·range·exists
- 13편 = Full-text Queries — match·multi_match·match_phrase
- 12편 = Search API — query·filter·source·sort 기본
- 19편 = Search Features — pagination·search_after·highlighting
- 20편 = Suggesters — term·phrase·completion
- 21편 = Vector Search — dense_vector·kNN·HNSW
- 22편 = RAG — semantic search·hybrid·rerank
- 33편 = Kibana·ELK — TSVB·Lens·Dashboards
- 38편 = 시리즈 마무리 — 결정 트리·체크리스트·자격증
한 줄 정리 — Pipeline Aggregations = 이미 만들어진 다른 agg 결과 위에서 한 번 더 굴리는 2차 가공. Parent (버킷마다 한 개) vs Sibling (전체에 한 개) 두 갈래, 핵심은 buckets_path. 시계열은 date_histogram + metric + derivative + moving_fn 4단 콤보가 운영 표준이에요.