포스트

집계(Aggregation): 버킷 집계 vs 메트릭 집계

집계(Aggregation): 버킷 집계 vs 메트릭 집계

검색 엔진인데 통계도 잘한다

Elasticsearch를 검색용으로만 쓰다가, “카테고리별 게시글 수”, “지역별 평균 가격” 같은 통계/분석이 필요해졌습니다. SQL의 GROUP BY + 집계함수에 해당하는 게 Elasticsearch의 Aggregation입니다. 크게 버킷 집계와 메트릭 집계 두 종류를 이해하면 됩니다.

두 집계의 관계

flowchart TD
    A["전체 문서"] --> B["버킷 집계<br/>(문서를 그룹으로 나눔)"]
    B --> C1["버킷: 서울"]
    B --> C2["버킷: 부산"]
    C1 --> M1["메트릭 집계<br/>avg(price), count"]
    C2 --> M2["메트릭 집계<br/>avg(price), count"]
  • 버킷(bucket) 집계: 문서를 기준에 따라 그룹(버킷)으로 나눔 = SQL의 GROUP BY.
  • 메트릭(metric) 집계: 숫자 값을 계산(합/평균/최대/유니크 수 등) = SQL의 SUM, AVG, COUNT.
  • 보통 버킷으로 나눈 뒤, 각 버킷 안에서 메트릭을 계산하는 식으로 중첩합니다.

메트릭 집계 예시

1
2
3
4
5
6
7
8
GET /orders/_search
{
  "size": 0,
  "aggs": {
    "avg_price": { "avg": { "field": "price" } },
    "max_price": { "max": { "field": "price" } }
  }
}

"size": 0은 검색 결과 문서는 필요 없고 집계만 받겠다는 뜻입니다.

Cardinality — 유니크한 값의 수

“순 방문자 수”, “중복 제거한 상품 종류 수”처럼 유니크 개수가 필요하면 cardinality를 씁니다(SQL COUNT(DISTINCT)).

1
2
3
4
5
6
{
  "size": 0,
  "aggs": {
    "unique_users": { "cardinality": { "field": "user_id" } }
  }
}

cardinality는 정확한 값이 아니라 근사치(HyperLogLog 기반)입니다. 대규모에서 메모리를 아끼려는 설계라, 약간의 오차를 허용한다는 점을 알고 써야 합니다.

버킷 집계 예시 (+ 메트릭 중첩)

지역별로 나누고(버킷), 각 지역의 평균 가격(메트릭)을 구합니다.

1
2
3
4
5
6
7
8
9
10
11
12
GET /orders/_search
{
  "size": 0,
  "aggs": {
    "by_region": {
      "terms": { "field": "region" },          // 버킷: region별 그룹
      "aggs": {
        "avg_price": { "avg": { "field": "price" } }  // 각 버킷 내 메트릭
      }
    }
  }
}

terms(범주별), date_histogram(기간별), range(구간별) 등이 대표적인 버킷 집계입니다.

집계 대상 필드 주의

집계는 text가 아니라 keyword(또는 숫자/날짜) 필드에 해야 합니다. text는 분석되어 토큰으로 쪼개져 있어 그룹 기준으로 부적합합니다(그리고 기본적으로 막혀 있습니다). 그래서 집계용 필드는 keyword로 매핑하세요. (역색인 글의 text/keyword 참고)

정리

  • 버킷 집계 = GROUP BY(그룹 나누기), 메트릭 집계 = 합/평균/유니크 등 계산.
  • 보통 버킷 → 메트릭 중첩으로 “그룹별 통계”를 만든다.
  • 유니크 수는 cardinality(근사치)임을 기억.
  • 집계는 keyword/숫자/날짜 필드에. text는 부적합.
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.