Query functions
These are the functions of the pipe language (stats, eval) and the KQL match() helper. The table is checked against the registry in crates/wt-search/src/functions.rs by the test function_catalog_matches_doc: one row per function, written exactly as the registry renders it.
Aggregates and timeslice go in stats; scalar functions go in eval and in aggregate arguments. Arguments that name a log attribute are converted to numbers where a number is needed (sum, avg, min, max, percentile, arithmetic, comparisons with a number); a value that is not a number counts as missing.
Function | Kind | Description |
|---|---|---|
| aggregate | Rows, or rows where x is not null. |
| aggregate | Exact number of distinct values of x. |
| aggregate | Sum of x as a number. |
| aggregate | Mean of x. |
| aggregate | Smallest x. |
| aggregate | Largest x. |
| aggregate | The p-th percentile of x; p is a number above 0 up to 100. |
| aggregate | Rows (or the sum of x) per second over the time slice, or over the range without one. |
| bucket | Group by time buckets of d (30s, 5m, 1h, 1d; 1 second to 1 day); the column is bucket. |
| scalar | The first argument that is not null or empty. |
| scalar | a when cond is true, else b. |
| scalar | Lower-case s. |
| scalar | Upper-case s. |
| scalar | The string at a dotted path in the JSON text s (at most 8 steps). |
| scalar | Part of s starting at from (1-based). |
| scalar | Capture group idx (1 to 9) of the first RE2 match of pattern in s, or empty. |
| scalar | Part idx (1-based, at most 1000) of s split on sep, or empty. |
| scalar | Length of s in bytes. |
| aggregate | Approximate number of distinct values of x (cheaper than count_distinct). |
| aggregate | The q-th quantile of x from a t-digest sketch; q is above 0 up to 1. |
| aggregate | The k (1 to 50) most frequent values of x as one comma-separated text. |
| scalar | The current time in nanoseconds since the epoch. |
| scalar | Nanoseconds since the epoch for the time that long ago (1s to 30d: s, m, h, d, w). |
| scalar | Round the time column t down to a second, minute, hour, day, week or month. |
| scalar | x rounded to n (0 to 9, default 0) decimal places. |
| scalar | x rounded down. |
| scalar | x rounded up. |
| scalar | Absolute value of x. |
| scalar | 1 when the IPv4 address is inside the range, else 0. |
| scalar | One part of a URL: scheme, host, port, path or query. |
| scalar | A 64-bit hash of s as text, for grouping without showing the value. |
| scalar | s encoded as base64. |
| scalar | x as text. |
| scalar | x as a number, or missing when it is not one. |
| scalar | Items of a JSON array in s, or of s split on sep. |
| scalar | 1 when s split on sep has the item v, else 0. |
| scalar | Number of distinct items of s split on sep. |
| scalar | s with numbers, ids and uuids replaced by <*>, to group lines by message shape. |
Each function compiles to an allow-listed ClickHouse function; anything else is rejected by the SQL surface (crates/wt-sql).
Calculated fields
A calculated field is a saved scalar expression that you reference as #name in a pipe query, for example logs | eval slow = #is_slow | stats count() by slow. They belong to one user in one project, at most 5 each, and a query may use at most 5. #name is replaced by (expression) before the query is parsed, so it works wherever a scalar does (eval, aggregate arguments, by after eval) and nowhere else. A calculated field cannot use another one. Manage them with GET/POST /v1/projects/{p}/calculated-fields and DELETE /v1/projects/{p}/calculated-fields/{id}.
Dataset field mappings
PUT /v1/projects/{p}/datasets/{id}/field-mappings stores up to 64 aliases from a short name to an OpenTelemetry attribute key (resource. prefix for resource attributes). A pipe query that names that dataset (dataset in the request) reads the alias as the attribute. A real column of the same name wins.
Stages
The stage list, with an example for each, is served by GET /v1/query/reference?lang=pipe and comes from crates/wt-search/src/stage_ref.rs; the test pipe_stage_reference_matches_parser parses every example. Notes on the less obvious ones:
parseandparse-wherecaptures can be typed:%{ms:int},float,ip,word,uuid,hex. Typed numbers become numeric columns.parse-wheredrops rows where any capture is empty.dedupkeeps the oldest row per key and orders the result by time.joinandunionread a second query in[ ... ](no nesting, at most 2 per query). The joined query must end instatsorfields; names may not clash.timewrapneedsstats ... by timeslice(..)first and addsperiodandoffset_s.streamstatssupportscount,sum,avg,minandmaxoverwindowrows (default 10, at most 1000) ordered by time.
Log patterns
The fingerprint pipeline stage adds pattern_id (a stable hash of the masked message) and pattern to each log at ingest, so logs | stats count() by pattern_id and ingest-time drop or metric rules work without clustering. pattern(body) gives the same masked shape at query time. Pattern clusters (POST /v1/projects/{p}/logs/patterns) also return typed_patterns, where a wildcard keeps its type (<int>, <ip>, <uuid>, <hex>, <float>, <str>) when every line agreed. POST /v1/projects/{p}/logs/patterns/action returns the pipeline stages that drop a pattern or count it as a metric; it changes nothing.