# 8.2 버킷 - Bucket Aggregations

&#x20; Bucket aggregation 은 주어진 조건으로 분류된 **버킷** 들을 만들고, 각 버킷에 소속되는 도큐먼트들을 모아 **그룹으로 구분**하는 것입니다. 각 버킷 별로 포함되는 도큐먼트의 개수는 **doc\_count** 값에 기본적으로 표시가 되며 각 버킷 안에 metrics aggregation 을 이용해서 다른 계산들도 가능합니다. 주로 사용되는 bucket aggregation 들은 **Range**, **Histogram**, **Terms** 등이 있습니다.

### range

&#x20; **range** 는 숫자 필드 값으로 범위를 지정하고 각 범위에 해당하는 버킷을 만드는 aggregation 입니다. **field** 옵션에 해당 필드의 이름을 지정하고 **ranges** 옵션에 배열로 **from**, **to** 값을 가진 오브젝트 값을 나열해서 범위를 지정합니다. 다음은 passangers 값이 각각 1000 미만, 1000\~4000 사이, 4000 이상 인 버킷들을 생성하는 예제입니다.

{% tabs %}
{% tab title="request" %}
{% code title="range aggs 를 이용해서 passangers 필드의 값을 버킷으로 구분" %}

```javascript
GET my_stations/_search
{
  "size": 0,
  "aggs": {
    "passangers_range": {
      "range": {
        "field": "passangers",
        "ranges": [
          {
            "to": 1000
          },
          {
            "from": 1000,
            "to": 4000
          },
          {
            "from": 4000
          }
        ]
      }
    }
  }
}
```

{% endcode %}
{% endtab %}

{% tab title="response" %}
{% code title="range aggs 를 이용해서 passangers 필드의 값을 버킷으로 구분한 결과" %}

```javascript
{
  "took" : 2,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 10,
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : [ ]
  },
  "aggregations" : {
    "passangers_range" : {
      "buckets" : [
        {
          "key" : "*-1000.0",
          "to" : 1000.0,
          "doc_count" : 1
        },
        {
          "key" : "1000.0-4000.0",
          "from" : 1000.0,
          "to" : 4000.0,
          "doc_count" : 3
        },
        {
          "key" : "4000.0-*",
          "from" : 4000.0,
          "doc_count" : 6
        }
      ]
    }
  }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

&#x20; 각각의 버킷을 구분하는 key 값은 `"from-to"` 형태로 생성됩니다. 위 쿼리 결과에서 각각의 `"*-1000.0"`, `"1000.0-4000.0"`, `"key" : "4000.0-*"` 버킷에 속한 도큐먼트의 개수가 **1**, **3**, **6**개 인 것을 확인할 수 있습니다.

{% hint style="info" %}
aggs 에 설정한 **from** 은 **이상** 즉 버킷에 포함이고 **to** 는 **미만** 으로 버킷에 포함하지 않습니다. 예를 들어 필드 값이 **200** 인 도큐먼트는 `"key" : "100-200"` 버킷에는 포함되지 않고 `"key" : "200-300"` 버킷에는 포함됩니다.
{% endhint %}

### histogram

&#x20; histogram 도 range 와 마찬가지로 숫자 필드의 범위를 나누는 aggs 입니다. 앞에서 본 range 는 from 과 to 를 이용해서 각 버킷의 범위를 지정했습니다. histogram 은 from, to 대신 **interval** 옵션을 이용해서 주어진 간격 크기대로 버킷을 구분합니다. 다음은 passangers 필드에 간격이 2000인 버킷들을 생성하는 예제입니다.

{% tabs %}
{% tab title="request" %}
{% code title="histogram aggs 를 이용해서 passangers 필드의 값을 버킷으로 구분" %}

```javascript
GET my_stations/_search
{
  "size": 0,
  "aggs": {
    "passangers_his": {
      "histogram": {
        "field": "passangers",
        "interval": 2000
      }
    }
  }
}
```

{% endcode %}
{% endtab %}

{% tab title="response" %}
{% code title="histogram aggs 를 이용해서 passangers 필드의 값을 버킷으로 구분한 결과" %}

```javascript
{
  "took" : 2,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 10,
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : [ ]
  },
  "aggregations" : {
    "passangers_his" : {
      "buckets" : [
        {
          "key" : 0.0,
          "doc_count" : 2
        },
        {
          "key" : 2000.0,
          "doc_count" : 2
        },
        {
          "key" : 4000.0,
          "doc_count" : 4
        },
        {
          "key" : 6000.0,
          "doc_count" : 2
        }
      ]
    }
  }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

&#x20; 각각의 버킷을 구분하는 key 에는 값의 최소값이 표시됩니다. 위 예제에서 `0`, `2000`, `4000`, `6000` 인 버킷들이 생성되었고 각 버킷들에 포함되는 도큐먼트 개수가 표시된 것을 볼 수 있습니다. key 값은 range 와 마찬가지로 key 로 지정된 값이 버킷에 이상(포함) / 미만(포함하지 않음) 으로 지정됩니다.

### date\_range, date\_histogram

