1. 엘라스틱 서치 쿼리에대해 간단하게 정리하고자 한다.
기본적으로 아래와 같은 형태를 띄고 있다.
- query: 기존 DB의 where 조건문에 해당 (query, filter)
- size: 0으로 설정할경우 결과는 보지않고 집계결과만 보겠다는 의미
- aggs: 집계 부분 기존 쿼리의 Group by에 해당
GET _search
{
"query":{
},
"size":0,
"aggs": {
}
}
집계 정리
1. SELECT count(*) FROM test group by userNo
- 유저 번호로 그룹핑한 결과를 보고 싶을때
{
"size":0,
"aggs": {
"user_no (집계 대상 이름)" : {
"terms": {
"field": "user.no"
}
}
}
}
결과
"aggregations" : {
"aa" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : 0,
"doc_count" : 66
},
{
"key" : 107,
"doc_count" : 14
}
...
이하 생략
2. userNo 값으로 집계한 결과를 대상으로 특정 url로 집계하고 싶을때
{
"size":0,
"aggs": {
"aa" : {
"terms": {
"field": "user.no"
},
"aggs": {
"url_name": {
"terms": {
"field": "url.keyword"
}
}
}
}
}
}
결과
"aggregations" : {
"user_no" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : 0, # 유저별 그룹핑
"doc_count" : 664745,
"url" : {
"doc_count_error_upper_bound" : 94,
"sum_other_doc_count" : 4659,
"buckets" : [ # url별 그룹핑
{
"key" : "http://aaa.com",
"doc_count" : 655
},
{
"key" : "https://test.com/",
"doc_count" : 469
},
{
.. 이하생략
3. 일별, 사용자별로 그룹핑 한다면? (2번은 집계한 결과의 집계, 3번과차이가 있음)
{
"size":0,
"aggs": {
"abnormal_status" : {
"composite": {
"sources": [
# time을 일별로 묶어줌, calendar_interval = day, week, hour..
{ "date": { "date_histogram": { "field": "time", "calendar_interval": "day", "format": "yyyy-MM-dd"}}},
{ "name": { "terms": { "field": "userNo" } } }
]
}
}
}
}
결과
"aggregations" : {
"abnormal_status" : {
"after_key" : {
"date" : "2022-07-14",
"name" : 2
},
"buckets" : [
{
"key" : {
"date" : "2022-01-03",
"name" : 0
},
"doc_count" : 1
},
{
"key" : {
"date" : "2022-05-30",
"name" : 0
},
"doc_count" : 1
}
... 이하생략
4. count(distinct column) > 사용자별로 (그룹핑) 몇개의 ip를 가지고있는가 (count(distint))
{
"size":0,
"aggs": {
"user" : {
"terms": {
"field": "userNo"
},
"aggs": {
"ip": {
"cardinality": {
"field": "ip"
}
}
}
}
}
}
결과
"aggregations" : {
"multi_ip_top_n" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : 0,
"doc_count" : 45,
"ip_cardinality" : {
"value" : 3
}
},
{
"key" : 2,
"doc_count" : 9,
"ip_cardinality" : {
"value" : 2
}
},
{
"key" : 25,
"doc_count" : 5,
"ip_cardinality" : {
"value" : 2
}
},
.. 이하 생략
5. sum, avg, min, max > status
status를 사용할경우 앞의 4가지를 전부 구할 수 있다.
"size":0,
"aggs": {
"user_no" : {
"terms": {
"field": "user.no"
},
"aggs": {
"sum": {
"sum": {
"field": "queryCnt"
}
},
"avg": {
"avg": {
"field": "queryCnt"
}
},
"min": {
"min": {
"field": "queryCnt"
}
},
"max": {
"max": {
"field": "queryCnt"
}
}
}
}
}
}
{
"query":{
"bool":{
"filter": [
{ "range": { "reqTime": { "gte": 20210623000000, "lte": 20220723235959 }}}
]
}
},
"size":0,
"aggs": {
"user_no" : {
"terms": {
"field": "user.no"
},
"aggs": {
"stats": {
"stats": {
"field": "queryCnt"
}
}
}
}
}
}
집계시 기억해야 할 점
엘라스틱 서치에서 제공하는 기본 집계의 수는 10000건이다. 이 이상으로 설정할 수 있지만 권장하지 않는다하고, 따라서 이에 맞게 결과를 도출하려 해야한다.
그럼에도 불구하고 10000건이상, 혹은 전체를 불러오고 싶을경우?(ex 페이징처리가 필요하다)
composite집계를 이용하여 처리할 수 있다.
앞선 composite 집계 결과를 보면 다음과 같이 after_key를 포함하고있다. 집계시 해당 키값을 넣어주게 되면 해당 키 다음부터 조회할 수 있다.
이런 방법으로 전체를 조회하거나 원하는 페이지를 조회할 수 있다.
"aggregations" : {
"abnormal_status" : {
"after_key" : {
"date" : "2022-07-14",
"name" : 2
},
"buckets" : [
{
"key" : {
"date" : "2022-01-03",
"name" : 0
},
"doc_count" : 1
},
{
"key" : {
"date" : "2022-05-30",
"name" : 0
},
"doc_count" : 1
}
... 이하생략
{
"size":0,
"aggs": {
"abnormal_status" : {
"composite": {
"sources": [
# time을 일별로 묶어줌, calendar_interval = day, week, hour..
{ "date": { "date_histogram": { "field": "time", "calendar_interval": "day", "format": "yyyy-MM-dd"}}},
{ "name": { "terms": { "field": "userNo" } } }
],
"size": 10000,
"after": #해당부분에 afterKey값을 넣어주면 그 위치부터 시작함
}
}
}
}
※ 이외에 알아둘만한 집계 함수
percentiles > 해당 퍼센트에 해당하는 기준값이 얼마인지 알 수 있다.
"percentiles_bucket" : {
"values" : {
"75.0" : 75,
"80.0" : 80,
"85.0" : 85,
"90.0" : 90,
"95.0" : 95,
"99.0" : 99
}
}
script사용
아래와 같이 집계중 script를 사용할 수 있는 집계들이 있다.
아래 집계는 time 필드를 HH형태로 바꾸어 집계한 결과이다.
ex 20220805124333 > 12
"aggs": {
"hourly_privacy_count" : {
"terms": {
"script": {"source": "doc['time'].value.getHour()"}
}
}