Aggregations을 수행할 때 사용할 데이터

 

Sub Aggregations

aggregation을 통해 집계를 내고 그 결과에서 세부적인 집계를 위해 Sub Aggregation을 지원하고 있습니다.

 

level 필드별 duration의 평균 시간 구하기

우리는 데이터를 가져오는 것이 목적이 아닌 Aggregations을 수행하는 것이 목적이기 때문에 "size"를 0으로 설정하였습니다.

api POST http://localhost:9200/test-log-index-2021-09-12/_search
header content-type: application/json
body {
    "size": 0,
    "query": {
        "match_all": {}
    },
    "aggs": {
        "byLevel": {
            "terms": {
                "field": "level"
            },
            "aggs": {
                "avgDuration": {
                    "avg": {
                        "field": "duration"
                    }   
                }
            }
        }
    }
}

결과 보기

level의 "info"는 9개가 있으며 9개의 duration 평균은 167.777777입니다.

level의 "warn"는 6개가 있으며 6개의 duration 평균은 333.33333입니다.

level의 "debug"는 5개가 있으며 5개의 duration 평균은 258입니다.

 

level별 action 종류 및 개수 측정하기

우리는 데이터를 가져오는 것이 목적이 아닌 Aggregations을 수행하는 것이 목적이기 때문에 "size"를 0으로 설정하였습니다.

api POST http://localhost:9200/test-log-index-2021-09-12/_search
header content-type: application/json
body {
    "size": 0,
    "query": {
        "match_all": {}
    },
    "aggs": {
        "byLevel": {
            "terms": {
                "field": "level"
            },
            "aggs": {
                "byAction": {
                    "terms": {
                        "field": "action"
                    }   
                }
            }
        }
    }
}

결과 보기

{
    "took": 4,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 20,
            "relation": "eq"
        },
        "max_score": null,
        "hits": []
    },
    "aggregations": {
        "byLevel": {
            "doc_count_error_upper_bound": 0,
            "sum_other_doc_count": 0,
            "buckets": [
                {
                    "key": "info",
                    "doc_count": 9,
                    "byAction": {
                        "doc_count_error_upper_bound": 0,
                        "sum_other_doc_count": 0,
                        "buckets": [
                            {
                                "key": "close_file",
                                "doc_count": 4
                            },
                            {
                                "key": "open_file",
                                "doc_count": 4
                            },
                            {
                                "key": "open_socket",
                                "doc_count": 1
                            }
                        ]
                    }
                },
                {
                    "key": "warn",
                    "doc_count": 6,
                    "byAction": {
                        "doc_count_error_upper_bound": 0,
                        "sum_other_doc_count": 0,
                        "buckets": [
                            {
                                "key": "close_file",
                                "doc_count": 2
                            },
                            {
                                "key": "open_file",
                                "doc_count": 2
                            },
                            {
                                "key": "close_socket",
                                "doc_count": 1
                            },
                            {
                                "key": "open_socket",
                                "doc_count": 1
                            }
                        ]
                    }
                },
                {
                    "key": "debug",
                    "doc_count": 5,
                    "byAction": {
                        "doc_count_error_upper_bound": 0,
                        "sum_other_doc_count": 0,
                        "buckets": [
                            {
                                "key": "close_file",
                                "doc_count": 2
                            },
                            {
                                "key": "open_file",
                                "doc_count": 2
                            },
                            {
                                "key": "close_socket",
                                "doc_count": 1
                            }
                        ]
                    }
                }
            ]
        }
    }
}

"info"는 9개이며 그중에 "close_file"는 4개, "open_file"은 4개, "open_socket"은 1개 입니다.

"warn"는 6개이며 그중에 "close_file"는 2개, "open_file"은 2개, "open_socket"은 1개, "close_socket"은 1개입니다.

"info"는 9개이며 그중에 "close_file"는 2개, "open_file"은 2개, "close_socket"은 1개입니다.

 

 

Aggregation을 수행할 때 사용할 데이터

terms

terms는 데이터의 keyword별 종류 및 개수를 집계하는데 사용합니다.

 

모든 도큐먼트의 level의 종류와 개수 구하기

