현재 사내 트래픽 데이터 추출 서비스에서 빠른 집계와 검색을 위해 사용자의 로그를 하나하나 문서화하여 ElasticSearch를 사용하고 있습니다.
ElasticSearch를 사용하여 집계된 데이터를 전송해주는 백엔드 API 까지 개발에 들어간 상태인데, 여기서 문제점이 생겼습니다.
데이터에서 중복 제거 된 수치를 집계하여 보여줘야 하는데, ElasticSearch에서 흔히 사용하는 cardinality는 정확한 집계를 보여주지 않습니다.
저는 정확한 데이터를 보내줘야 하기 때문에 성능이 떨어지는 단점이 있지만 scripted_metric 사용해 보고자 합니다.
이를 위해 제가 사용한 집계 쿼리만 따로 보면 다음과 같습니다.
"aggs": {
"distinct count": {
"scripted_metric": {
"init_script": "state.docs=new HashSet()",
"map_script": """
state.docs.add(doc['field'].value)
""",
"combine_script": "return state.docs;",
"reduce_script": """
int count = 0;
for (s in states) {
count += s.size();
}
return count;
"""
}
}
}
쿼리문을 분석하면 다음과 같습니다.
주의 할 점으로 검색한 인덱스 별 중복 제거의 합 혹은 검색한 모든 인덱스에서의 중복 제거에 따라 방식이 달라집니다.
states는 현재 인덱스 별 중복 제거된 HashSet의 집합이라고 볼 수 있습니다.
또한 해당 필드의 정보를 뽑고 싶다면, 아래와 같이 새로운 HashSet(모든 인덱스 상관없이 중복 제거를 원할 경우) 혹은 List(인덱스 별로 중복제거를 원할 경우)에 넣어서 반환하시면 됩니다.
"aggs": {
"distinct count": {
"scripted_metric": {
"init_script": "state.docs=new HashSet()",
"map_script": """
state.docs.add(doc['field'].value)
""",
"combine_script": "return state.docs;",
"reduce_script": """
def all_docs = new HashSet();
for (s in states) {
all_docs.addAll(s);
}
return all_docs;
"""
}
}
}