InfluxFlux.jl
A minimal Julia package for read-only access to an InfluxDB v2 server using the Flux query language.
Installation
] add InfluxFluxQuick start
using InfluxFlux
using Dates
# Create a server handle
srv = influx_server("http://localhost:8086", "my-org", "my-token")
# List available buckets
list_buckets(srv)
# Fetch a measurement as a DataFrame
t0 = DateTime(2024, 1, 1)
t1 = DateTime(2024, 1, 2)
df = measurement(srv, "my-bucket", "temperature", t0, t1)
# Downsample to hourly means
df_hourly = aggregate_measurement(srv, "my-bucket", "temperature", t0, t1, Hour(1))
# Run an arbitrary Flux query
df = flux_to_dataframe(srv, """
from(bucket: "my-bucket")
|> range(start: -1h)
|> filter(fn: (r) => r._measurement == "cpu")
""")Time specifications
Functions that accept time bounds take a TimeSpec, which is any of:
| Type | Meaning |
|---|---|
Int | Epoch nanoseconds (Unix × 10⁹) |
DateTime | Treated as UTC |
ZonedDateTime | Converted to UTC automatically |
Use time_spec_to_epoc_ns to convert any TimeSpec to an integer epoch-nanosecond value.
API reference
Server
InfluxFlux.influx_server — Function
influx_server(uri, org, api_token) -> InfluxServerCreate a handle to an InfluxDB v2 server.
Arguments
uri: Base URL of the server, e.g."http://localhost:8086".org: InfluxDB organisation name.api_token: Read-access API token.
Query execution
InfluxFlux.flux — Function
flux(srv, flux_query) -> Vector{UInt8}Execute a raw Flux query against srv and return the response body as bytes.
Throws an InfluxFluxError on any non-200 HTTP response.
InfluxFlux.flux_to_dataframe — Function
flux_to_dataframe(srv, flux_query) -> DataFrameExecute a Flux query and return the result as a single DataFrame.
df = flux_to_dataframe(srv, """
from(bucket: "sensors")
|> range(start: -1h)
|> filter(fn: (r) => r._measurement == "cpu_load")
""")
# df has columns: result, table, _start, _stop, _time, _value, _field, _measurement, host, …Throws if the query returns more than one result table. Use flux_to_dataframe_multi when multiple results are expected.
InfluxFlux.flux_to_dataframe_multi — Function
flux_to_dataframe_multi(srv, flux_query) -> NamedTupleExecute a Flux query and return a NamedTuple mapping each named result to a Vector{DataFrame} (one element per table group).
Use this when a query yields multiple results with yield(name: ...):
q = """
from(bucket: "sensors")
|> range(start: -1h)
|> filter(fn: (r) => r._measurement == "temperature")
|> yield(name: "temp")
from(bucket: "sensors")
|> range(start: -1h)
|> filter(fn: (r) => r._measurement == "humidity")
|> yield(name: "hum")
"""
result = flux_to_dataframe_multi(srv, q)
result.temp # Vector{DataFrame} for temperature tables
result.hum # Vector{DataFrame} for humidity tablesInfluxFlux.clean_influx_df — Function
clean_influx_df(df) -> DataFrameDrop the InfluxDB bookkeeping columns (result, table, _start, _stop, _measurement, Column1) from a DataFrame, leaving only the time and field columns.
raw = flux_to_dataframe(srv, "from(bucket: \"env\") |> range(start: -1h) |> pivot(...)")
# raw has columns: result, table, _start, _stop, _measurement, _time, temp, humidity
df = clean_influx_df(raw)
# df has columns: _time, temp, humidityHigh-level helpers
InfluxFlux.measurement — Function
measurement(srv, bucket, measurement_name, from, to) -> DataFrameFetch a measurement over a time range and return it as a single DataFrame with one column per field and a _time column of epoch-nanosecond integers.
df = measurement(srv, "sensors", "temperature", now() - Hour(1), now())
# df columns: _time, indoor, outdoorThrows if the query returns more than one table group (i.e. multiple tag-set combinations). Use measurement_multi in that case.
InfluxFlux.measurement_multi — Function
measurement_multi(srv, bucket, measurement_name, from, to) -> Vector{DataFrame}Fetch a measurement over a time range and return one DataFrame per table group (typically one per tag-set combination). Each DataFrame has one column per field plus a _time column of epoch-nanosecond integers.
dfs = measurement_multi(srv, "sensors", "temperature", now() - Minute(10), now())
# dfs[1] — columns: _time, value (tag set A)
# dfs[2] — columns: _time, value (tag set B)See also measurement when only one table group is expected.
InfluxFlux.aggregate_measurement — Function
aggregate_measurement(srv, bucket, measurement_name, from, to, window; fn="mean")
-> DataFrameDownsample a measurement into fixed-width time buckets and return a single DataFrame. See aggregate_measurement_multi for supported fn values.
# Hourly means over the past day
df = aggregate_measurement(srv, "sensors", "temperature",
now() - Day(1), now(), Hour(1))
# df columns: _time, indoor, outdoor
# 10-minute maxima
df = aggregate_measurement(srv, "power", "consumption",
now() - Hour(6), now(), Minute(10); fn="max")Throws if the query returns more than one table group. Use aggregate_measurement_multi in that case.
InfluxFlux.aggregate_measurement_multi — Function
aggregate_measurement_multi(srv, bucket, measurement_name, from, to, window; fn="mean")
-> Vector{DataFrame}Like measurement_multi but downsamples each field with an aggregate function applied over window-sized time buckets. Returns one DataFrame per tag-set combination.
# 5-minute means over the last hour
dfs = aggregate_measurement_multi(srv, "sensors", "temperature",
now() - Hour(1), now(), Minute(5))
# Maximum instead of mean
dfs = aggregate_measurement_multi(srv, "sensors", "temperature",
now() - Hour(1), now(), Minute(5); fn="max")window can be any Period, e.g. Second(30), Minute(5), Hour(1).
Common values for fn: "mean", "median", "sum", "count", "min", "max". See the Flux aggregateWindow docs for the full list.
Schema inspection
InfluxFlux.list_buckets — Function
list_buckets(srv) -> Vector{String}Return the names of all buckets visible to the configured API token.
list_buckets(srv)
# ["_monitoring", "_tasks", "sensors", "power"]InfluxFlux.list_measurements — Function
list_measurements(srv, bucket) -> Vector{String}Return the measurement names stored in bucket.
list_measurements(srv, "sensors")
# ["humidity", "pressure", "temperature"]InfluxFlux.list_fields — Function
list_fields(srv, bucket) -> Vector{String}
list_fields(srv, bucket, measurement) -> Vector{String}Return field key names in bucket, optionally filtered to a single measurement.
list_fields(srv, "sensors")
# ["humidity", "pressure", "temperature"]
list_fields(srv, "sensors", "temperature")
# ["indoor", "outdoor"]Utilities
InfluxFlux.time_spec_to_epoc_ns — Function
time_spec_to_epoc_ns(time_spec::TimeSpec) -> IntConvert a TimeSpec value to an integer epoch-nanosecond timestamp (Unix × 10⁹).
Accepts an Int (returned as-is), a DateTime (treated as UTC), or a ZonedDateTime (converted to UTC first).
Error handling
All non-200 responses throw an InfluxFluxError with the HTTP status code, the InfluxDB error code (if the response body is JSON), and the error message.
try
df = measurement(srv, "no-such-bucket", "cpu", t0, t1)
catch e::InfluxFlux.InfluxFluxError
println("HTTP $(e.status): $(e.message)")
endNotes
- Read-only — there is no write or delete API.
- Bucket and measurement names are passed directly into Flux queries without escaping; callers are responsible for trusting input against their own server.
- The
_timecolumn in DataFrames returned bymeasurementandaggregate_measurementis an integer representing epoch nanoseconds.