우리는 데이터를 가져오는 것이 목적이 아닌 Aggregation을 수행하는 것이 목적이기 때문에 "size"를 0으로 설정하였습니다.

level 필드의 데이터 종류와 각 종류별로 도큐먼트가 몇개 있는지 확인합니다.

api POST http://localhost:9200/test-log-index-2021-09-12/_search
header content-type: application/json
body {
    "size": 0,
    "query": {
        "match_all": {}
    },
    "aggs": {
        "byLevel": {
            "terms": {
                "field": "level"
            }
        }
    }
}

결과 보기

"byLevel" 내부의 buckets를 보면 "key"에는 level필드의 값이 "doc_count"에는 개수를 표현하고 있습니다.

즉 "info"는 9개가 있고, "warn"은 6개, "debug"는 5개가 있는 것을 확인할 수 있습니다.

 

range

값의 범위 별로 도큐먼트의 개수를 측정하는데 사용할 수 있습니다.

 

모든 도큐먼트의 duration의 범위별 개수 측정하기

우리는 데이터를 가져오는 것이 목적이 아닌 Aggregations을 수행하는 것이 목적이기 때문에 "size"를 0으로 설정하였습니다.

duration의 범위 100이하, 100부터 200까지, 200부터 300까지, 300부터 400까지, 400이상 으로 범위를 지정하여 검색하겠습니다.

api POST http://localhost:9200/test-log-index-2021-09-12/_search
header content-type: application/json
body {
    "size": 0,
    "query": {
        "match_all": {}
    },
    "aggs": {
        "byDuration": {
            "range": {
                "field": "duration",
                "ranges": [
                    {
                        "to": 100
                    },
                    {
                        "from": 100,
                        "to": 200
                    },
                    {
                        "from": 200,
                        "to": 300
                    },
                    {
                        "from": 300,
                        "to": 400
                    },
                    {
                        "from": 400
                    }
                ]
            }
        }
    }
}

결과 보기

 

histogram

range의 경우 from, to를 통하여 범위를 지정했다면 histogram은 range와 다르게 interval옵션을 통하여 interval단위 별로 필드의 개수를 측정하는 Aggregations입니다.

 

모든 도큐먼트의 duration을 100 단위로 개수 측정하기

우리는 데이터를 가져오는 것이 목적이 아닌 Aggregations을 수행하는 것이 목적이기 때문에 "size"를 0으로 설정하였습니다.

api POST http://localhost:9200/test-log-index-2021-09-12/_search
header content-type: application/json
body {
    "size": 0,
    "query": {
        "match_all": {}
    },
    "aggs": {
        "byDuration": {
            "histogram": {
                "field": "duration",
                "interval": 100
            }
        }
    }
}

결과 보기

100 단위 별로 도큐먼트의 개수를 확인할 수 있습니다.

 

date_range

range처럼 date필드 또한 범위를 지정하여 집계를 할 수 있습니다.

 

모든 도큐먼트의 start_time 필드를 범위 별로 측정하기

우리는 데이터를 가져오는 것이 목적이 아닌 Aggregations을 수행하는 것이 목적이기 때문에 "size"를 0으로 설정하였습니다.

"start_time"필드의 값을 범위 별로 측정하도록 하겠습니다.

api POST http://localhost:9200/test-log-index-2021-09-12/_search
header content-type: application/json
body {
    "size": 0,
    "query": {
        "match_all": {}
    },
    "aggs": {
        "date_range": {
            "date_range": {
                "field": "start_time",
                "ranges": [
                    {
                        "from": "2021-09-12 10:10:10",
                        "to": "2021-09-12 10:10:15"
                    },
                    {
                        "from": "2021-09-12 10:10:15",
                        "to": "2021-09-12 10:10:20"
                    },
                    {
                        "from": "2021-09-12 10:10:20"
                    }
                ]
            }
        }
    }
}

결과 보기

각 시간대별로 도큐먼트의 개수를 확인할 수 있습니다.

 

date_histogram

histogram과 같이 date_histogram도 interval 옵션을 넣어 각 interval별 집계를 측정할 수 있습니다.

interval옵션에 second, minute, hour, day, month, week 등을 넣을 수 있습니다.

 

