Elasticsearchで日付の範囲の集約クエリを書く

自力で書こうとして、「あれ？どう書くんだっけ？」となったので備忘録

## 集約クエリについて

Elsticseaechでは、通常の検索クエリに合わせて[集約](https://www.elastic.co/guide/jp/elasticsearch/reference/current/gs-executing-aggregations.html)のクエリを書くことができます(下記にサンプル)。

<details><summary>qiitaで検索した記事の検索結果に対してタグの集約を合わせて返す場合のクエリの例</summary>

```json:# GET /articles/_search
{
  "_source": [
    "id",
    "body"
  ],
  "query": {
    "bool": {
      "should": [
        {
          "multi_match": {
            "fields": [
              "title",
              "title.ngram",
              "body",
              "body.ngram"
            ],
            "query": "qiita",
            "type": "phrase",
            "operator": "and"
          }
        }
      ]
    }
  },
  "sort": [
    "_score"
  ],
  "aggs": {
    "tags": {
      "terms": {
        "field": "tag"
      }
    }
  }
}
```

</details>
クエリ中の`aggs`から始まる下記の部分で`tags`というkeyで、indexの中のヒットするコンテンツからtagフィールドを精査してくれます。

```json
  "aggs": {
    "tags": {
      "terms": {
        "field": "tag"
      }
    }
  }
```

そして、検索結果と合わせて下記のようにチュートリアルタグがついた記事が48件あった、などのように返してくれるようになっています。

```json
{
  "aggs": {
    "tags": {
      "doc_count_error_upper_bound": 0,
      "sum_other_doc_count": 10,
      "buckets": [
        {
          "key": "チュートリアル",
          "doc_count": 48
        },
        {
          "key": "aws",
          "doc_count": 1
        },
        #(中略)
        {
          "key": "infrastructure",
          "doc_count": 1
        }
      ]
    }
  }
}
```

## ○○日以内に作られた記事の数を知りたい

では、タグと同じように、日付で集計する場合はどのように書くとよいでしょう？
今回は7日以内、30日以内、365日以内に作られた記事の数が欲しい場合を考えます。
また、indexにはdate型のcreated_atが入っています。

## Date range aggregationを使おう

[Date range aggregation](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-daterange-aggregation.html)を下記のように使うことで、時間をいい感じに集計することができます。

```json
{
  "aggs": {
    "date": {
      "date_range": {
        "field": "created_at",
        "format": "yyyy-MM-dd'T'HH:mm:ssZ",
        "ranges": [
          {
            "from": "now-168h"
          },
          {
            "from": "now-720h"
          },
          {
            "from": "now-8760h"
          }
        ]
      }
    }
  }
}
```

ポイントはfromの部分で、`now-時間`の形式で○○前からの記事数を集めることができます。
下記のような形式で帰ってきます。

```json
{
  "aggregations": {
    "date": {
      "buckets": [
        {
          "key": "2021-08-12T03:25:33+0000-*",
          "from": 1628738733198,
          "from_as_string": "2021-08-12T03:25:33+0000",
          "doc_count": 55
        },
        {
          "key": "2022-07-13T03:25:33+0000-*",
          "from": 1657682733198,
          "from_as_string": "2022-07-13T03:25:33+0000",
          "doc_count": 2
        },
        {
          "key": "2022-08-05T03:25:33+0000-*",
          "from": 1659669933198,
          "from_as_string": "2022-08-05T03:25:33+0000",
          "doc_count": 0
        }
      ]
    }
  }
}
```

今回はhを指定しているのでそれぞれ`（指定したい日付範囲）*24`を入れてあげればいいわけですね。

下記のように書いてもいいでしょう。

```json
{
  "aggs": {
    "date": {
      "date_range": {
        "field": "created_at",
        "format": "yyyy-MM-dd'T'HH:mm:ssZ",
        "ranges": [
          {
            "from": "now-1w"
          },
          {
            "from": "now-30d"
          },
          {
            "from": "now-365d"
          }
        ]
      }
    }
  }
}
```