Terms 집계
Terms 집계
terms 집계는 필드의 각 고유 term에 대해 동적으로 버킷을 만들어요.
출처: 문서
본문
파라미터 (Parameters)
terms 집계는 다음 파라미터를 받아요.
| 파라미터 | 필수/선택 | 데이터 타입 | 설명 |
|---|---|---|---|
| field | 선택 | String | 집계할 필드. keyword, numeric, ip, boolean 또는 date 필드여야 해요. field 또는 script 중 하나는 필수예요. |
| script | 선택 | Object | 집계할 값을 생성하는 스크립트. field 또는 script 중 하나는 필수예요. field와 함께 사용하면 스크립트는 값 스크립트로 동작하며 필드 값을 _value로 받아요. |
| size | 선택 | Integer | 반환할 버킷 수. 기본값은 10이에요. |
| shard_size | 선택 | Integer | 각 샤드에서 수집되는 후보 term 수. 값이 높을수록 정확도가 향상돼요. 기본값은 size * 1.5 + 10이에요. |
| min_doc_count | 선택 | Integer | 버킷이 응답에 포함되기 위한 최소 문서 수. 기본값은 1이에요. |
| shard_min_doc_count | 선택 | Integer | term이 후보가 되기 위한 샤드 레벨의 최소 문서 수. 기본값은 0이에요. |
| show_term_doc_count_error | 선택 | Boolean | true면 버킷별 오차 추정치를 포함해요. 기본값은 false예요. |
| order | 선택 | Object | 버킷의 정렬 순서를 제어해요. _count, _key 또는 하위 집계 메트릭의 이름을 각각 asc 또는 desc와 함께 받아요. 기본값은 {"_count": "desc"}예요. |
| include | 선택 | String, Array 또는 Object | 어떤 term 값이 버킷을 만들 수 있는지 필터링해요. regex 문자열, 정확한 값의 배열 또는 partition 객체를 받아요. |
| exclude | 선택 | String 또는 Array | 어떤 term 값이 버킷을 만들 수 없는지 필터링해요. regex 문자열 또는 정확한 값의 배열을 받아요. |
| missing | 선택 | String 또는 Number | 대상 필드가 없는 문서에 사용할 값으로, 해당 버킷에 배치돼요. 기본적으로 누락된 문서는 무시돼요. |
| execution_hint | 선택 | String | term이 수집되는 방식을 제어해요. 유효한 값은 map(값을 직접 메모리에 보관)과 global_ordinals(ordinals 매핑 사용, 카디널리티가 높은 필드에서 더 메모리 효율적)예요. OpenSearch가 최선의 옵션을 자동으로 선택해요. |
| collect_mode | 선택 | String | 중첩 집계가 계산되는 방식을 제어해요. 유효한 값은 다음과 같아요: |
| - depth_first: 가지치기 전에 모든 분기를 확장. | |||
| - breadth_first: 확장 전에 각 레벨에서 가지치기를 수행해 깊게 중첩된 집계의 메모리를 줄임. | |||
| 기본값은 depth_first예요. |
예제 (Example)
다음 예제는 terms 집계를 사용해 웹 로그 데이터에서 응답 코드별 문서 수를 찾아요:
GET opensearch_dashboards_sample_data_logs/_search
{
"size": 0,
"aggs": {
"response_codes": {
"terms": {
"field": "response.keyword",
"size": 10
}
}
}
}
예제 응답 (Example response)
...
"aggregations" : {
"response_codes" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "200",
"doc_count" : 12832
},
{
"key" : "404",
"doc_count" : 801
},
{
"key" : "503",
"doc_count" : 441
}
]
}
}
}
값은 key 키로 반환돼요. doc_count는 각 버킷의 문서 수를 지정해요. 기본적으로 버킷은 doc-count 내림차순으로 정렬돼요.
반환된 값을 오름차순 개수로 정렬("order": {"count": "asc"})하여 terms로 드문 값을 검색할 수도 있어요. 하지만 여러 샤드가 관련될 때 부정확한 결과를 초래할 수 있으므로 이 방법은 권장하지 않아요. 전역적으로 드문 term은 개별 샤드마다 드문 것처럼 보이지 않거나 일부 샤드가 반환하는 가장 빈도가 낮은 결과에 완전히 없을 수도 있어요. 반대로 한 샤드에서 드물게 나타나는 term이 다른 샤드에서는 흔할 수도 있어요. 두 시나리오 모두에서 희귀 term이 샤드 수준 집계 중에 누락되어 전체 결과가 부정확해질 수 있어요. terms 집계 대신, 이런 경우를 더 정확하게 처리하도록 특별히 설계된 rare_terms 집계를 사용하는 것을 권장해요.
size와 shard_size 파라미터 (The size and shard_size parameters)
terms 집계가 반환하는 버킷 수는 기본값이 10인 size 파라미터로 제어돼요.
또한 집계를 담당하는 조정 노드(coordinating node)는 각 샤드에 최상위 고유 term을 요청해요. 각 샤드가 반환하는 버킷 수는 shard_size 파라미터로 제어돼요. 이 파라미터는 size 파라미터와 구별되며 버킷 문서 수의 정확도를 높이는 메커니즘으로 존재해요.
예를 들어 size와 shard_size 파라미터가 모두 3인 시나리오를 상상해보세요. terms 집계는 각 샤드에 최상위 세 개의 고유 term을 요청해요. 조정 노드는 결과를 집계해 최종 결과를 계산해요. 샤드에 최상위 3개에 포함되지 않는 객체가 있으면 응답에 나타나지 않아요. 그러나 이 요청의 shard_size 값을 높이면 각 샤드가 더 많은 수의 고유 term을 반환할 수 있어, 조정 노드가 관련된 모든 결과를 받을 가능성이 높아져요.
기본적으로 shard_size 파라미터는 size * 1.5 + 10으로 설정돼요.
동시 세그먼트 검색을 사용할 때 shard_size 파라미터는 각 세그먼트 조각에도 적용돼요.
shard_size 파라미터는 terms 집계의 성능과 문서 수 정확도의 균형을 맞추는 방법으로 사용돼요. 더 높은 shard_size 값은 더 높은 문서 수 정확도를 보장하지만 더 많은 메모리와 컴퓨팅 사용을 초래해요. 더 낮은 shard_size 값은 더 나은 성능을 보이지만 문서 수 정확도는 낮아져요.
문서 수 오차 (Document count error)
응답에는 doc_count_error_upper_bound와 sum_other_doc_count라는 두 키도 포함돼요.
terms 집계는 최상위 고유 term을 반환해요. 따라서 데이터에 고유 term이 많으면 그중 일부가 결과에 나타나지 않을 수 있어요. sum_other_doc_count 필드는 응답에서 제외된 문서의 합을 나타내요. 이 경우 모든 고유 값이 응답에 나타나므로 개수는 0이에요.
doc_count_error_upper_bound 필드는 최종 결과에서 제외된 고유 값에 대한 가능한 최대 개수를 나타내요. 이 필드를 사용해 개수의 오차 범위를 추정해요.
doc_count_error_upper_bound 값과 정확도의 개념은 기본 정렬 순서(문서 수 내림차순)를 사용하는 집계에만 적용돼요. 내림차순 문서 수로 정렬하면 반환되지 않은 모든 term은 반환된 term과 같거나 더 적은 문서를 포함한다는 것이 보장되기 때문이에요. 이를 기반으로 doc_count_error_upper_bound를 계산할 수 있어요.
show_term_doc_count_error 파라미터가 true로 설정되면 terms 집계는 전체 값 외에도 각 고유 버킷에 대해 계산된 doc_count_error_upper_bound를 표시해요.
min_doc_count와 shard_min_doc_count 파라미터 (The min_doc_count and shard_min_doc_count parameters)
min_doc_count 파라미터를 사용해 min_doc_count 결과보다 적은 고유 term을 필터링할 수 있어요. min_doc_count 임계값은 모든 샤드에서 검색된 결과를 병합한 후에만 적용돼요. 각 샤드는 특정 term의 전역 문서 수를 알지 못해요. 최상위 shard_size만큼 빈번한 전역 term과 샤드 로컬 최상위 term 사이에 큰 차이가 있으면 min_doc_count 파라미터를 사용할 때 예상치 못한 결과를 받을 수 있어요.
별도로 shard_min_doc_count 파라미터는 샤드가 조정자에게 돌려보내는, shard_min_doc_count 결과보다 적은 고유 term을 필터링하는 데 사용돼요.
동시 세그먼트 검색을 사용할 때 shard_min_doc_count 파라미터는 각 세그먼트 조각에 적용되지 않아요. 자세한 내용은 관련 GitHub 이슈를 참고하세요.
값 부분 집합으로 필터링 (Filtering values to a subset)
include와 exclude 파라미터를 사용해 집계 버킷에 나타나는 term 값을 필터링할 수 있어요. 두 파라미터를 모두 지정하면 include가 먼저 평가되고 그런 다음 exclude가 결과에 적용돼요.
정규 표현식 필터링 (Regular expression filtering)
include와 exclude 파라미터 모두 Lucene의 정규 표현식 구문으로 된 정규 표현식 문자열을 받아요:
GET /opensearch_dashboards_sample_data_logs/_search
{
"size": 0,
"aggs": {
"success_codes": {
"terms": {
"field": "response.keyword",
"include": "2.*",
"exclude": "204"
}
}
}
}
기본적으로 최대 regex 문자열 길이는 1000자예요. 이 제한은 index.max_regex_length 인덱스 설정으로 변경할 수 있어요. 자세한 내용은 Index settings를 참고하세요.
정확한 값 필터링 (Exact value filtering)
include와 exclude 파라미터 모두 정확한 값의 배열을 받아요:
GET /opensearch_dashboards_sample_data_logs/_search
{
"size": 0,
"aggs": {
"specific_codes": {
"terms": {
"field": "response.keyword",
"include": ["200", "404", "503"]
}
}
}
}
include와 exclude 파라미터는 regex 문자열 또는 정확한 값의 배열이라는 동일한 형식을 사용해야 해요.
모든 term 페이지 처리 (Paginating through all terms)
필드에 단일 요청으로 검색하기에는 너무 많은 고유 term이 있을 때 분할(partition) 기반 필터링을 사용해 여러 요청을 보내 모든 term을 검색할 수 있어요. term은 해시 함수를 사용해 파티션에 할당되므로 분포는 거의 균등해요.
모든 고유 term을 검색하려면 num_partitions와 같은 수의 요청을 보내요. include 파라미터에 partition과 num_partitions를 지정해요. 각 요청에 대해 num_partitions를 동일하게 유지하고 partition을 0부터 num_partitions - 1까지 증가시켜요:
GET /opensearch_dashboards_sample_data_logs/_search
{
"size": 0,
"aggs": {
"partitioned_terms": {
"terms": {
"field": "response.keyword",
"size": 10000,
"include": {
"partition": 0,
"num_partitions": 5
}
}
}
}
}
include 파라미터는 요청당 하나의 형식만 사용할 수 있어요: regex 문자열, 값 배열 또는 partition 객체. 따라서 필터링(regex 또는 배열 기반)과 파티션 기반 검색을 결합할 수 없어요. 파티션 기반 검색을 사용할 때 exclude 파라미터는 지원되지 않아요.
수집 모드 (Collect mode)
두 가지 수집 모드가 있어요: depth_first와 breadth_first. depth_first 수집 모드는 집계 트리의 모든 분기를 깊이 우선 방식으로 확장하고 확장이 완료된 후에만 가지치기를 수행해요.
그러나 중첩 terms 집계를 사용할 때 반환되는 버킷 수의 카디널리티는 중첩의 각 레벨에서 필드의 카디널리티와 곱해져, 집계를 중첩할수록 버킷 수에서 조합 폭발이 쉽게 발생할 수 있어요.
breadth_first 수집 모드를 사용해 이 문제를 해결할 수 있어요. 이 경우 가지치기가 집계 트리의 첫 번째 레벨에 적용된 다음 다음 레벨로 확장되어 계산되는 버킷 수를 크게 줄일 수 있어요.
또한 breadth_first 수집을 수행하는 데는 메모리 오버헤드가 있으며, 이는 일치하는 문서 수와 선형적으로 관련돼요. breadth_first 수집은 부모 레벨에서 가지치기된 버킷 집합을 캐시하고 재생하여 동작하기 때문이에요.
정렬 순서 (Sort order)
기본적으로 버킷은 _count 내림차순(문서가 가장 많은 순서)으로 정렬돼요. order 파라미터를 사용해 정렬 순서를 변경할 수 있어요. 다음 예제는 응답 코드를 알파벳순으로 정렬해요:
GET /opensearch_dashboards_sample_data_logs/_search
{
"size": 0,
"aggs": {
"response_codes": {
"terms": {
"field": "response.keyword",
"order": { "_key": "asc" }
}
}
}
}
응답은 키별로 오름차순 정렬된 버킷을 반환해요:
{
...
"aggregations" : {
"response_codes" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "200",
"doc_count" : 12832
},
{
"key" : "404",
"doc_count" : 801
},
{
"key" : "503",
"doc_count" : 441
}
]
}
}
}
하위 집계 메트릭으로 정렬할 수도 있어요. 다음 예제는 응답 코드를 평균 바이트 내림차순으로 정렬해요:
GET /opensearch_dashboards_sample_data_logs/_search
{
"size": 0,
"aggs": {
"response_codes": {
"terms": {
"field": "response.keyword",
"order": { "avg_bytes": "desc" }
},
"aggs": {
"avg_bytes": {
"avg": { "field": "bytes" }
}
}
}
}
}
집계 트리에서 더 깊이 중첩된 메트릭으로 정렬하려면 > 구분자를 사용해 경로를 지정해요. 다음 예제는 성공(200) 응답의 평균 바이트만으로 운영 체제를 정렬해요:
GET /opensearch_dashboards_sample_data_logs/_search
{
"size": 0,
"aggs": {
"os": {
"terms": {
"field": "machine.os.keyword",
"size": 3,
"order": { "successful>avg_bytes": "desc" }
},
"aggs": {
"successful": {
"filter": { "term": { "response.keyword": "200" } },
"aggs": {
"avg_bytes": {
"avg": { "field": "bytes" }
}
}
}
}
}
}
}
응답은 중첩된 avg_bytes 메트릭으로 OS 버킷을 정렬해요:
{
...
"aggregations" : {
"os" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 5639,
"buckets" : [
{
"key" : "win xp",
"doc_count" : 2885,
"successful" : {
"doc_count" : 2549,
"avg_bytes" : {
"value" : 6083.602196939976
}
}
},
{
"key" : "ios",
"doc_count" : 2737,
"successful" : {
"doc_count" : 2512,
"avg_bytes" : {
"value" : 5954.860270700637
}
}
},
{
"key" : "win 8",
"doc_count" : 2813,
"successful" : {
"doc_count" : 2585,
"avg_bytes" : {
"value" : 5910.602321083172
}
}
}
]
}
}
}
파이프라인 집계는 정렬에 사용할 수 없어요.
다중 필드 terms 집계 (Multi-field terms aggregation)
terms 집계는 다중 필드를 기본적으로 지원하지 않아요. 필드 조합으로 그룹화하려면 multi_terms 집계 또는 필드 값을 결합하는 스크립트를 사용해요:
GET /opensearch_dashboards_sample_data_logs/_search
{
"size": 0,
"aggs": {
"os_and_response": {
"terms": {
"script": {
"source": "doc['machine.os.keyword'].value + ' - ' + doc['response.keyword'].value"
},
"size": 5
}
}
}
}
더 나은 성능을 위해 multi_terms 집계를 사용하거나 copy_to를 사용해 색인 시점에 결합 필드를 만드는 것을 고려해요.
인덱스 간 필드 타입 혼합 (Mixing field types across indexes)
같은 필드 이름이 인덱스 간에 서로 다른 숫자 타입(예: 한 인덱스에서는 long, 다른 인덱스에서는 double)을 가질 때 terms 집계는 값을 더 넓은 타입으로 승격해요. 소수점이 아닌 값은 double로 캐스팅되며, 이는 2^53을 초과하는 값의 정밀도 손실을 초래할 수 있어요.
누락 값 (Missing values)
missing 파라미터는 대상 필드가 없는 문서에 값을 할당해 해당 버킷에 배치해요. 기본적으로 필드가 없는 문서는 집계에서 제외돼요. 다음 예제는 machine.os.keyword 필드가 누락된 문서에 "unknown"을 할당해요:
GET /opensearch_dashboards_sample_data_logs/_search
{
"size": 0,
"aggs": {
"os": {
"terms": {
"field": "machine.os.keyword",
"missing": "unknown"
}
}
}
}
스크립트 (Script)
field 대신 스크립트를 사용해 term 값을 동적으로 계산할 수 있어요. 다음 예제는 bytes 필드를 기반으로 로그 항목을 크기 카테고리로 분류해요:
GET /opensearch_dashboards_sample_data_logs/_search
{
"size": 0,
"aggs": {
"byte_ranges": {
"terms": {
"script": {
"source": "if (doc['bytes'].value > 10000) return 'large'; else if (doc['bytes'].value > 1000) return 'medium'; else return 'small'"
}
}
}
}
}
응답은 문서를 계산된 카테고리로 그룹화해요:
{
...
"aggregations" : {
"byte_ranges" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "medium",
"doc_count" : 6995
},
{
"key" : "small",
"doc_count" : 6377
},
{
"key" : "large",
"doc_count" : 702
}
]
}
}
}
값 스크립트 (Value script)
field와 script를 모두 지정하면 스크립트는 값 스크립트로 동작하고 필드 값을 _value로 받아요. 다음 예제는 각 응답 코드에 "HTTP "를 접두사로 붙여요:
GET /opensearch_dashboards_sample_data_logs/_search
{
"size": 0,
"aggs": {
"responses_prefixed": {
"terms": {
"field": "response.keyword",
"script": {
"source": "'HTTP ' + _value"
}
}
}
}
}
응답은 변환된 키를 보여줘요:
{
...
"aggregations" : {
"responses_prefixed" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "HTTP 200",
"doc_count" : 12832
},
{
"key" : "HTTP 404",
"doc_count" : 801
},
{
"key" : "HTTP 503",
"doc_count" : 441
}
]
}
}
}
사전 집계 데이터 처리 (Accounting for preaggregated data)
doc_count 필드는 버킷에 집계된 개별 문서 수를 나타내지만, doc_count 자체는 사전 집계된 데이터를 저장하는 문서를 올바르게 증가시키는 방법이 없어요. 사전 집계된 데이터를 처리하고 버킷의 문서 수를 정확히 계산하려면 _doc_count 필드를 사용해 단일 요약 필드에 문서 수를 추가할 수 있어요. 문서에 _doc_count 필드가 포함되면 모든 버킷 집계가 해당 값을 인식하고 버킷 doc_count를 누적적으로 증가시켜요. _doc_count 필드를 사용할 때 다음 사항을 고려하세요:
- 이 필드는 중첩 배열을 지원하지 않으며 양의 정수만 사용할 수 있어요.
- 문서에 _doc_count 필드가 없으면 집계는 문서를 사용해 개수를 1만큼 증가시켜요.
정확한 문서 수에 의존하는 OpenSearch 기능은 _doc_count 필드 사용의 중요성을 보여줘요. 이 필드가 다른 검색 도구를 지원하는 데 어떻게 쓰일 수 있는지 보려면 사전 집계된 데이터가 있는 문서를 롤업 인덱스에 저장하는 Index Management(IM) 플러그인의 OpenSearch 기능인 Index rollups를 참고하세요.
예제 요청 (Example request)
PUT /my_index/_doc/1
{
"response_code": 404,
"date":"2022-08-05",
"_doc_count": 20
}
PUT /my_index/_doc/2
{
"response_code": 404,
"date":"2022-08-06",
"_doc_count": 10
}
PUT /my_index/_doc/3
{
"response_code": 200,
"date":"2022-08-06",
"_doc_count": 300
}
GET /my_index/_search
{
"size": 0,
"aggs": {
"response_codes": {
"terms": {
"field" : "response_code"
}
}
}
}
예제 응답 (Example response)
{
"took" : 20,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 3,
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ]
},
"aggregations" : {
"response_codes" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : 200,
"doc_count" : 300
},
{
"key" : 404,
"doc_count" : 30
}
]
}
}
}