모든 도큐먼트의 start_time 필드를 1분 별로 측정하기

우리는 데이터를 가져오는 것이 목적이 아닌 Aggregations을 수행하는 것이 목적이기 때문에 "size"를 0으로 설정하였습니다.

api POST http://localhost:9200/test-log-index-2021-09-12/_search
header content-type: application/json
body {
    "size": 0,
    "query": {
        "match_all": {}
    },
    "aggs": {
        "date_his": {
            "date_histogram": {
                "field": "start_time",
                "interval": "minute"
            }
        }
    }
}

결과 보기

 

 

 

 

Aggregation을 수행할 때 사용할 데이터

 

cardinality

cardinality는 데이터의 종류가 몇 가지 있는지 확인하는 데 사용됩니다.

 

user_id가 1인 도큐먼트에 action이 몇가지 종류가 있는지 확인하기

우리는 데이터를 가져오는 것이 목적이 아닌 Aggregations을 수행하는 것이 목적이기 때문에 "size"를 0으로 설정하였습니다.

api POST http://localhost:9200/test-log-index-2021-09-12/_search
header content-type: application/json
body {
    "size": 0,
    "query": {
        "bool": {
            "must": [
                {
                    "match": {
                        "user_id": "1"
                    }
                }
            ]
        }
    },
    "aggs": {
        "cardinality_by_action": {
            "cardinality": {
                "field": "action"
            }
        }
    }
}

결과 보기

"cardinality_by_action"를 통해 user_id가 1인 도큐먼트에서는 action의 종류가 4개가 있다는 것을 확인할 수 있습니다.

실제로 "open_file", "close_file", "open_socket", "close_socket" 이렇게 4개입니다.

 

percentiles

각각의 값을 백분율로 보는 Aggregations입니다. 퍼센트 옵션을 설정하지 않으면 디폴트로 [1, 5, 25, 50, 75, 95, 99]로 설정됩니다.

 

user_id가 1인 도큐먼트에 duration의 백분율 확인하기

우리는 데이터를 가져오는 것이 목적이 아닌 Aggregations을 수행하는 것이 목적이기 때문에 "size"를 0으로 설정하였습니다.

api POST http://localhost:9200/test-log-index-2021-09-12/_search
header content-type: application/json
body {
    "size": 0,
    "query": {
        "bool": {
            "must": [
                {
                    "match": {
                        "user_id": "1"
                    }
                }
            ]
        }
    },
    "aggs": {
        "percentiles_by_duration": {
            "percentiles": {
                "field": "duration"
            }
        }
    }
}

결과 보기

하위 1%의 값은 100이며, 상위 99%의 값은 430인 것을 확인할 수 있습니다.

 

퍼센트를 설정하여 확인하기

이제는 디폴트 퍼센트값이 아닌 퍼센트를 사용자가 [25, 50, 75]로 지정하여 검색하겠습니다.

api POST http://localhost:9200/test-log-index-2021-09-12/_search
header content-type: application/json
body {
    "size": 0,
    "query": {
        "bool": {
            "must": [
                {
                    "match": {
                        "user_id": "1"
                    }
                }
            ]
        }
    },
    "aggs": {
        "percentiles_by_duration": {
            "percentiles": {
                "field": "duration",
                "percents": [ 25, 50, 75 ]
            }
        }
    }
}

결과 보기

 

percentile_ranks

지정한 값이 백분율로 몇 퍼센트 안에 속하는지 확인할 수 있습니다.

values라는 옵션으로 값을 지정할 수 있습니다.

 

user_id가 1인 도큐먼트에 필드 duration에 100, 200, 300의 값이 있다면 백분율 몇 퍼센트인지 확인하기

우리는 데이터를 가져오는 것이 목적이 아닌 Aggregations을 수행하는 것이 목적이기 때문에 "size"를 0으로 설정하였습니다.

api POST http://localhost:9200/test-log-index-2021-09-12/_search
header content-type: application/json
body {
    "size": 0,
    "query": {
        "bool": {
            "must": [
                {
                    "match": {
                        "user_id": "1"
                    }
                }
            ]
        }
    },
    "aggs": {
        "rank_by_duration": {
            "percentile_ranks": {
                "field": "duration",
                "values": [ 100, 200, 300 ]
            }
        }
    }
}

