-
ES - Aggregations🔍 elastic search 2022. 9. 22. 17:23
Aggregations
Elasticsearch는 검색엔진 뿐 아니라 로그 분석 을 비롯한 다양한 목적의 데이터 시스템으로 사용되고 있다.
이렇게 활용이 가능한 이유는 데이터를 단순히 검색할 뿐 아니라 여러가지 연산을 할 수 있는 Aggregation 기능이 있기 때문이다.
Kibana 에서 바 차트, 파이 차트 등으로 데이터를 시각화 할 수 있는데 여기서 Aggregation 기능을 사용
aggregation은 번역하면 “집계” 라는 뜻이지만, ES에서는 원문대로 aggregation 혹은 애그리게이션으로 많이 표현.
크게 세 종류
- Metrics Aggregations
- Bucket Aggregations
- Pipeline Aggregations
Metrics Aggregations
필드 값에서 합계 또는 평균과 같은 메트릭을 계산하는 aggregation
- ex) min, max, sum, avg, stats, cardinality 등등
min, max, sum, avg
최소, 최대, 합, 평균 값을 가져오는 aggregation
- 가장 흔하게 사용되는 metrics aggregation
Example - sum
POST /sales/_search?size=0 { "query": { "constant_score": { "filter": { "match": { "type": "hat" } } } }, "aggs": { "hat_prices": { "sum": { "field": "price" } } } } // curl curl -X POST "localhost:9200/sales/_search?size=0&pretty" -H 'Content-Type: application/json' -d' { "query": { "constant_score": { "filter": { "match": { "type": "hat" } } } }, "aggs": { "hat_prices": { "sum": { "field": "price" } } } } ' // response { ... "aggregations": { "hat_prices": { "value": 450.0 } } }
- sales 에 있는 hat 필드값의 hat_prices 값의 합을 가져옴.
Stats
min, max, sum, avg 값을 모두 가져와야 한다면 stats aggregation을 사용하면 위 4개의 값 모두와 count 값을 한번에 가져온다.
Example
// grade 필드의 min, max, sum, avg 값을 가져오는 aggs POST /exams/_search?size=0 { "aggs": { "grades_stats": { "stats": { "field": "grade" } } } } // curl curl -X POST "localhost:9200/exams/_search?size=0&pretty" -H 'Content-Type: application/json' -d' { "aggs": { "grades_stats": { "stats": { "field": "grade" } } } } ' // response { ... "aggregations": { "grades_stats": { "count": 2, "min": 50.0, "max": 100.0, "avg": 75.0, "sum": 150.0 } } }
Aggregation 결과만 보고 싶다면 ❓
기본적으로 Aggregation을 포함하는 검색은 aggregation 및 검색 결과를 모두 반환한다.
- 이때, aggregation 결과만 보고 싶다면 “size” : 0 을 추가하면 된다.
GET /students/_search { "size": 0, "aggs": ... }
cardinality
필드의 값이 모두 몇 종류인지 분포값을 알고 싶을 때 사용
일반적으로 Text 필드에서는 사용할 수 없고
- 숫자
- Keyword
- ip
필드 등에 사용이 가능하다.
사용자 접속 로그에서 IP 주소 필드를 가지고 실제 접속한 사용자가 몇명인지 파악하는 등의 용도로 주로 사용된다.
Example
// type 필드 가 몇 종류인지 가져오는 aggs POST /sales/_search?size=0 { "aggs": { "type_count": { "cardinality": { "field": "type" } } } } // curl curl -X POST "localhost:9200/sales/_search?size=0&pretty" -H 'Content-Type: application/json' -d' { "aggs": { "type_count": { "cardinality": { "field": "type" } } } } ' // response { ... "aggregations": { "type_count": { "value": 3 } } }
Bucket Aggregations
주어진 조건으로 분류된 버킷들을 만들고 각 버킷에 소속되는 도큐먼트들을 모아 그룹으로 구분
- 각 버킷 별로 포함되는 도큐먼트의 개수가 doc_count 값에 기본적으로 표시된다.
- 각 버킷 안에 metrics aggregation을 이용한 다른 계산도 가능하다.
range
숫자 필드 값으로 범위를 지정하고 각 범위에 해당하는 버킷을 만드는 aggregation
Example
// price 필드의 값을 range aggs를 이용해 버킷으로 구분 GET sales/_search { "aggs": { "price_ranges": { "range": { "field": "price", "ranges": [ { "to": 100.0 }, { "from": 100.0, "to": 200.0 }, { "from": 200.0 } ] } } } } // curl curl -X GET "localhost:9200/sales/_search?pretty" -H 'Content-Type: application/json' -d' { "aggs": { "price_ranges": { "range": { "field": "price", "ranges": [ { "to": 100.0 }, { "from": 100.0, "to": 200.0 }, { "from": 200.0 } ] } } } } ' // response { ... "aggregations": { "price_ranges": { "buckets": [ { "key": "*-100.0", "to": 100.0, "doc_count": 2 }, { "key": "100.0-200.0", "from": 100.0, "to": 200.0, "doc_count": 2 }, { "key": "200.0-*", "from": 200.0, "doc_count": 3 } ] } } }
histogram
range 와 동일하게 숫자 필드의 범위를 나누는 aggregation
range는 from / to 를 이용해 각 버킷의 범위를 지정하지만,
histogram은 interval 옵션 을 이용해서 주어진 간격 크기대로 버킷을 구분한다.
Example
// price 필드의 값을 histogram aggs를 이용해 버킷으로 구분 POST /sales/_search?size=0 { "aggs": { "prices": { "histogram": { "field": "price", "interval": 50 } } } } // curl curl -X POST "localhost:9200/sales/_search?size=0&pretty" -H 'Content-Type: application/json' -d' { "aggs": { "prices": { "histogram": { "field": "price", "interval": 50 } } } } ' // response { ... "aggregations": { "prices": { "buckets": [ { "key": 0.0, "doc_count": 1 }, { "key": 50.0, "doc_count": 1 }, { "key": 100.0, "doc_count": 0 }, { "key": 150.0, "doc_count": 2 }, { "key": 200.0, "doc_count": 3 } ] } } }
terms
keyword 필드의 문자열 별로 버킷을 나누는 aggregation
Example
// genre 값에 따라 버킷 생성 GET /_search { "aggs": { "genres": { "terms": { "field": "genre" } } } } // curl curl -X GET "localhost:9200/_search?pretty" -H 'Content-Type: application/json' -d' { "aggs": { "genres": { "terms": { "field": "genre" } } } } ' // response { ... "aggregations": { "genres": { "doc_count_error_upper_bound": 0, "sum_other_doc_count": 0, "buckets": [ { "key": "electronic", "doc_count": 6 }, { "key": "rock", "doc_count": 3 }, { "key": "jazz", "doc_count": 2 } ] } } }
⚠️ text 필드로 terms aggregation을 사용했을 경우 ⚠️
⇒ 오류가 발생 💣
- 텍스트 필드는 aggregation 및 정렬과 같은 문서별 필드 데이터가 필요한 작업에 최적화 ❌
- 기본적으로 이러한 작업은 실행 중지된다.
- 대신 키워드 필드를 사용
즉, 텍스트 필드는 terms 로 나누어서 색인되기 때문에 버킷을 나누기에 적당하지 않다.
- 입력된 문자열을 하나의 토큰으로 저장하는 키워드 필드를 사용해야 한다!!
Pipeline Aggregations
다른 metrics aggregation의 결과를 새로운 입력으로 하는 pipeline aggregation
다른 버킷의 결과들을 다시 연산
- min_bucket
- max_bucket
- avg_bucket
- sum_bucket
- stats_bucket
- moving_avg - 이동 평균 구하기
- derivative - 미분 값 구하기
- cumulative_sum - 누적 합 구하기
Pipeline aggregation은 “buckets_path”: “<버킷 이름>” 옵션을 이용해 사용할 버킷을 입력 값으로 지정한다.
Example
// price 의 값을 입력으로 받는 cumulative_sum aggs 실행 POST /sales/_search { "size": 0, "aggs": { "sales_per_month": { "date_histogram": { "field": "date", "calendar_interval": "month" }, "aggs": { "sales": { "sum": { "field": "price" } }, "cumulative_sales": { "cumulative_sum": { "buckets_path": "sales" } } } } } } // curl curl -X POST "localhost:9200/sales/_search?pretty" -H 'Content-Type: application/json' -d' { "size": 0, "aggs": { "sales_per_month": { "date_histogram": { "field": "date", "calendar_interval": "month" }, "aggs": { "sales": { "sum": { "field": "price" } }, "cumulative_sales": { "cumulative_sum": { "buckets_path": "sales" } } } } } } ' // response { "took": 11, "timed_out": false, "_shards": ..., "hits": ..., "aggregations": { "sales_per_month": { "buckets": [ { "key_as_string": "2015/01/01 00:00:00", "key": 1420070400000, "doc_count": 3, "sales": { "value": 550.0 }, "cumulative_sales": { "value": 550.0 } }, { "key_as_string": "2015/02/01 00:00:00", "key": 1422748800000, "doc_count": 2, "sales": { "value": 60.0 }, "cumulative_sales": { "value": 610.0 } }, { "key_as_string": "2015/03/01 00:00:00", "key": 1425168000000, "doc_count": 2, "sales": { "value": 375.0 }, "cumulative_sales": { "value": 985.0 } } ] } } }
Sub Aggregation
Bucket Aggregation으로 만든 버킷들 내부에 다시 “aggs” : {} 을 선언해
또 다른 버킷을 만들거나 Metrics Aggregation을 만들어 사용하는 aggregation
Example
// 이미 만든 stations 버킷별로 avg aggs을 이용해 passangers 필드의 평균값 계산 GET my_stations/_search { "size": 0, "aggs": { "stations": { "terms": { "field": "station.keyword" }, "aggs": { "avg_psg_per_st": { "avg": { "field": "passangers" } } } } } } // response { "took" : 3, "timed_out" : false, "_shards" : { "total" : 1, "successful" : 1, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : { "value" : 10, "relation" : "eq" }, "max_score" : null, "hits" : [ ] }, "aggregations" : { "stations" : { "doc_count_error_upper_bound" : 0, "sum_other_doc_count" : 0, "buckets" : [ { "key" : "강남", "doc_count" : 5, "avg_psg_per_st" : { "value" : 5931.2 } }, { "key" : "불광", "doc_count" : 1, "avg_psg_per_st" : { "value" : 971.0 } }, { "key" : "신촌", "doc_count" : 1, "avg_psg_per_st" : { "value" : 3912.0 } }, { "key" : "양재", "doc_count" : 1, "avg_psg_per_st" : { "value" : 4121.0 } }, { "key" : "종각", "doc_count" : 1, "avg_psg_per_st" : { "value" : 2314.0 } }, { "key" : "홍제", "doc_count" : 1, "avg_psg_per_st" : { "value" : 1021.0 } } ] } } }
주의 ❗
- 하위 버킷이 깊어질수록 ES 가 하는 작업량과 메모리 소모량이 기하급수적으로 늘어남
- → 예상치 못한 오류를 발생
- 보통은 2레벨의 깊이 이상의 버킷은 생성하지 않는 것이 좋다.
참고자료
https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations.html
'🔍 elastic search' 카테고리의 다른 글
Elasticsearch + Springboot (0) 2022.09.27 ES - 텍스트 분석 (0) 2022.09.22 ES - 데이터 검색 (1) 2022.09.21 ES - 기본 API (0) 2022.09.21 Docker로 ES 설치하기 (1) 2022.09.21