Elasticsearchのdate_histogramで、ドキュメント数が少ないときに、日時の上限と下限がうまく設定できない問題の対処


概要

elasticsearchで、一定期間ごとの集計値を出す方法にdate_histogramがある。
便利な機能だが、集計対象期間内のドキュメント数が少ないときにこれを使うと落とし穴があったので対処法をメモする。

試した環境

  • elasticsaerch (7.10)

何が問題だったか

以下のように、elasticsearchから、2020-11-29 11:00:00から、2020-11-29 15:00:00まで、1時間ごとに、ドキュメントの総数を集計するクエリを投げる。

POST /<index名>/_search
{
  "size": 0,
  "query": {
    "range": {
      "<時刻が入っているフィールド名>": {
        "gte": "2020-11-29T11:00:00.000Z",
        "lte": "2020-11-29T15:00:00.000Z"
      }
    }
  },
  "aggs": {
    "<集計名>": {
      "date_histogram": {
        "field": "<時刻が入っているフィールド名>",
        "interval": "1h",
        "order": {
          "_key": "asc"
        }
      }
    }
  }
}

すると、以下のようなレスポンスが返ってきた。

{
  "took": 8,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 4,
      "relation": "eq"
    },
    "max_score": null,
    "hits": []
  },
  "aggregations": {
    "<集計名>": {
      "buckets": [
        {
          "key_as_string": "2020-11-29T13:00:00.000Z",
          "key": 1606654800000,
          "doc_count": 4
        }
      ]
    }
  }
}

11時から15時までの時間範囲で集計するようクエリを投げたのに、返ってきたのは13時の集計値のみ。

この問題が起こったのは、11時、12時、14時、15時には、ドキュメントが一つも存在しなかったため、elasticsearchが勝手に集計結果から省いてしまったためである。

対処法

elasticsearchにクエリを投げるときに、date_histogramフィールド内に、以下のようにmin_doc_countextended_boundsを指定する。

POST /<index名>/_search
{
  "size": 0,
  "query": {
    "range": {
      "<時刻が入っているフィールド名>": {
        "gte": "2020-11-29T11:00:00.000Z",
        "lte": "2020-11-29T15:00:00.000Z"
      }
    }
  },
  "aggs": {
    "<集計名>": {
      "date_histogram": {
        "field": "<時刻が入っているフィールド名>",
        "interval": "1h",
        "min_doc_count": 0,
        "extended_bounds": {
          "min": "2020-11-29T11:00:00.000Z",
          "max": "2020-11-29T15:00:00.000Z"
        },
        "order": {
          "_key": "asc"
        }
      }
    }
  }
}

min_doc_countに0を指定すると、ドキュメントの数がゼロでも集計に含めるようになる。

また、extended_boundsを指定することで、histogramの上限と下限を強制的に指定する。

こうすると、以下のようなレスポンスが得られる。

{
  "took": 6,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 4,
      "relation": "eq"
    },
    "max_score": null,
    "hits": []
  },
  "aggregations": {
    "<集計名>": {
      "buckets": [
        {
          "key_as_string": "2020-11-29T11:00:00.000Z",
          "key": 1606647600000,
          "doc_count": 0
        },
        {
          "key_as_string": "2020-11-29T12:00:00.000Z",
          "key": 1606651200000,
          "doc_count": 0
        },
        {
          "key_as_string": "2020-11-29T13:00:00.000Z",
          "key": 1606654800000,
          "doc_count": 4
        },
        {
          "key_as_string": "2020-11-29T14:00:00.000Z",
          "key": 1606658400000,
          "doc_count": 0
        },
        {
          "key_as_string": "2020-11-29T15:00:00.000Z",
          "key": 1606662000000,
          "doc_count": 0
        }
      ]
    }
  }

参考