mirror of
https://github.com/hyperdxio/hyperdx
synced 2026-04-21 13:37:15 +00:00
## Summary This PR updates Raw SQL charts with support for dashboard filters, using the $__filters macro. Lucene global filters require a Source to be included in the ChartConfig, for schema introspection and the `implicitColumnExpression` value. To support Lucene filters, this PR also updates the RawSqlChartConfig type to include optional `source`, `implicitColumnExpression`, and `from` properties. Only `source` is saved in the database. The external API has been updated to accept the `source` field for raw SQL charts as well. Dashboard import/export has also been updated to support source mapping for raw sql charts with sources. ### Screenshots or video Both the global filter and the drop-down filters are applied: <img width="683" height="574" alt="Screenshot 2026-03-17 at 10 57 36 AM" src="https://github.com/user-attachments/assets/280ba0b5-55f7-4107-a55c-eeb1497ac7de" /> To render Lucene conditions, we require Source information (the `from` and `implicitColumnExpression` fields). When a source is not set, filters are therefore not applied: <img width="782" height="618" alt="Screenshot 2026-03-17 at 10 54 41 AM" src="https://github.com/user-attachments/assets/3ad19ea7-12ee-4334-abe2-8985a0be952c" /> Similarly, if the `$__filters` macro is not used, then the filters are not applied <img width="704" height="292" alt="Screenshot 2026-03-17 at 10 56 33 AM" src="https://github.com/user-attachments/assets/e1169e4a-2f64-4cd2-bc05-f699fecef8c1" /> Import: <img width="993" height="669" alt="Screenshot 2026-03-17 at 3 35 16 PM" src="https://github.com/user-attachments/assets/6ebe20c0-19e2-4e90-95d0-8b02c2af0612" /> ### How to test locally or on Vercel This can be tested in the preview environment. 1. Create a saved dashboard and add some filters. 2. Create a raw sql tile, with or without the $__filters macro 3. Try selecting filters, deselecting filters, and applying global SQL and Lucene filters 4. Inspect the generated queries in the console. ### References - Linear Issue: Closes HDX-3694 - Related PRs:
118 lines
4 KiB
TypeScript
118 lines
4 KiB
TypeScript
import { ChSql } from './clickhouse';
|
|
import {
|
|
convertDateRangeToGranularityString,
|
|
convertGranularityToSeconds,
|
|
} from './core/utils';
|
|
import { DateRange, DisplayType, RawSqlChartConfig } from './types';
|
|
|
|
type QueryParamDefinition = {
|
|
name: string;
|
|
type: string;
|
|
description: string;
|
|
get: (config: RawSqlChartConfig & Partial<DateRange>) => any;
|
|
};
|
|
|
|
const getIntervalSeconds = (config: RawSqlChartConfig & Partial<DateRange>) => {
|
|
const granularity = config.granularity ?? 'auto';
|
|
|
|
const effectiveGranularity =
|
|
granularity === 'auto' && config.dateRange
|
|
? convertDateRangeToGranularityString(config.dateRange)
|
|
: granularity;
|
|
|
|
return convertGranularityToSeconds(effectiveGranularity);
|
|
};
|
|
|
|
export const QUERY_PARAMS: Record<string, QueryParamDefinition> = {
|
|
startDateMilliseconds: {
|
|
name: 'startDateMilliseconds',
|
|
type: 'Int64',
|
|
description:
|
|
'start of the dashboard date range, in milliseconds since epoch',
|
|
get: (config: RawSqlChartConfig & Partial<DateRange>) =>
|
|
config.dateRange ? config.dateRange[0].getTime() : undefined,
|
|
},
|
|
endDateMilliseconds: {
|
|
name: 'endDateMilliseconds',
|
|
type: 'Int64',
|
|
description: 'end of the dashboard date range, in milliseconds since epoch',
|
|
get: (config: RawSqlChartConfig & Partial<DateRange>) =>
|
|
config.dateRange ? config.dateRange[1].getTime() : undefined,
|
|
},
|
|
intervalSeconds: {
|
|
name: 'intervalSeconds',
|
|
type: 'Int64',
|
|
description: 'time bucket size in seconds',
|
|
get: getIntervalSeconds,
|
|
},
|
|
intervalMilliseconds: {
|
|
name: 'intervalMilliseconds',
|
|
type: 'Int64',
|
|
description: 'time bucket size in milliseconds',
|
|
get: (config: RawSqlChartConfig & Partial<DateRange>) =>
|
|
getIntervalSeconds(config) * 1000,
|
|
},
|
|
};
|
|
|
|
export const QUERY_PARAMS_BY_DISPLAY_TYPE: Record<
|
|
DisplayType,
|
|
QueryParamDefinition[]
|
|
> = {
|
|
[DisplayType.Line]: [
|
|
QUERY_PARAMS.startDateMilliseconds,
|
|
QUERY_PARAMS.endDateMilliseconds,
|
|
QUERY_PARAMS.intervalSeconds,
|
|
QUERY_PARAMS.intervalMilliseconds,
|
|
],
|
|
[DisplayType.StackedBar]: [
|
|
QUERY_PARAMS.startDateMilliseconds,
|
|
QUERY_PARAMS.endDateMilliseconds,
|
|
QUERY_PARAMS.intervalSeconds,
|
|
QUERY_PARAMS.intervalMilliseconds,
|
|
],
|
|
[DisplayType.Table]: [
|
|
QUERY_PARAMS.startDateMilliseconds,
|
|
QUERY_PARAMS.endDateMilliseconds,
|
|
],
|
|
[DisplayType.Pie]: [
|
|
QUERY_PARAMS.startDateMilliseconds,
|
|
QUERY_PARAMS.endDateMilliseconds,
|
|
],
|
|
[DisplayType.Number]: [
|
|
QUERY_PARAMS.startDateMilliseconds,
|
|
QUERY_PARAMS.endDateMilliseconds,
|
|
],
|
|
[DisplayType.Search]: [],
|
|
[DisplayType.Heatmap]: [],
|
|
[DisplayType.Markdown]: [],
|
|
};
|
|
|
|
const TIME_CHART_EXAMPLE_SQL = `SELECT
|
|
toStartOfInterval(TimestampTime, INTERVAL {intervalSeconds:Int64} second) AS ts, -- (Timestamp column)
|
|
ServiceName, -- (Group name column)
|
|
count() -- (Series value column)
|
|
FROM otel_logs
|
|
WHERE TimestampTime >= fromUnixTimestamp64Milli ({startDateMilliseconds:Int64})
|
|
AND TimestampTime < fromUnixTimestamp64Milli ({endDateMilliseconds:Int64})
|
|
AND $__filters
|
|
GROUP BY ServiceName, ts`;
|
|
|
|
export const DATE_RANGE_WHERE_EXAMPLE_SQL = `WHERE TimestampTime >= fromUnixTimestamp64Milli ({startDateMilliseconds:Int64})
|
|
AND TimestampTime <= fromUnixTimestamp64Milli ({endDateMilliseconds:Int64})
|
|
AND $__filters`;
|
|
|
|
export const QUERY_PARAM_EXAMPLES: Record<DisplayType, string> = {
|
|
[DisplayType.Line]: TIME_CHART_EXAMPLE_SQL,
|
|
[DisplayType.StackedBar]: TIME_CHART_EXAMPLE_SQL,
|
|
[DisplayType.Table]: DATE_RANGE_WHERE_EXAMPLE_SQL,
|
|
[DisplayType.Pie]: DATE_RANGE_WHERE_EXAMPLE_SQL,
|
|
[DisplayType.Number]: DATE_RANGE_WHERE_EXAMPLE_SQL,
|
|
[DisplayType.Search]: '',
|
|
[DisplayType.Heatmap]: '',
|
|
[DisplayType.Markdown]: '',
|
|
};
|
|
|
|
export function renderQueryParam(name: keyof typeof QUERY_PARAMS): string {
|
|
// eslint-disable-next-line security/detect-object-injection
|
|
return `{${QUERY_PARAMS[name].name}:${QUERY_PARAMS[name].type}}`;
|
|
}
|