결과 보기

 

Aggregation을 수행할 때 사용할 데이터

Aggregations이란

저장된 데이터를 수치화시키는 여러 가지의 연산을 수행하는 역할을 합니다. 이러한 기능을 통하여 데이터를 분석하여 사용자가 보기 좋게 시각화를 할 수 있습니다.

 

이번 페이지에서는 min, max, sum, avg, stats에 대하여 알아보겠습니다.

min

query의 검색 결과에 특정 필드의 값이 가장 작은 값을 나타냅니다.

 

user_id가 1인 도큐먼트의 duration 필드의 최솟값 구하기

"size"를 0으로 설정한 이유는 우리는 데이터를 가져오는 것이 목적이 아닌 user_id가 1인 도큐먼트에서 duration이 가장 작은 값만 도출하는 것이 목적이기 때문에 불필요하게 데이터를 가져오는 것을 생략하기 위함입니다.

"aggs"를 통하여 쿼리에 Aggregation을 선언하고 "minDuration"이라는 Aggregation의 이름을 사용자가 지정합니다. 그리고 "min"을 통하여 duration이라는 필드의 최솟값을 측정하도록 합니다.

api POST http://localhost:9200/test-log-index-2021-09-12/_search
header content-type: application/json
body {
    "size": 0,
    "query": {
        "bool": {
            "must": [
                {
                    "match": {
                        "user_id": "1"
                    }
                }
            ]
        }
    },
    "aggs": {
        "minDuration": {
            "min": {
                "field": "duration"
            }
        }
    }
}

결과 보기

"hits"는 검색 결과를 나타내지만 우리는 "size"를 0으로 설정하였기 때문에 결과값은 나오지 않습니다.

"aggregations"에 우리가 지정한 "minDuration"이라는 명칭으로 최소값이 100이라는 결과가 나왔습니다.

 

max

query의 검색 결과에 특정 필드의 값이 가장 큰 값을 나타냅니다.

 

user_id가 1인 도큐먼트의 duration 필드의 최대값 구하기

"maxDuration"이라는 명칭으로 duration의 값이 가장 큰 값을 구하는 쿼리입니다.

api POST http://localhost:9200/test-log-index-2021-09-12/_search
header content-type: application/json
body {
    "size": 0,
    "query": {
        "bool": {
            "must": [
                {
                    "match": {
                        "user_id": "1"
                    }
                }
            ]
        }
    },
    "aggs": {
        "maxDuration": {
            "max": {
                "field": "duration"
            }
        }
    }
}

결과 보기

maxDuration을 통하여 user_id가 1인 도큐먼트에서 duration이 가장 큰 값은 430이라는 결과를 알 수 있습니다.

 

sum

query의 검색 결과에 특정 필드의 값을 전부 더한 값을 나타냅니다.

 

user_id가 1인 도큐먼트의 duration 필드의 전부 더한 값 구하기

"sumDuration"이라는 명칭으로 user_id가 1인 duration의 값을 전부 더한 값을 구하는 쿼리입니다.

api POST http://localhost:9200/test-log-index-2021-09-12/_search
header content-type: application/json
body {
    "size": 0,
    "query": {
        "bool": {
            "must": [
                {
                    "match": {
                        "user_id": "1"
                    }
                }
            ]
        }
    },
    "aggs": {
        "sumDuration": {
            "sum": {
                "field": "duration"
            }
        }
    }
}

결과 보기

sumDuration을 통하여 user_id가 1인 도큐먼트에서 duration을 전부 더한 값이 2600이라는 것을 알 수 있습니다.

 

avg

query의 검색 결과에 특정 필드의 값의 평균 값을 나타냅니다.

 

user_id가 1인 도큐먼트의 duration 필드의 전부 더한 값 구하기

"avgDuration"이라는 명칭으로 user_id가 1인 duration의 값의 평균을 구하는 쿼리입니다.

api POST http://localhost:9200/test-log-index-2021-09-12/_search
header content-type: application/json
body {
    "size": 0,
    "query": {
        "bool": {
            "must": [
                {
                    "match": {
                        "user_id": "1"
                    }
                }
            ]
        }
    },
    "aggs": {
        "avgDuration": {
            "avg": {
                "field": "duration"
            }
        }
    }
}

