Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

5. Expressions

An expression computes one value from a row: a filter’s predicate, a derived field, a sort key, a group measure. There is one evaluator for every stage, so anything that works in where works in derive, set, select … as, sort keys, group measures and sub-pipelines, with the same results and the same errors.

5.1 Forms

FormExample
Literal42, "paid", true, null
Field pathage, profile.country, o.user (an alias, in a join)
Optional pathnick?, profile?.country
Parameter or variable$min, $cities
Unarynot active, !active, -qty, +qty
Binaryage >= 18, price * qty
Grouping(city == "Paris" or city == "Lyon")
Membershipstatus in ["paid", "pending"], "dev" in tags, region not in ["US"]
Rangeage between 18 and 30, price not between 10 and 20
Array[qty, qty * 2, "x"]
Object{"a": 1}, merge(meta, {"v": 2})
Function callround(price * qty, 2), coalesce(nick, name)

Every form nests in every other: round(div(price * qty, qty - 1, 0), 2), max([a, b, c]) in allowed.

5.2 Precedence

From lowest to highest; binary operators associate to the left.

LevelOperators
1or, ||
2and, &&
3 (prefix)not, !
4==, !=
5<, <=, >, >=, in, not in, between … and …
6+, -
7*, /, %
8 (prefix)unary -, unary +

As in SQL and Python, not binds looser than comparisons: not city == "Berlin" is not (city == "Berlin"), and not active and age > 18 is (not active) and age > 18.

5.3 Semantics

ConstructRule
ComparisonsNumbers across integer and float; strings lexicographically; null with ==/!=; any other mix is an error
and, or, notOperands must be booleans. and and or short-circuit
in, not inThe right side must be an array (a literal, a field, a parameter). Items match with ==; other kinds simply do not match
between a and ba <= x and x <= b
ArithmeticSee 4.5
TextNo +: use concat
A bare booleanIs a predicate: where active, where true

5.4 Lazy evaluation

and, or, if, case and coalesce evaluate only what they need. This makes guards safe:

on users | where exists(nick) and nick != ""
on orders | derive unit = if(qty == 0, null, price / qty)
on rows | derive label = case(score >= 9, "A", score >= 7, "B", "C")

5.5 Constant folding

Once parameters and variables are bound, a conditional whose condition is constant is replaced by the branch it takes: if(false, a, b) is b, true or x is true, coalesce(null, x) is x, and case drops its false conditions. derive f = f writes nothing. So a switch driven by a parameter costs nothing at run time:

on ledger | derive entity = if($consolidate, null, entity) | group | by entity | sum amount | end

5.6 Function calls

Functions are called by name with parenthesized arguments; any expression may be an argument. Unknown names and wrong argument counts are refused when the query is parsed, before any data is read:

invalid expression "nope(age) > 1" for stage "where": unknown function `nope`; available functions: abs, sqrt, …
invalid expression "abs(age, 2) > 1" for stage "where": function `abs` expects 1 argument(s), got 2

The function reference lists all of them.

5.7 Expressions per stage

StageWhere an expression goes
whereThe predicate
selectEach item: <expression> as <name>
derive, setEach assignment: <path> = <expression>
sortEach key
group (compact)Each item, with as
group (compound)by keys, measure values, where filters of measures, by orders of first/last
bucketThe value bucketed
pivotrows, columns, values
fillfrom, to, step, and with values
lookup, unionEvery stage of the sub-pipeline, which can read the outer row through its alias
letEach value (no document fields: it is computed once, before the query runs)