6. Anatomy of a query
[explain] [let … |]* <source> ( | <stage> )*
Here | stands for any stage separator: |, or ; and line breaks in a query that holds no |
(3.2).
A request is, in order: an optional explain, zero or more let headers, one source, and zero or more stages.
A request can instead be a view statement (define view …, undefine view …, chapter 11).
6.1 Sources
| Source | Reads |
|---|---|
on <collection> | The documents of a collection (from is a synonym) |
on <collection> as of <date | generation> | The collection as it was then (chapter 8) |
on <collection> history | One row per stored write of the collection |
on values [{…}, …] | Literal rows |
on view <name> [version <n>] | A view (chapter 11) |
Each takes an optional as <alias>. A query may be the source alone: on users returns every document.
Collections
- Dotted names work:
on shop.orders. - A collection exists from its first write; reading one that does not exist returns no rows (not an error).
- In a Place, the source reads only the rows of that Place (and of the App instance, when the request names one).
Aliases
At the top level, on users as u | where u.age > 30 and where age > 30 are the same: the qualifier is dropped
before planning (outside string literals), so an alias shadows a field of the same name. In a lookup or union
sub-pipeline, the alias names the outer row, and the sub-pipeline’s own alias names the joined row.
Literal rows: values
on values [{code: "FR", rate: 0.2}, {code: "DE", rate: 0.19}] | where rate > 0.19
Objects with bare or quoted keys. values works wherever a collection is read: lookup values […] as r | … | end,
union | on values […] | end, the one-line lookup values […] as v on code == account into hit. The rows are written
once into a constant collection named after their content (_values.v<hash>): read like any collection (indexes of
lookup, streaming), shared by every Place, never writable.
6.2 Stages
Simple stages
| <stage-name> <arguments>
Everything up to the next top-level separator is the stage’s argument text, which the stage’s own grammar reads.
Compound stages and end
Some stages open a sub-pipeline, a sequence of stages closed by | end:
on users
| lookup orders
| where status == "paid"
| into paid
| end
| select name, paid
| Policy | Stages | Rule |
|---|---|---|
| Always compound | union, pivot, window | Always open a sub-pipeline |
| Compound unless one-line | lookup / join | One-line when the header holds into (lookup t on a == b into f) |
| Compound when bare | group, load | Compound when written without arguments; one-line otherwise |
| Never compound | every other stage | Always simple |
Without |, the same query reads:
on users
lookup orders
where status == "paid"
into paid
end
select name, paid
- A missing
| endis an error:sub-pipeline opened by stage at … is missing '| end'; written with lines,`lookup` at line 2 opens a block that no `end` closes. - An
endwith nothing open is an error:unexpected 'end' outside a sub-pipeline(`end` at line N closes no blockwritten with lines). - Sub-pipelines nest: a
lookupinside aunionbranch inside a view.
The words inside a compound stage are directives, not stages: by, sum, avg… in group; rows, columns,
values, aggregate in pivot; partition by, order by, lag… in window; with, chunk in load.
6.3 Placement rules
| Rule | Error |
|---|---|
Nothing follows a terminal stage: set, delete, insert, load, into, count | operator … follows a terminal operator |
insert is the only stage of its pipeline | insert … must be the only operator |
load is the first stage | load operator at index N must be first |
select, sort, limit, skip appear once per pipeline (first may repeat) | limit operator is duplicated |
| Unknown stage names are refused | unknown stage "frobnicate" |
| Arguments are checked before any data is read | invalid limit syntax … expected a non-negative integer |
Sub-pipelines of lookup and union only read; on, from, with, chunk are refused inside a lookup | a planning error naming the stage |
6.4 Read and write queries
A query is a write when it holds insert, set, delete, into or load; otherwise it only reads.
- A read streams its rows as they are produced, within the memory contract of chapter 14.
- A write runs in one transaction: everything commits, or nothing does.
- A
readOnlyrequest refuses a write (query.read_only_violation), and so do view bodies andletqueries.
6.5 Scope: Places and App instances
When a request runs in a Place:
- every source reads only the rows of that Place (
_place), and of the App instance when the request names one (_app_instance); - every write stamps its rows with that scope;
on viewreads the Place’s view of that name, else the standard one;- materialized views keep one cache per scope (chapter 12).
The language does not change: the scope is applied by the Core, not written in the query.