Main Page » XQuery » XQuery 3.1

XQuery 3.1

This article provides a summary of the most important features of the XQuery 3.1 Recommendation.

Maps

A map is a function that associates a set of keys with values, resulting in a collection of key/value pairs. Each key/value pair in a map is called an entry. A key is an arbitrary atomic value, and the associated value is an arbitrary sequence. Within a map, no two entries have the same key, when compared using the eq operator. It is not necessary that all the keys should be mutually comparable (for example, they can include a mixture of integers and strings).

Maps can be constructed as follows:

map { },                                    (: empty map :)
map { 'key': true(), 1984: (<a/>, <b/>) },  (: map with two entries :)
map:merge(                                  (: map with ten entries :)
  for $i in 1 to 10
  return map { $i: 'value' || $i }
)

The function corresponding to the map has the signature function($key as xs:anyAtomicType) as item()*. The expression $map($key) returns the associated value; the function call map:get($map, $key) is equivalent. For example, if $books-by-isbn is a map whose keys are ISBNs and whose associated values are book elements, then the expression $books-by-isbn("0470192747") returns the book element with the given ISBN. The fact that a map is a function item allows it to be passed as an argument to Higher-Order Functions that expect a function item as one of their arguments. As an example, the following query uses the higher-order function fn:for-each($f, $seq) to extract all bound values from a map:

let $map := map { 'foo': 42, 'bar': 'baz', 123: 456 }
return for-each(map:keys($map), $map)

This returns some permutation of (42, 'baz', 456).

Because a map is a function item, functions that apply to functions also apply to maps. A map is an anonymous function, so fn:function-name returns the empty sequence; fn:function-arity always returns 1.

Like all other values, maps are immutable. For example, the map:remove function creates a new map by removing an entry from an existing map, but the existing map is not changed by the operation. Like sequences, maps have no identity. It is meaningful to compare the contents of two maps, but there is no way of asking whether they are "the same map": two maps with the same content are indistinguishable.

Maps may be compared using the fn:deep-equal function. Visit Map Functions for further operations on maps.

Arrays

An array is a function that associates a set of positions, represented as positive integer keys, with values. The first position in an array is associated with the integer 1. The values of an array are called its members. In the type hierarchy, array has a distinct type, which is derived from function. In BaseX, arrays (as well as sequences) are based on an efficient Finger Tree implementation.

Arrays can be constructed in two ways. With the square bracket notation, the comma serves as delimiter:

[],            (: empty array :)
[ (1, 2) ],    (: array with single member :)
[ 1 to 2, 3 ]  (: array with two members; same as: [ (1, 2), 3 ] :)

With the array keyword and curly brackets, the inner expression is evaluated as usual, and the resulting values will be the members of the array:

array { },           (: empty array;              same as: array { () } :)  
array { (1, 2) },    (: array with two members;   same as: array { 1, 2 } :)
array { 1 to 2, 3 }  (: array with three members; same as: array { 1, 2, 3 } :)

The function corresponding to the array has the signature function($index as xs:integer) as item()*. The expression $array($index) returns an addressed member of the array. The following query returns the five array members 48 49 50 51 52 as result:

let $array := array { 48 to 52 }
for $i in 1 to array:size($array)
return $array($i)

Like all other values, arrays are immutable. For example, the array:reverse function creates a new array containing a re-ordering of the members of an existing array, but the existing array is not changed by the operation. Like sequences, arrays have no identity. It is meaningful to compare the contents of two arrays, but there is no way of asking whether they are "the same array": two arrays with the same content are indistinguishable.

Atomization

If an array is atomized, all of its members will be atomized. As a result, an atomized item may now result in more than one item. Some examples:

data([ 1 to 2 ])      (: returns the sequence 1, 2 :)
[ 'a', 'b', 'c' ] = 'b'  (: returns true :)
<a>{ [ 1, 2 ] }</a>      (: returns <a>1 2</a> :)
array { 1 to 2 } + 3     (: error: the left operand returns two items :)

Atomization also applies to function arguments. The following query returns 5, because the array will be atomized to a sequence of 5 integers:

let $f := function($x as xs:integer*) { count($x) }
return $f([ 1 to 5 ])

However, the next query returns 1, because the array is already of the general type item(), and no atomization will take place:

let $f := function($x as item()*) { count($x) }
return $f([ 1 to 5 ])

Arrays can be compared with the fn:deep-equal function. Visit Array Functions for further operations on arrays.

Lookup Operator

The lookup operator provides some syntactic sugar to access values of maps or array members. It is introduced by the question mark (?) and followed by a specifier. The specifier can be:

  1. A wildcard *,
  2. The name of the key,
  3. The integer offset, or
  4. Any other parenthesized expression.

The following example demonstrates the four alternatives:

let $map := map { 'R': 'red', 'G': 'green', 'B': 'blue' }
return (
  $map?*           (: returns all values; same as: map:keys($map) ! $map(.) :),
  $map?R           (: returns the value for key 'R'; same as: $map('R') :),
  $map?('G', 'B')  (: returns the values for key 'G' and 'B' :)
),

let $array := [ 'one', 'two', 'three' ]
return (
  $array?*         (: returns all values; same as: (1 to array:size($array)) ! $array(.) :),
  $array?1         (: returns the first value; same as: $array(1) :),
  $array?(2 to 3)  (: returns the second and third value; same as: (1 to 2) ! $array(.) :)
)

