집계(Aggregations) — 버킷·메트릭·파이프라인과 fielddata 함정
Elasticsearch 공식 문서(elastic.co/docs) 및 기술 블로그 종합 — 아키텍처부터 운영까지 (웹 리서치, 2026) — Elastic 공식 문서 + 여러 기술 블로그(Opster, Baeldung 등) 교차 확인
이 모듈을 다 읽으면
- 버킷 집계와 메트릭 집계의 역할 차이를 구분할 수 있다
- text 필드에 바로 집계를 걸 수 없는 이유와 올바른 대응을 설명할 수 있다
- 다단계 집계에서 버킷 수가 폭발적으로 늘어날 수 있는 위험을 판단할 수 있다
SQL의 GROUP BY에 대응하는 버킷 집계, 집계함수에 대응하는 메트릭 집계, 그리고 fielddata/버킷 폭발 같은 실무 함정을 정리한다.
버킷 집계와 메트릭 집계 — SQL GROUP BY와의 대응
메트릭 집계(metric aggregation)는 필드 값들로부터 합계·평균·최댓값 같은 단일 수치를 계산한다(SQL의 SUM/AVG/MAX에 대응). 버킷 집계(bucket aggregation)는 문서를 필드 값·범위·기타 기준으로 여러 '버킷'으로 그룹화한다(SQL의 GROUP BY에 대응) — terms 집계(값별 그룹화), date_histogram(날짜 구간별), range(수치 구간별) 등이 있다. 버킷 집계 안에 메트릭 집계나 또 다른 버킷 집계를 중첩할 수 있어(예: 지역별로 그룹화한 뒤 그 안에서 카테고리별로 다시 그룹화하고 각 조합의 평균 매출을 계산), SQL의 다단계 GROUP BY + 집계함수와 유사한 표현이 가능하다.
핵심 포인트
- 메트릭 집계 = SUM/AVG/MAX 같은 단일 수치 계산, 버킷 집계 = GROUP BY처럼 문서를 그룹으로 나눔
- 버킷 집계 안에 메트릭/버킷 집계를 중첩해 다단계 GROUP BY와 유사한 표현이 가능하다
text 필드에 집계가 막혀 있는 이유 — fielddata
terms 집계처럼 '값별로 그룹화'하려면 각 문서가 그 필드에 대해 어떤 '정확한 값'을 가졌는지 알아야 하는데, text 필드는 analyzer를 거쳐 여러 토큰으로 쪼개져 저장되므로 '원래 값' 자체가 없다 — 'South Korea'가 'south'와 'korea' 두 토큰으로 나뉘어 있으면 집계 결과도 나라 단위가 아니라 토큰 단위로 쪼개진다. 이 때문에 Elasticsearch는 기본적으로 text 필드에 fielddata를 비활성화해 집계를 막아둔다('Fielddata is disabled on text fields by default' 에러). fielddata:true로 강제로 켤 수는 있지만, 이는 역인덱스 구조와 별개로 힙 메모리에 필드의 모든 원문 토큰을 올려야 해서 매우 메모리 집약적이라 권장되지 않는다 — 정석 해법은 애초에 집계용 keyword 서브필드(doc_values 기반, 디스크에 컬럼형으로 저장돼 메모리 부담이 훨씬 적음)를 만들어 그쪽에 집계하는 것이다.
핵심 포인트
- text 필드는 analyzer로 토큰화돼 '원래 값'이 없어 기본적으로 집계(fielddata)가 비활성화되어 있다
- fielddata:true로 강제 활성화 가능하지만 힙 메모리 부담이 커 지양된다
- 정석 해법: keyword 서브필드(doc_values 기반, 디스크 컬럼형 저장)를 만들어 그쪽에 집계한다
다단계 집계의 버킷 폭발
버킷 집계를 여러 단계로 중첩하면(예: 지역 → 카테고리 → 상품 → 월별) 각 단계의 버킷 수가 곱해지는 조합적 폭발이 일어날 수 있다 — 지역 10개 × 카테고리 20개 × 상품 1000개 × 월 12개면 최종 버킷 수가 240만 개에 이를 수 있다. 이런 다차원 집계는 메모리와 응답 시간을 급격히 늘리므로, size 파라미터로 각 단계에서 상위 N개만 보게 제한하거나, 필요한 조합만 걸러내는 필터를 먼저 적용하는 방식으로 버킷 폭발을 통제해야 한다.
핵심 포인트
- 다단계 버킷 집계는 각 단계 버킷 수가 곱해져 조합적으로 폭발할 수 있다
- 각 단계 size 제한이나 사전 필터링으로 버킷 폭발을 통제해야 한다