The Flux subset InfluxElixir.Client.Local answers, with InfluxDB 2's
semantics (verified against influxdb:2.7; see
docs/design/2026-09-24_local-flux-pipeline.md).
A query is a pipeline, from(bucket: "b") |> range(...) |> ..., and every
stage is applied — or the query is refused by name.
Supported stages:
from(bucket: "b")— first; the bucket must exist (404 otherwise)range(start: s[, stop: s])— required, as on the engine;sis Unix seconds, an RFC3339 time, a duration from now (-1h,-30m,-7d,-10s,-2w) ornow();stopdefaults to now. Every row carries_startand_stop. A range that is empty (startnot beforestop) is the engine's 400 "cannot query an empty range". Times are 64-bit nanoseconds as on the engine, so a time that does not fit wraps:stop: 99999999999999(seconds) is 1976-05-08 in_stop.filter(fn: (r) => ...)—r.keyorr["key"]compared with== != < <= > >=against a string, number or boolean, combined withand,or,notand parentheses. A key the row lacks never matches.first() last() min() max()— the selected row per tablemean() sum() count()— one row per table, without_time(meanis a float;countcounts rows)limit(n: N[, offset: M])— per tableyield(name: "x")— names the result
Tables are the series — measurement, tag set, field — numbered from 0
in that order, rows in time order, as the engine numbers them. Time order
is the stored nanoseconds, finer than the microsecond a row's _time
carries, so first() and last() choose between points a microsecond
holds as the engine does.
Summary
Types
A comparison operand: a row key compared against a literal.
A parsed query.
One pipeline stage after from.
Functions
The measurements a query reads: the ones its filter stages pin with
r._measurement == "m" (joined by or, narrowed by and), or :all.
A table never mixes measurements, so a point of any other one can only
be filtered out; Client.Local reads just these from the store.
Parses a Flux query. now_ns is the instant now() and a relative
range resolve against. Returns {:error, message} for syntax the
double does not model.
The {start, stop} nanoseconds of a query's range stage.
Runs a parsed query over the bucket's points (%{measurement, tags, fields, timestamp} maps, duplicates already merged).
Types
@type predicate() :: {:cmp, binary(), binary(), term()} | {:and, predicate(), predicate()} | {:or, predicate(), predicate()} | {:not, predicate()}
A comparison operand: a row key compared against a literal.
A parsed query.
@type stage() :: {:range, integer(), integer()} | {:filter, predicate()} | {:selector, :first | :last | :min | :max} | {:aggregate, :mean | :sum | :count} | {:limit, non_neg_integer(), non_neg_integer()} | {:yield, binary()}
One pipeline stage after from.
Functions
The measurements a query reads: the ones its filter stages pin with
r._measurement == "m" (joined by or, narrowed by and), or :all.
A table never mixes measurements, so a point of any other one can only
be filtered out; Client.Local reads just these from the store.
Parses a Flux query. now_ns is the instant now() and a relative
range resolve against. Returns {:error, message} for syntax the
double does not model.
The {start, stop} nanoseconds of a query's range stage.
Runs a parsed query over the bucket's points (%{measurement, tags, fields, timestamp} maps, duplicates already merged).