The lookup operator can also be used without left operand. In this case, the context item will be used as input. This query returns Akureyri:

let $maps := (
  map { 'name': 'Guðrún', 'city': 'Reykjavík' },
  map { 'name': 'Hildur', 'city': 'Akureyri' }
)
return $maps[?name = 'Hildur'] ?city

Arrow Operator

The arrow operator => provides a convenient alternative syntax for passing on functions to a value. The expression that precedes the operator will be supplied as first argument of the function that follows the arrow. If $v is a value and f() is a function, then $v => f() is equivalent to f($v), and $v => f($j) is equivalent to f($v, $j):

(: Returns 3 :)
count(('A', 'B', 'C')),
('A', 'B', 'C') => count(),
('A', 'B', 'C') => (function( $sequence) { count( $sequence)})(),

(: Returns W-E-L-C-O-M-E :)
string-join(tokenize(upper-case('w e l c o m e')), '-'),
'w e l c o m e' => upper-case() => tokenize() => string-join('-'),

(: Returns xfmdpnf :)
codepoints-to-string(
  for $i in string-to-codepoints('welcome')
  return $i + 1
),
(for $i in 'welcome' => string-to-codepoints()
 return $i + 1) => codepoints-to-string()

The syntax makes nested function calls more readable, as it is easy to see if parentheses are balanced.

String Constructor

The string constructor has been inspired by here document literals of the Unix shell and script languages. It allows you to generate strings that contain various characters that would otherwise be interpreted as XQuery delimiters.

The string constructors syntax uses two backticks and a square bracket for opening and closing a string:

(: Returns "This is a 'new' & 'flexible' syntax." :)
``["This is a 'new' & 'flexible' syntax."]``

XQuery expressions can be embedded via backticks and a curly bracket. The evaluated results will be separated with spaces, and all strings will eventually be concatenated:

(: Returns »Count 1 2 3, and I will be there.« :)
let $c := 1 to 3
return ``[»Count `{ $c }`, and I will be there.«]``

Serialization

Two Serialization methods have been added to the Serialization spec:

Adaptive Serialization

The adaptive serialization provides an intuitive textual representation for all XDM types, including maps and arrays, functions, attributes, and namespaces. All items will be separated by the value of the item-separator parameter, which by default is a newline character. It is e.g. utilized by the function fn:trace.

Example:

declare option output:method 'adaptive';

<element id='id0'/>/@id,
xs:token("abc"),
map { 'key': 'value' },
true#0

Result:

id="id0"
xs:token("abc"),
map { "key": "value" }
true#0

JSON Serialization

The new json serialization output method can be used to serialize XQuery maps, arrays, atomic values and empty sequences as JSON.

The json output method has been introduced in BaseX before it was added to the official specification. It complies with the standard serialization rules and, at the same time, preserves the existing semantics:

  • If an XML node of type element(json) is found, it will be serialized following the serialization rules for JSON Functions.
  • Any other node or atomic value, map, array, or empty sequence will be serialized according to the rules in the specification.

The following two queries will both return the JSON snippet { "key": "value" }:

declare option output:method 'json';
map { "key": "value" }
declare option output:method 'json';
<json type='object'>
  <key>value</key>
</json>

Functions

Numerous new functions have been added to the specification. Please visit the corresponding pages for more details:

  • Array Functions: array:size, array:append, array:subarray, array:remove, array:insert-before, array:head, array:tail, array:reverse, array:join, array:flatten, array:for-each, array:filter, array:fold-left, array:fold-right, array:for-each-pair
  • Map Functions: map:merge, map:size, map:keys, map:contains, map:get, map:entry, map:put, map:remove, map:for-each
  • Standard Functions: too many to list

The functions fn:transform and fn:load-xquery-module may be added in a future version of BaseX.

Binary Data

Items of type xs:hexBinary and xs:base64Binary can be compared against each other. The following queries all yield true:

xs:hexBinary('') < xs:hexBinary('bb'),
xs:hexBinary('aa') < xs:hexBinary('bb'),
max((xs:hexBinary('aa'), xs:hexBinary('bb'))) = xs:hexBinary('bb')

Collations

XQuery 3.1 provides a default collation, which allows for a case-insensitive comparison of ASCII characters (A-Z = a-z). This query returns true:

declare default collation
  'http://www.w3.org/2005/xpath-functions/collation/html-ascii-case-insensitive';
'HTML' = 'html'

If the ICU Library is downloaded and added to the classpath, the full Unicode Collation Algorithm features become available in BaseX:

(: returns 0 (both strings are compared as equal) :)
compare('a-b', 'ab', 'http://www.w3.org/2013/collation/UCA?alternate=shifted')

Enclosed Expressions

Enclosed expression is the syntactical term for the expressions that are specified inside a function body, try/catch clauses, node constructors and some other expressions. In the following example expressions, its the empty sequence:
declare function local:x() { () };
try { () } catch * { () },
element x { () },
text { () }

With XQuery 3.1, the expression can be omitted. The following query is equivalent to the upper one:

declare function local:x() { };
try { } catch * { },
element x { }
text { }

Changelog

Version 8.6
  • Updated: Collation argument was inserted between first and second argument.
Version 8.4Version 8.2
  • Added: fn:json-to-xml, fn:xml-to-json.
Version 8.1
  • Updated: arrays are now based on a Finger Tree implementation.

⚡Generated with XQuery