Predicates and Flow Control

Predicates and Sequence Flow Control

A predicate [...] keeps the items for which it is true. A number means a position (book[2], book[last()]); anything else becomes a Boolean, so book[supply] keeps books with a supply child. Predicates chain left to right, so book[price > 20][2] differs from book[2][price > 20]. And a predicate binds to its step: //name[1] means every name that is the first of its siblings, not the first in the document, which is (//name)[1].

XPath 3.1 also has flow control: for, let, if ... then ... else, the quantifiers some and every, the simple map ! (evaluate the right side once per item, with . bound to it) and the arrow => (pass a value as the next function's first argument):

Positional and Boolean predicates, and XPath's flow-control expressions
xp '//book[supply/price > 20][2]/@id ! string()' \
   'count(//contributor/name[1])' 'count((//contributor/name)[1])' \
   'for $b in //book[pages > 340] return $b/@id || ":" || $b/pages' \
   'let $p := //price return max($p) - min($p)' \
   'some $a in //availability satisfies $a = "out-of-stock"' \
   '//book[1]/title => upper-case() => string-length()'
Output
//book[supply/price > 20][2]/@id ! string() => "b3"
count(//contributor/name[1])           => 6
count((//contributor/name)[1])         => 1
for $b in //book[pages > 340] return $b/@id || ":" || $b/pages => "b2:428" "b5:344"
let $p := //price return max($p) - min($p) => 2.4509999999999998e1
some $a in //availability satisfies $a = "out-of-stock" => true()
//book[1]/title => upper-case() => string-length() => 16

The price spread came back in floating point because untyped prices became doubles (Numeric and Math Functions shows the fix). Note that / needs nodes on its left and sorts its result into document order, while ! accepts any items and keeps their order: use ! to compute a value per item.


Sequence Flow Control and Typing

XPath sequences are flat, ordered lists of items (nested sequences are automatically flattened). Flow-control constructs (for, let, if/then/else, some/every ... satisfies) and typing operators (instance of, castable as, cast as, treat as) build on this sequence model. See also FLWOR for the equivalent, more general expressions available in XQuery.

Each of the following expressions evaluates to the same sequence, (1, 2, 3, 4, 5, 6):
(3 idiv 2, (2, (3)), (), 4 to 6)
reverse(reverse(1 to 6))
(1 to 6)[. mod 1 eq 0]
(1 to 6)[. < 10]
(0 to 5)!(. + 1)
(1 to 3)!(.*2-1 , .*2)
let $a := (1 to 3), $b := (4 to 6) return ($a, $b)
for $i in (0,3), $j in (1,2,3) return ($i + $j)
if (5 le 5) then (1 to 6) else (0)
(1 to 6) treat as xs:decimal+

Each of the following expressions evaluates to true:
2 = (1,2,3)
3 > (2,4)
(1,2) = (2,3)
(1,2) != (2,3)
some $x in (1 to 3), $y in (2 to 4) satisfies $x + $y < 4
every $x in (1 to 3), $y in (2 to 4) satisfies $x + $y > 0
15 instance of xs:integer
15 instance of item()
(1,2) instance of xs:integer+
xs:unsignedByte(1) instance of xs:integer
xs:dateTime('2015-03-04T12:00:00') instance of xs:anyType
xs:dayTimeDuration('P9DT15H30M30S') instance of xs:duration
1.3 castable as xs:integer
1.3 cast as xs:boolean
xs:date('2015-08-31') > xs:date('2015-07-31')

A general comparison such as (1,2) = (2,3) is true if any pairing of items from the two sequences satisfies the comparison — it is equivalent to 1=2 or 1=3 or 2=2 or 2=3.


Each of the following expressions evaluates to the duration P30D (30 days):
xs:dayTimeDuration("P30D")
"P30D" cast as xs:dayTimeDuration?
xs:dayTimeDuration('P20D') + xs:dayTimeDuration('P10D')
xs:date('2015-03-04') - xs:date('2015-02-02')
ch03-sequence-flow.xpath.txt
merged-01-01-ch03-sequence-flow.xpath.txt
for $i in (0,3), $j in (1,2,3) return ($i + $j)
Output
(1, 2, 3, 4, 5, 6)