XQuery Reference
FLWOR Expressions
FLWOR — For, Let, Where, Order by, Return — is the loop at the heart of XQuery. Where an XPath path expression can only select nodes in document order, a FLWOR binds variables, filters, groups, sorts, and then shapes one result per iteration. If you have ever wanted XPath to produce a sorted report, or one summary row per author, this is the construct that does it.
for $x in EXPRLoop over a sequence, binding one item at a time
The for clause is the engine of a FLWOR. It evaluates the expression once, then runs everything after it once per item in the result, with $b bound to the current item. Multiple for clauses nest, producing every combination — the XQuery equivalent of a nested loop.
Example: for $b in //book
let $x := EXPRBind a name to a value once, without looping
A let clause binds the whole result of an expression to a variable — it does not iterate. Use it to name a sub-expression you reference more than once, or to hold an entire sequence (for example, everything in a group after a group by).
Example: let $price := $b/price
where CONDITIONKeep only the iterations where the condition is true
where filters the tuples produced by the preceding for and let clauses. It does the same job as an XPath predicate, but it runs after the bindings exist, so it can compare variables from several clauses at once.
Example: where $b/price > 10
order by EXPRSort the iterations before they reach return
Path expressions always return nodes in document order; order by is the only way to change that. Add ascending (the default) or descending, comma-separate several keys for tie-breaking, and prefix with stable when equal keys must keep their original relative order.
Example: order by $b/title descending
group by $k := EXPRCollapse iterations into one per distinct key
After a group by, the FLWOR emits one tuple per distinct key value. Every other in-scope variable is rebound to the whole group as a sequence — which is exactly why count($b), sum($b/price) and string-join() work naturally in the return clause. Note: the Sandbox's evaluation engine (FontoXPath) parses this clause but does not implement it, so a query using it errors here even though it is valid XQuery 3.1. Rewrite it as `for $k in distinct-values(...)` with a `let $group := $items[... = $k]` inside; the Sandbox offers a one-click rewrite when it sees this.
Example: group by $author := $b/author
count $iBind a running 1-based position number
The count clause numbers the tuples that reach it, starting at 1. Because it sits after order by, the numbers reflect the sorted order — the reliable way to produce a ranked list. Note: the Sandbox's evaluation engine (FontoXPath) parses this clause but does not implement it, so a query using it errors here even though it is valid XQuery 3.1. Use `at $i` on the for clause, or `position()` over a pre-sorted sequence, instead.
Example: count $i
at $iPositional variable on a for clause
A positional variable numbers items as they come out of the for clause's source sequence, before any sorting. Use at when you care about the original position, and count when you care about the output position.
Example: for $b at $i in //book
allowing emptyKeep an iteration even when the sequence is empty
Normally a for clause over an empty sequence produces no tuples at all, so the whole iteration disappears. allowing empty binds the variable to the empty sequence instead — the XQuery equivalent of a left outer join.
Example: for $r allowing empty in $b/review
return EXPRProduce the output for each surviving iteration
Every FLWOR ends with exactly one return, and it runs once per remaining tuple. The results are concatenated into a single sequence. The return body is usually either a plain expression or constructed markup.
Example: return $b/title