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 EXPR

Loop 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 := EXPR

Bind 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 CONDITION

Keep 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 EXPR

Sort 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 := EXPR

Collapse 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 $i

Bind 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 $i

Positional 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 empty

Keep 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 EXPR

Produce 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

← Back to the XQuery reference