Browse documentation
CanLang handbook

Queries and aggregates

Queries are typed expressions over the records visible in their evaluation scope. Their tail order is archived=include, as, where, order=, then select, each at most once. Grouping uses the group(domain as row,key) builtin, rather than a SQL-style grouping clause. Query work is bounded; there is no query-tail limit keyword. Query aliases let a subsequent expression refer to the selected row; they do not widen access. Aggregates operate only on their declared input and authorization scope.

On this page

Query authority and provisional state

A viewer-facing list uses the caller's row and field policy. A privileged report must declare its authority scope and operation admission explicitly; its description must identify the information it exposes. A CRUD when predicate sees the proposed candidate through row, but its model scans see provisional owner state immediately before that write: earlier staged effects are visible, while the proposed candidate is not yet staged. An update scan still sees the old stored row. Final invariants instead inspect completed provisional state.

Default record queries omit archived and expired rows. Model archived=include includes archived rows but never expired content. Record order defaults to created,id, with ID appended as the final tie-breaker. Array queries retain input order unless explicitly ordered and use original position to break equal-key ties. first returns nullable; all over an empty input is true and any is false. Empty numeric/duration sums are zero; money sums require an explicit currency unless a nonempty literal proves a value exists.

Do not treat a query as SQL or use cross-owner joins as a consistency mechanism. Read views across owners are eventual and business mutations cannot atomically span owners. See read policies, expressions, and transactions.