15 · Composing directives (@filter, @sort, @top, @source, @join)
What directives are and aren't
Directives (@filter, @sort, @top, @source, @join) live inside
a data block and shape the row set the block iterates over. They are
evaluated in a fixed order regardless of source-cell order:
@source <Name>— picks which source the block iterates over.@join <Source> on ...— pairs primary rows with rows from another source.@filter <condition>— keeps rows where the condition is truthy.@sort <column> [asc|desc]— orders the rows.@top <N>— keeps the first N rows after filtering and sorting.
Authoring tip: put the directives in the order they execute. That's not required by the spec — but it makes the template readable.
Composing them
A common shape: top 5 high-value Seoul renewals.
{{ @filter [Region] = "Seoul" }}
{{ @filter [Amount] > 1000 }}
{{ @sort [Amount] desc }}
{{ @top 5 }}
{{ [Account] }} | {{ [Amount] }}
Order of evaluation:
- Filter Region=Seoul.
- Filter Amount>1000 (compose with AND).
- Sort the surviving rows by Amount descending.
- Take the first 5.
Multiple @filter directives compose with AND
Per ADR-0029, multiple @filter in one block AND together. There is
no OR keyword. To express OR, either:
- Combine into one filter with
IN:{{ @filter [Region] in __lists__[active_regions] }} - Split into two data blocks (each in its own template region) and let the row sets union by virtue of both being rendered.
- Pre-process upstream.
Composing @source + @join
{{ @source Renewals }}
{{ @join Customers on Renewals[customer_id] = Customers[id] }}
{{ @filter Customers[tier] = "A" }}
{{ @sort Renewals[amount] desc }}
{{ @top 10 }}
{{ Renewals[customer_id] }}
{{ Customers[name] }}
{{ Renewals[amount] }}
Steps:
- Iterate Renewals (per
@source). - Inner-join with Customers by id; rows with no match drop.
- Keep only joined rows where Customers' tier is "A".
- Sort by Renewals.amount desc.
- Take top 10.
@filter can reference either source's columns; column resolution
uses the active block's source for bare brackets and the explicit
Source[Column] form for the joined side.
Forbidden compositions
Per ADR-0029:
- At most one
@sourceper data block. Duplicates raisexl3/directive/invalid-syntax. - At most one
@joinper data block. Multi-join is out of scope. - No self-join.
@join S on S[a] = S[b]whereSis the active source raisesxl3/join/bad-on-clause.
@top after @sort
{{ @sort [Amount] desc }}
{{ @top 10 }}
Top-N is meaningless without an ordering. If you write @top without
@sort, you get the first N rows in source order — which can be
useful but is rarely what authors mean.
Empty after filter
If @filter drops all rows, the data block expands to zero rows. The
template row's styling/formats stay on the output but no data row is
produced. Footer rows below the block stay visible.
Spec pointers
- ADR-0029 — Directive composition + source edge semantics.
spec/language.md"Filter", "Sort", "Top", "Source", "Join".- Cookbook 05 for
@filter in __lists__[…]. - Cookbook 07 for the
@source+@joinbasics. - Cookbook 09 for
@sort+@topbasics.