결과 보기

avgDuration을 통하여 user_id가 1인 도큐먼트에서 duration의 평균값이 216.66666666이라는 것을 알 수 있습니다.

 

stats

stats를 통하여 위에서 본 count, min, max, sum, avg의 모든 결과를 가져올 수 있습니다.

 

user_id가 1인 도큐먼트의 duration 필드의 stats 결과 값 확인하기

"statsDuration"이라는 명칭으로 user_id가 1인 도큐먼트의 count, min, max, sum, avg의 값을 확인하는 쿼리입니다.

api POST http://localhost:9200/test-log-index-2021-09-12/_search
header content-type: application/json
body {
    "size": 0,
    "query": {
        "bool": {
            "must": [
                {
                    "match": {
                        "user_id": "1"
                    }
                }
            ]
        }
    },
    "aggs": {
        "statDuration": {
            "stats": {
                "field": "duration"
            }
        }
    }
}

결과 보기

"statDuration"을 통해 count, min, max, avg, sum의 결과 값을 확인할 수 있습니다.

 

그렇다면 min만 필요하더라도 stats하나로 모든 결과 값을 구하면 된다고 생각할 수 있습니다. 그러나 이러한 생각은 좋지 못한 생각입니다. min만 구하기 위해서 stats를 사용하는 것은 매우 안 좋은 선택입니다. 이 이유는 Elasticsearch에게 그만큼 많은 부하를 줄 수 있기 때문입니다. 그러나 반대로 min, max, sum을 구해야 될 경우에는 min, max, sum 각각 3번의 검색을 수행하는 것보다는 stats를 한 번을 사용하여 네트워크 I/O를 줄여 주는 것이 더 현명한 선택이라고 생각됩니다.

 

 

Keyword 

Elasticsearch에는 문자열 필드 중 text와 keyword가 존재합니다. Keyword는 키워드라는 표현 그대로 데이터 자체를 분류하는데 사용하는 타입입니다. 즉 검색 필터로 사용되며, 데이터를 정렬하고 Aggregation으로 데이터를 집계하는데 사용되는 타입입니다. Keyword 타입으로 "hello my elasticsearch"라는 데이터가 색인되면 "hello my elasticsearch" 그대로 데이터가 색인되어 검색 시 "hello my elasticsearch" 그대로 데이터를 검색해야 합니다.

 

Text

Text 필드는 지정한 분석기를 이용하여 데이터를 분석하는데 사용합니다. 만약 Analyzer를 지정하지 않으면 Standard Analyzer로 설정되는데 "hello my elasticsearch"라는 데이터가 색인되면 "hello", "my", "elasticsearch"로 데이터가 색인되어 keyword와 다르게 "hello"라고 검색을 해도 검색이 됩니다. 즉 유사한 문자열을 검사하거나 Analyzer 설정을 통해 보다 더 전문적인 검색을 수행하는데 사용하는 타입입니다.

 

정리

간단하게 정리하면 다음과 같습니다.

  Keyword Text
사용 목적 통계, 정렬, 필터링 유사성 및 전문적인 분석
검색 방식 입력된 값과 정확히 일치하는 문자를 검색 설정한 Analyzer에 따라서 검색
색인 방식 입력한 문자열 그대로 색인 토큰으로 분리되어 색인
Aggregation  색인된 데이터로 집계 가능 집계 불가능

 

Keyword와 Text 타입을 둘다 적용

Text처럼 분석도 필요하지만 Keyword처럼 Aggregation 모두 필요할 때 필드의 타입을 Keyword와 Text 둘 다 적용할 수 있습니다. 사실 별 다른 타입을 지정하지 않으면 기본 값으로 Keyword와 Text 타입 둘 다 적용됩니다. 이런 경우 문득 다방면으로 사용할 수 있을 것 같아 좋아 보일 수 있지만 보다 더 많은 리소스를 사용하기 때문에 사용 목적에 맞게 필드 타입을 지정하여 사용하는 것을 권장합니다.

+ Recent posts