Add PromQL query support for dashboard visualizations (ticket 013)

Hand-rolls a minimal PromQL parser (src/promql.rs) for metric-name +
label-matcher selection, optional [range] selectors, and top-level
sum()/avg()/rate() wrapping, rather than pulling in a full PromQL parser
crate — the required subset is small and a hand-rolled parser stays fully
engine-independent (mirrors filter_parser.rs) with no unused-AST overhead.

Adds EngineQuery::Promql/EngineResponse::Promql (engine parses + evaluates
in place against the index, no copying) and GET /api/query, returning
Vec<PromqlSeriesDto> (labels + (timestamp_ms, f64) points) for charting,
with malformed queries surfaced as 400 + ErrorDto.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XFUNZwCvz3kcod3352ZR2o
This commit is contained in:
Hugh Kaznowski
2026-09-05 13:10:41 +01:00
co-authored by Claude Sonnet 5
parent e48c03218f
commit ac67f9e120
5 changed files with 727 additions and 3 deletions
+25
View File
@@ -83,6 +83,31 @@ pub struct BucketValuesParams {
pub limit: Option<usize>,
}
/// One chart-ready time series returned by `GET /api/query` (ticket 013's PromQL
/// endpoint): a label combination plus its `(timestamp_ms, value)` points, sorted by
/// timestamp. `labels` uses plain `String` values (not `MetricPrimitive`) since every
/// value has already been through `MetricPrimitive`'s `Display` form — chart code
/// doesn't need the typed value, just something to show in a legend/tooltip.
///
/// `value` is always `f64` (see `crate::engine::coerce_to_f64` for how a bucket's
/// `MetricPrimitive` is coerced into one), since a chart needs a plottable number
/// regardless of the underlying metric primitive's kind.
#[derive(Debug, Clone, Serialize, PartialEq)]
pub struct PromqlSeriesDto {
pub labels: BTreeMap<String, String>,
pub points: Vec<(i64, f64)>,
}
/// Query params for `GET /api/query`. `promql` is the query string (see
/// `crate::promql`); `start_ms`/`end_ms` bound which points are included, mirroring
/// `/api/logs`/`/api/metrics/:name`.
#[derive(Debug, Deserialize)]
pub struct PromqlParams {
pub promql: String,
pub start_ms: Option<i64>,
pub end_ms: Option<i64>,
}
/// JSON error body returned by API handlers on a 4xx/5xx response, e.g.
/// `{ "error": "invalid regex: ..." }`.
#[derive(Debug, Serialize)]