XQuery Reference

XQuery Functions

The built-in functions you reach for most in real XQuery work, grouped by the job they do rather than by specification chapter. Every one of these is part of the shared XPath 3.1 function library, so the same call works in XPath mode too — what XQuery adds is the FLWOR and constructor machinery around them. Each entry says when you want the function, not just what it does.

concat(…) / string-join(…)

Strings — build one string out of several values

Use `concat()` when you have a fixed number of parts, and `string-join()` when you have a sequence of unknown length. `concat()` accepts only single items, so passing a sequence of more than one node raises an error — wrap it in `string-join()` instead.

Example: concat($r/name, ' (', $r/cuisine, ')')

substring / contains / starts-with

Strings — test and slice text

`contains()`, `starts-with()` and `ends-with()` return booleans and are the everyday way to filter on text. `substring()` is 1-based, and `substring-before()` / `substring-after()` split around a delimiter — handy for pulling an ID out of a compound attribute value.

Example: //recipe[contains(name, 'Curry')]/name

upper-case / lower-case / normalize-space

Strings — normalise before comparing

XPath and XQuery string comparison is case-sensitive and whitespace-sensitive, so most 'my query returns nothing' bugs are fixed by wrapping both sides in `lower-case()` and `normalize-space()`. `normalize-space()` collapses internal runs of whitespace and trims the ends — essential when matching text that was pretty-printed across lines.

Example: //recipe[lower-case(cuisine) = 'thai']

tokenize / replace / matches

Strings — regular expressions

`matches()` tests, `replace()` rewrites, and `tokenize()` splits — all three take XPath regex syntax, which is close to but not identical to Perl or JavaScript. Escape backslashes carefully inside string literals, and remember that `replace()` uses `$1` for capture groups.

Example: tokenize(//album[1]/@genre, ',\s*')

count / sum / avg / min / max

Numbers — aggregate a sequence

These atomise their argument, so they work directly on attribute or element nodes without an explicit conversion in most cases. `count()` is the only one that never errors on non-numeric input, so use it when you just need cardinality. On an empty sequence, `count()` returns 0 while `sum()` returns 0 and `avg()` returns the empty sequence.

Example: sum(//album/tracks/track/@number)

round / floor / ceiling / round-half-to-even

Numbers — control rounding

`round()` rounds halves upward, which quietly biases financial totals; `round-half-to-even()` is the banker's-rounding alternative and takes an optional precision argument. `format-number()` is the one to use when the output is for display rather than further arithmetic.

Example: round-half-to-even(avg(//album/@year), 0)

distinct-values / reverse / subsequence

Sequences — reorder and trim

`subsequence($seq, $start, $length)` is the 1-based way to page through a sequence and reads better than a positional predicate for anything past the first item. `reverse()` flips order without sorting, and `distinct-values()` removes duplicates after atomisation — so it returns values, never nodes.

Example: subsequence(reverse(//recipe/name), 1, 2)

empty / exists / head / tail

Sequences — guard before you compute

`empty()` and `exists()` are the readable alternatives to comparing `count(...)` against zero, and they let the engine stop early. `head()` and `tail()` split the first item off a sequence, which is the idiomatic base case for a recursive `declare function`.

Example: if (exists(//recipe[cuisine = 'Thai'])) then 'yes' else 'no'

for-each / filter / fold-left

Sequences — higher-order functions

These take function items as arguments and are shared with XPath 3.1. In XQuery they are often the shorter form of a whole FLWOR: `filter()` replaces a `where`, `for-each()` replaces a simple `for ... return`, and `fold-left()` expresses accumulations that would otherwise need recursion.

Example: for-each(//recipe/name, function($n) { upper-case($n) })

current-date / xs:date casts

Dates and numbers — cast untyped text before comparing

Untyped XML text is not a date or a number until you cast it: `xs:date($v)`, `xs:integer($v)` or `$v cast as xs:date`. Once typed, dates support comparison and subtraction (yielding a duration), and `current-date()` gives you today for relative tests. Note that this engine does not implement `format-date()` — build display strings with `concat()` and `substring()` instead.

Example: for $a in //album return xs:integer($a/@year) + 1

name / local-name / namespace-uri

Nodes — inspect names and namespaces

`local-name()` ignores prefixes entirely, which is why it is the standard escape hatch for documents in a default namespace. `name()` returns the prefixed name as written, and `namespace-uri()` gives the URI a prefix resolves to — useful for confirming a document really is namespaced before you fight with prefixes.

Example: //*[local-name() = 'recipe']/name

data / string / node-name

Nodes — convert between nodes and values

`string()` gives you the string value of a node, `data()` gives its typed value (a string for untyped XML), and both are how you stop a result from being a node when you want text. In a constructed attribute the conversion happens implicitly, which is why `id="{$r/@id}"` works without a wrapper.

Example: for $r in //recipe return data($r/@difficulty)

← Back to the XQuery reference