SELECT and OUTPUT
Querying elements and shaping printed rows.
SELECT never modifies a document — it only prints.
SELECT GROUP
; print the whole group as-is
SELECT GROUP goods;
; without FOREACH, SELECT loops implicitly over the group's children
SELECT GROUP goods OUTPUT id, level;
; with FOREACH: filter and shape rows explicitly
SELECT GROUP goods
FOREACH good IN goods
WHERE good.cost > 500
OUTPUT good.id, good.cost, good.cost * 2 AS double_cost
;SELECT TAG / SELECT ROOT
SELECT TAG t prints every element with tag t, at any depth; SELECT ROOT drops the tag filter and prints every element in the document, any
tag — see Group vs. Tag vs. Root:
SELECT TAG ItemSpec; all ItemSpec elements, any depth
SELECT ROOT; every element in the document, any tagSELECT ... AS alias
AS alias after any selector (GROUP, TAG, or ROOT) renames the
printed tag on the way out — display-only, the document itself is
untouched:
SELECT GROUP goods AS items; ; <items>...</items>, children unchanged
SELECT TAG ItemSpec AS Item; ; every match printed as <Item>
; composes with FOREACH — applies to whatever gets printed, WHERE'd rows
; included, and to OUTPUT * (a whole-element projection)
SELECT GROUP goods AS items
FOREACH good IN goods
WHERE good.cost > 500
;Only the outermost element's tag changes; children keep their real tags.
For a permanent rename that edits the document, see RENAME in
Mutations.
OUTPUT
OUTPUT is the emission point — like a SQL column list. It's evaluated
when execution reaches it, and prints a flat element containing only the
cited attributes/expressions:
OUTPUT *— the whole current element. This is the implicit default of aSELECTloop that has noOUTPUTat all.OUTPUT good.id, good.cost— only those attributes.OUTPUT good.cost * 2 AS double_cost— a computed expression needsASto name its output column; bare attribute references don't.- A missing attribute is silently omitted from the row (not printed as
empty) — contrast with
WHERE, where a missing attribute evaluates as false.
OUTPUT also works inside a plain FOREACH (not just SELECT loops), and
inside nested loops — where it joins scopes from multiple levels into one
flat row:
FOREACH office IN offices
FOREACH member IN office
WHERE member.cost > 5
OUTPUT office.id AS office, member.name, member.cost
;Here each printed row combines the outer office element with the inner
member element currently being visited.
Aggregate OUTPUT: COUNT, MIN, MAX, SUM, AVG, FIRST, LAST, ANY, ALL
A regular OUTPUT prints one row per element the loop reaches. An
aggregate OUTPUT instead summarizes every reached element into a
single line of comma-joined plain values — no XML wrapper:
FOREACH v IN ItemSpec WHERE v.type = 0 OUTPUT COUNT(v.id);
; -> 2
FOREACH v IN ItemSpec WHERE v.type = 0
OUTPUT MIN(v.cost), MAX(v.cost), SUM(v.cost), AVG(v.cost)
;
; -> 10,30,40,20
FOREACH v IN ItemSpec WHERE v.type = 0 OUTPUT FIRST(v.name), LAST(v.name);
; -> a,b
FOREACH v IN ItemSpec OUTPUT COUNT(*); ; every row, nulls included
; -> 3
FOREACH v IN ItemSpec WHERE v.type = 0 OUTPUT ANY(v.cost > 25), ALL(v.cost > 5);
; -> true,trueRules:
COUNT(expr)counts elements whereexpris non-null.COUNT(*)is the one function that takes a literal*instead of an expression: it counts every reached row unconditionally, including ones a plainCOUNT(expr)would skip for being null.MIN/MAX/SUM/AVGignore elements whereexpris missing or non-numeric (same null-skipping spirit as everywhere else in the language).FIRST(expr)/LAST(expr)return the first/last non-null value ofexprin reach order (nulls skipped, same asMIN/MAX).ANY(expr)/ALL(expr)are boolean aggregates over every reached row's truthiness (a missing attribute is falsy, same asWHERE) — nothing is skipped, so every reached row counts toward the result.- Zero contributing elements reports
0forCOUNT/MIN/MAX/SUM/AVG, an empty value forFIRST/LAST,falseforANY, andtrueforALL(vacuously — same convention as SQL'sALLover an empty set). - An aggregate
OUTPUTmust be the loop's onlyOUTPUT— mixing it with a plain attribute/expression, or with a secondOUTPUT, is a parse error, since there's no single row shape that could satisfy both a per-element projection and a whole-loop summary. - Aliases (
AS name) are accepted for readability but aren't printed — a single summary row has nothing to disambiguate. - Aggregate functions are only valid as a direct
OUTPUTitem, not insideWHEREor aSETexpression.
Possible future additions: CONCAT/GROUP_CONCAT (join values with a
separator), a distinct-values COUNT.