Implementation:Apache Druid Time Manipulation
| Knowledge Sources | |
|---|---|
| Domains | Web_Console, Explore_View |
| Last Updated | 2026-02-10 10:00 GMT |
Overview
Provides SQL expression manipulation utilities for time-shifting, expanding, and over-querying time-based WHERE clauses in the explore view.
Description
The Time Manipulation module contains functions that transform time-related SQL expressions to support period-over-period comparison and granularity-aligned over-querying. The `computeWhereForCompares` function decomposes a WHERE clause into time and non-time parts, then creates shifted copies for each comparison period. The `shiftBackAndExpandTimeInExpression` function walks SQL comparison nodes to shift timestamp literals backward by a duration and optionally expand them via TIME_FLOOR/TIME_CEIL. The `decomposeTimeInInterval` function rewrites TIME_IN_INTERVAL calls into explicit comparison pairs. The `overqueryWhere` function expands time boundaries to align with a granularity and optionally fetch one extra granularity period on each side. The `evaluatesToTimeLiteral` helper identifies expressions that resolve to constant timestamps.
Usage
Used in the explore view's query generation pipeline when time-comparison mode is enabled, to create shifted time filters for period-over-period analysis, and when charts need slightly expanded time ranges to ensure complete granularity-aligned data.
Code Reference
Source Location
- Repository: Apache Druid
- File: web-console/src/views/explore-view/utils/time-manipulation.ts
- Lines: 1-251
Signature
export type Compare = `P${string}`;
export function computeWhereForCompares(
where: SqlExpression,
compares: Compare[],
expandDuration: string | undefined,
): {
commonWhere: SqlExpression;
comparelessWhere: SqlExpression;
mainWherePart: SqlExpression;
perCompareWhereParts: SqlExpression[];
};
export function decomposeTimeInInterval(expression: SqlExpression): SqlExpression;
export function shiftBackAndExpandTimeInExpression(
expression: SqlExpression,
compare: string,
expandDuration: string | undefined,
): SqlExpression;
export function evaluatesToTimeLiteral(ex: SqlExpression): boolean;
export function overqueryWhere(
where: SqlExpression,
timeColumnName: string,
granularity: Duration,
oneExtra: boolean,
): SqlExpression;
Import
import { computeWhereForCompares, overqueryWhere } from '../utils/time-manipulation';
I/O Contract
Inputs
| Name | Type | Required | Description |
|---|---|---|---|
| where | SqlExpression |
Yes | The original WHERE clause to decompose and transform |
| compares | Compare[] |
Yes | Array of ISO 8601 duration strings (e.g., 'P1W', 'P1M') for period-over-period comparison |
| expandDuration | undefined | No | An optional duration to expand time boundaries via TIME_FLOOR/TIME_CEIL |
| expression | SqlExpression |
Yes | A SQL expression to walk and transform |
| compare | string |
Yes | An ISO 8601 duration to shift time literals backward |
| ex | SqlExpression |
Yes | A SQL expression to check whether it evaluates to a time literal |
| timeColumnName | string |
Yes | The name of the time column (typically '__time') for over-query expansion |
| granularity | Duration |
Yes | A chronoshift Duration for granularity alignment of time boundaries |
| oneExtra | boolean |
Yes | Whether to include one extra granularity period on each side |
Outputs
| Name | Type | Description |
|---|---|---|
| commonWhere | SqlExpression |
The combined WHERE clause covering the main time range and all comparison ranges |
| comparelessWhere | SqlExpression |
The non-time portion of the WHERE clause |
| mainWherePart | SqlExpression |
The time portion of the original WHERE clause |
| perCompareWhereParts | SqlExpression[] |
Time-shifted WHERE parts, one per comparison period |
| (return) | SqlExpression |
A transformed SQL expression with time shifts, expansions, or decompositions applied |
| (return) | boolean |
Whether the expression evaluates to a constant timestamp |
Usage Examples
Compute WHERE for period-over-period comparison
import { computeWhereForCompares } from '../utils/time-manipulation';
import { SqlExpression } from 'druid-query-toolkit';
const where = SqlExpression.parse(
"TIMESTAMP '2024-01-01' <= __time AND __time < TIMESTAMP '2024-02-01'"
);
const result = computeWhereForCompares(where, ['P1M'], undefined);
// result.mainWherePart: original time filter
// result.perCompareWhereParts[0]: shifted back by 1 month
// result.commonWhere: OR of main and shifted ranges
Over-query with granularity alignment
import { overqueryWhere } from '../utils/time-manipulation';
import { Duration, Timezone } from 'chronoshift';
const expanded = overqueryWhere(where, '__time', new Duration('PT1H'), true);
// Expands time boundaries to hourly alignment with one extra hour on each side
Decompose TIME_IN_INTERVAL
import { decomposeTimeInInterval } from '../utils/time-manipulation';
const decomposed = decomposeTimeInInterval(
SqlExpression.parse("TIME_IN_INTERVAL(__time, '2024-01-01/2024-02-01')")
);
// Produces: TIMESTAMP '2024-01-01' <= __time AND __time < TIMESTAMP '2024-02-01'