&#x20; **range** 와 **histogram** aggs 처럼 숫자 외에도 날짜 필드를 이용해서 범위별로 버킷의 생성이 가능합니다. 이 때 사용되는 **date\_range**, **date\_histogram** aggs 들은 특히 시계열 데이터에서 날짜별로 값을 표시할 때 매우 유용합니다. **date\_range** 는 **ranges** 옵션에 `{"from": "2019-06-01", "to": "2016-07-01"}` 와 같이 입력하며 **date\_histogram** 은 **interval** 옵션에 `day`, `month`, `week` 와 같은 값들을 이용해서 날짜 간격을 지정할 수 있습니다. 다음은 **date\_histogram** 으로 **date** 필드를 **1개월** 간격으로 구분하는 예제입니다.

{% tabs %}
{% tab title="request" %}
{% code title="date\_histogram 을 이용해서 date 값을 1개월 간격의 버킷으로 구분" %}

```javascript
GET my_stations/_search
{
  "size": 0,
  "aggs": {
    "date_his": {
      "date_histogram": {
        "field": "date",
        "interval": "month"
      }
    }
  }
}
```

{% endcode %}
{% endtab %}

{% tab title="response" %}
{% code title="date\_histogram 을 이용해서 date 값을 1개월 간격의 버킷으로 구분한 결과" %}

```javascript
{
  "took" : 2,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 10,
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : [ ]
  },
  "aggregations" : {
    "date_his" : {
      "buckets" : [
        {
          "key_as_string" : "2019-06-01T00:00:00.000Z",
          "key" : 1559347200000,
          "doc_count" : 2
        },
        {
          "key_as_string" : "2019-07-01T00:00:00.000Z",
          "key" : 1561939200000,
          "doc_count" : 2
        },
        {
          "key_as_string" : "2019-08-01T00:00:00.000Z",
          "key" : 1564617600000,
          "doc_count" : 2
        },
        {
          "key_as_string" : "2019-09-01T00:00:00.000Z",
          "key" : 1567296000000,
          "doc_count" : 3
        },
        {
          "key_as_string" : "2019-10-01T00:00:00.000Z",
          "key" : 1569888000000,
          "doc_count" : 1
        }
      ]
    }
  }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

&#x20; date\_histogram의 결과에서는 버킷별로 key 에 시작 날짜가 **epoch\_millis** 값으로 표시됩니다. 그리고 **key\_as\_string** 에 **ISO8601** 형태로도 함께 표시됩니다.

{% hint style="warning" %}
7.2 버전 부터는 interval 옵션이 사용 종료 권장(depricated) 되고 대신 **fixed\_interval** 과 **calendar\_interval** 으로 나누어 지게 되었습니다. year, quarter, month, week, 같은 달력 기준의 값은 `"calendar_interval" : "month"` 로 입력을 하고, 30일 처럼 정확히 구분되는 날짜들은 `"fixed_interval" : "30d"` 로 지정을 해야 합니다.
{% endhint %}

### terms

&#x20; 앞에서 살펴 본 **(date\_)range**, **histogram** 은 모두 숫자, 날짜를 가지고 구간을 나누는 aggregation 이었습니다. **terms** aggregation 은 **keyword** 필드의 문자열 별로 버킷을 나누어 집계가 가능합니다. keyword 필드 값으로만 사용이 가능하며 분석된 text 필드는 일반적으로는 사용이 불가능합니다. 다음은 my\_stations 인덱스에서 **station.keyword** 필드를 기준으로 버킷들을 만드는 예제입니다.

{% tabs %}
{% tab title="request" %}
{% code title="terms 을 이용해서 station 값을 별로 버킷 생성" %}

```javascript
GET my_stations/_search
{
  "size": 0,
  "aggs": {
    "stations": {
      "terms": {
        "field": "station.keyword"
      }
    }
  }
}
```

{% endcode %}
{% endtab %}

{% tab title="response" %}
{% code title="terms 을 이용해서 station 값을 별로 버킷 생성한 결과" %}

```javascript
{
  "took" : 2,
  "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
        },
        {
          "key" : "불광",
          "doc_count" : 1
        },
        {
          "key" : "신촌",
          "doc_count" : 1
        },
        {
          "key" : "양재",
          "doc_count" : 1
        },
        {
          "key" : "종각",
          "doc_count" : 1
        },
        {
          "key" : "홍제",
          "doc_count" : 1
        }
      ]
    }
  }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

&#x20; **terms** aggregation 에는 **field** 외에도 가져올 버킷의 개수를 지정하는 **size** 옵션이 있으며 디폴트 값은 **10** 입니다. 인덱스의 특정 keyword 필드에 있는 모든 값들을 종류별로 버킷을 만들면 가져와야 할 결과가 매우 많기 때문에 먼저 도큐먼트 개수 또는 주어진 metrics 연산 결과가 가장 많은 버킷 들을 샤드별로 계산해서 상위 몇개의 버킷들만 coordinate 노드로 가져오고, 그것들을 취합해서 결과를 나타냅니다. 이 과정은 검색의 query 그리고 fetch 과정과 유사합니다.
