こんにちは。Elasticsearchテクニカルコンサルタントの江見です。
この記事は、Elastic Stack (Elasticsearch) Advent Calendar 2025の24日目の記事です。
ログの検索、売上データ、ユーザの行動履歴など、業務システムでタイムスタンプの範囲検索はほぼ必須の機能になります。
特に日単位で範囲指定をする場合は、yyyy-MM-ddのように日付のみを指定するのが人間にとっては直感的にわかりやすいと思います。
Elasticsearchで範囲検索を行う場合は、rangeクエリを使用するのが一般的ですが、yyyy-MM-ddの形式のままrangeクエリを使用した場合に
「ヒットするはずのドキュメントが返ってこない」「余分なドキュメントが返ってくる」などのケースが発生しがちです。
今回はyyyy-MM-ddでrangeクエリを使用した場合の挙動について紹介します。
1.rangeクエリについて
Elasticsearch で「ある範囲の値を持つドキュメントを取得したい」場合、最もよく使われるのが range クエリ です。
rangeクエリでは以下の演算子を使用して範囲を検索します。
| 演算子 | 意味 | 境界を含むか |
| gte | 以上 | 含む(≧) |
| gt | より大きい | 含まない(>) |
| lte | 以下 | 含む(≦) |
| lt | 未満 | 含まない(<) |
例えば、年齢を示すageフィールドに対して、20歳以上、30歳未満で検索したい場合は以下のように表します。
{ "range": { "age": { "gte": 20, "lt": 30 } } }
2.yyyy-MM-ddでrangeクエリを書いた時の挙動とよくある間違い
数値の range クエリでは、指定した値がそのまま比較に使われるため、直感と実際の挙動がズレることはあまりありません。
しかし、日付フィールドの場合は少し事情が異なります。
Elasticsearch の date 型フィールドは、内部的には UTC の epoch millis(1 つの時点) に変換されて比較されます。
そのため、yyyy-MM-dd のように 時刻を含まない日付を指定した場合、
Elasticsearch はそれを「どの時点として扱うか」を決める必要があります。
この挙動を理解するには、以下の 2 つのルールを分けて考えることが重要です。
2-1. 不完全な日付(missing date components)の補完
yyyy-MM-dd のように時刻を含まない日付は、「時・分・秒・ナノ秒」といった要素が欠けた 不完全な日付 として扱われます。
Elasticsearch では、これを 1 つの時点として解釈する必要がある場合、欠けている要素を既定のルールで補完します。
特に、上限(lte / lt)として解釈される場合は、欠けている要素は以下のように 最大値で補完されます。
| 欠けている要素 | 補完される値 |
| 時 | 23 |
| 分 | 59 |
| 秒 | 59 |
| ナノ秒 | 999_999_999 |
2-2. range クエリにおける境界の解釈
range クエリでは、指定した値そのものではなく、「その値で取りうる どの時点 を境界にするか」が重要になります。
- 下限(gte / gt)
→ 指定した日付で取りうる 最小の時点
- 上限(lte / lt)
→ 指定した日付で取りうる 最大の時点
例えば、2025-12-20の1日分のデータを取得したいと考えた時、「2025-12-20 以上(gte)、2025-12-21以下(lte)」として以下のクエリを作成したとします。
{ "range": { "timestamp": { "gte": "2025-12-20", "lte": "2025-12-21" } } }
ですが、この場合それぞれの検索値は以下のように補完されます。
| クエリ上の表記 | range クエリの境界としての解釈 |
| "gte": "2025-12-20" | "2025-12-20T00:00:00.000_000_000Z" 以上 |
| "lte": "2025-12-21" | "2025-12-21T23:59:59.999_999_999Z" 以下 |
そのため、1日分のデータを取ろうとしたはずが2025-12-21のデータも含まれてしまい、約2日分のデータが取得できてしまうことになります。
クエリ上では日付しか指定していないため気づきにくく、
「ヒットする件数が多い」「翌日のデータが混ざる」といった形で問題が表面化しがちです。
3.1日分の範囲をとる場合の書き方の例
1.最初から時刻を指定する
時刻がElasticsearchで自動で補完されないようにするのが一番確実な方法です。
先ほどの例の場合以下のように指定すれば意図通りの検索が可能です。
{ "range": { "timestamp": { "gte": "2025-12-20T00:00:00.000", "lte": "2025-12-20T23:59:59.999" } } }
2.日付で丸めた表記を利用する
時刻まで書かずに指定する方法として、日付の丸め指定(rounding)を使う書き方があります。
以下のように、||/dを日付につけると、日単位で切り捨てをした値で検索されます。
(timezoneはUTCとのずれをなくすために追記)
{ "range": { "timestamp": { "gte": "2025-12-20||/d", "lte": "2025-12-20||/d", "time_zone": "+09:00" } } }
上記クエリは以下のように解釈されます。
| クエリ上の表記 | range クエリの境界としての解釈 |
| "gte": "2025-12-20||/d" | "2025-12-20T00:00:00.000" 以上 |
| "lte": "2025-12-20||/d" | "2025-12-20T23:59:59.999" 以下 |
4.まとめ
Elasticsearchで日時フィールドを扱う場合、意外と奥が深いです。
今回ご紹介した時刻の補完だけでなく、timezoneによるズレや、境界値の扱いなど注意すべき観点がいくつかあるため、
それらを意識したクエリ設計が必要になります。
今回の記事がElasticsearchのクエリを書く際の参考になれば幸いです。
アクロクエストでは幅広いユースケースに対してElastic Cloud/Elastic Stackをご利用頂く際の導入検討から運用まで幅広くサービスを提供しています。
Elasticsearchコンサルティングサービス
実際にElastic Cloudのご利用を検討される場合は是非お気軽にお問い合わせください。
ENdoSnipe/Elasticsearchお問い合わせフォーム
Acroquest Technologyでは、キャリア採用を行っています。
- Azure OpenAI/Amazon Bedrock等を使った生成AIソリューションの開発
- ディープラーニング等を使った自然言語/画像/音声/動画解析の研究開発
- マイクロサービス、DevOps、最新のOSSやクラウドサービスを利用する開発プロジェクト
- 書籍・雑誌等の執筆や、社内外での技術の発信・共有によるエンジニアとしての成長
少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。