FLWOR expressions, also known simply as FLWORs, are used for queries that are more complex. In addition to allowing more readable and structured selections, they allow functionality such as joining data from multiple sources, constructing new elements and attributes, evaluating functions on intermediate values, and sorting results.
FLWOR (pronounced "flower"), stands for "for, let, where, order by, return," the keywords that are used in the expression. Example 6-1 shows a FLWOR that is equivalent to the second path expression from the previous section.
Example 6-1. FLWOR
for $prod in doc("catalog.xml")//product let $prodDept := $prod/@dept where $prodDept = "ACC" or $prodDept = "WMN" return $prod/name
Of course, this is far more verbose, and for such a simple example, the path expression is preferable. However, this example is useful as an illustration before moving on to examples that are more complex. As you can see, the FLWOR is made up of several parts:
for
This clause sets up an iteration through the product
elements returned by the path expression. The variable $prod
is bound, in turn, to each product
in the sequence. The rest of the FLWOR is evaluated once for each product
, in this case, four times.
let
This clause binds the $prodDept
variable to the value of the dept
attribute.
where
This clause selects elements whose dept
attribute is equal to ACC
or WMN
.
return
This clause returns the name
child of each of the three product
elements that pass the where
clause.
The overall syntax of a FLWOR is shown in Figure 6-1. The details of the syntax of the for
and let
clauses are provided in the following sections.
There can be multiple for
and let
clauses, in any order, followed by an optional where
clause, followed by an optional order by
clause, followed by the required return
clause. A FLWOR must have at least one for
or let
clause.
FLWORs can be the whole query, or they can appear in other expressions such as in the return
clause of another FLWOR or even in a function call, as in:
max(for $prod in doc("catalog.xml")//product return xs:integer($prod/number))
The for
and return
keywords are aligned vertically here to make the structure of the FLWOR more obvious. This is generally good practice, though not always possible.
Let's take a closer look at the clauses that make up the FLWOR. The order by
clause is covered in Chapter 7.
XPath 2.0 does not support FLWORs, but instead supports a simplified subset called for
expressions, which can only have one for
clause and a return
clause. Any for
expression is also a valid FLWOR that returns the same results.
A for
clause, whose syntax is shown in Figure 6-2, sets up an iteration that allows the rest of the FLWOR to be evaluated multiple times, once for each item in the sequence returned by the expression after the in
keyword. This sequence, also known as the binding
sequence, can evaluate to any sequence of zero, one or more items. In the previous example, it was a sequence of product
elements, but it could also be atomic values, or nodes of any kind, or a mixture of items. If the binding sequence is the empty sequence, the rest of the FLWOR is simply not evaluated (it iterates zero times).
The FLWOR expression with its for
clause is similar to loops in procedural languages such as C. However, one key difference is that in XQuery, because it is a functional language, the iterations are considered to be in no particular order. They do not necessarily occur sequentially, one after the other. One manifestation of this is that you cannot keep variable counters that are incremented with each iteration, or continuously append to the end of a string variable with each iteration. "Working with Positions and Sequence Numbers" in Chapter 9 provides more information about simulating counters.
Another useful technique is to supply a sequence of integers in the for
clause in order to specify the number of times to iterate. This can be accomplished through a range expression, which creates a sequence of consecutive integers. For example, the range expression 1 to 3
evaluates to a sequence of integers (1, 2, 3). The FLWOR shown in Example 6-2 iterates three times and returns three oneEval
elements.
Example 6-2. Using a range expression
Query for $i in 1 to 3 return <oneEval>{$i}</oneEval> Results <oneEval>1</oneEval> <oneEval>2</oneEval> <oneEval>3</oneEval>
Range expressions can be included within parenthesized expressions, as in (1 to 3, 6, 8 to 10)
. They can also use variables, as in 1 to $prodCount
. Each of the expressions before and after the to
keyword must evaluate to an integer.
If the first integer is greater than the second, as in 3 to 1
, or if either operand is the empty sequence, the expression evaluates to the empty sequence. The reason for this is to ensure that for $i in 1 to count($seq)
does the expected thing even if $seq
is an empty sequence.
You can use the reverse
function if you want to descend in value, as in:
for $i in reverse(1 to 3)
You can also increment by some value other than 1 using an expression like:
for $i in (1 to 100)[. mod 2 = 0]
which gives you every other number (2, 4, 6, etc.) up to 100.
You can use multiple for
clauses in a FLWOR, which is similar to nested loops in a programming language. The result is that the rest of the FLWOR is evaluated for every combination of the values of the variables. Example 6-3 shows a query with two for
clauses, and demonstrates the order of the results.
Example 6-3. Multiple for clauses
Query for $i in (1, 2) for $j in ("a", "b") return <oneEval>i is {$i} and j is {$j}</oneEval> Results <oneEval>i is 1 and j is a</oneEval> <oneEval>i is 1 and j is b</oneEval> <oneEval>i is 2 and j is a</oneEval> <oneEval>i is 2 and j is b</oneEval>
The order is significant; it uses the first value of the first variable ($i
) and iterates over the values of the second variable ($j
), then takes the second value of $i
and iterates over the values of $j
.
Also, multiple variables can be bound in a single for
clause, separated by commas. This has the same effect as using multiple for
clauses. The example shown in Example 6-4 returns the same results as Example 6-3. This syntax is shorter but can be less clear in the case of complex expressions.
Example 6-4. Multiple variable bindings in one for clause
for $i in (1, 2), $j in ("a", "b") return <oneEval>i is {$i} and j is {$j}</oneEval>
Specifying multiple variable bindings (or multiple for
clauses) is especially useful for joining data. This is described further in the section "Joins," later in this chapter.
A let
clause is a convenient way to bind a variable to a value. Unlike a for
clause, a let
clause does not result in iteration; it binds the whole sequence to the variable rather than binding each item in turn. The let
clause serves as a programmatic convenience that avoids repeating the same expression multiple times. Using some implementations, it can also improve performance, because the expression is evaluated only once instead of each time it is needed.
The syntax of a let
clause is shown in Figure 6-3.
To illustrate the difference between for
and let
clauses, compare Example 6-5 with Example 6-2.
Example 6-5. Using a let clause with a range expression
Query let $i := (1 to 3) return <oneEval>{$i}</oneEval> Results <oneEval>1 2 3<oneEval>
The FLWOR with the let
clause returns only a single oneEval
element, because no iteration takes place and the return
clause is evaluated only once.
One or more let
clauses can be intermingled with one or more for
clauses. Each of the let
and for
clauses may reference a variable bound in any previous clause. The only requirement is that they all appear before any where
, order by
, or return
clauses of that FLWOR. Example 6-6 shows such a FLWOR.
Example 6-6. Intermingled for and let clauses
let $doc := doc("catalog.xml") for $prod in $doc//product let $prodDept := $prod/@dept let $prodName := $prod/name where $prodDept = "ACC" or $prodDept = "WMN" return $prodName
As with for
clauses, adjacent let
clauses can be represented using a slightly shortened syntax that replaces the let
keyword with a comma, as in:
let $prodDept := $prod/@dept, $prodName := $prod/name
Another handy use for the let
clause is to perform several functions or operations in order. For example, suppose I want to take a string and replace all instances of at
with @
, replace all instances of dot
with a period (.
), and remove any remaining spaces. I could write the expression:
replace(replace(replace($myString,'at','@'),'dot','.'),' ','')
but that is difficult to read and debug, especially as more functions are added. An alternative is the expression:
let $myString2 := replace($myString,'at','@') let $myString3 := replace($myString2,'dot','.') let $myString4 := replace($myString3,' ','') return $myString4
which makes the query clearer.
The where
clause is used to specify criteria that filter the results of the FLWOR. The where
clause can reference variables that were bound by a for
or let
clause. For example:
where $prodDept = "ACC" or $prodDept = "WMN"
references the $prodDept
variable. In addition to expressing complex filters, the where
clause is also very useful for joins.
Only one where
clause can be included per FLWOR, but it can be composed of many expressions joined by and
and or
keywords, as shown in Example 6-7.
Note that when using paths within the where
clause, they need to start with an expression that sets the context. For example, it has to say $prod/number > 100
rather than just number > 100
. Otherwise, the processor does not know where to look for the number
child.
The effective Boolean value of the where expression is calculated. This means that if the where expression evaluates to a Boolean value false
, a zero-length string, the number 0 or NaN
, or the empty sequence, it is considered false, and the return expression is not evaluated. If the effective Boolean value is true
, the return expression is evaluated. For example, you could use:
where $prod/name
which returns true
if $prod
has a name
child, and false
if it does not. As another example, you could use:
where $numProds
which returns true
if $numProds
is a numeric value that is not zero (and not NaN
). However, these types of expressions are somewhat cryptic, and it is preferable to use clearer expressions, such as:
where exists($prod/name) and $numProds > 0
The return
clause consists of the return
keyword followed by the single expression that is to be returned. It is evaluated once for each iteration, assuming the where expression evaluated to true
. The result value of the entire FLWOR is a sequence of items returned by each evaluation of the return
clause. For example, the value of the entire FLWOR:
for $i in (1 to 3) return <oneEval>{$i}</oneEval>
is a sequence of three oneEval
elements, one for each time the return
clause was evaluated.
If more than one expression is to be included in the return
clause, they can be combined in a sequence. For example, the FLWOR:
for $i in (1 to 3) return(
<one>{$i}</one>,
<two>{$i}</two>)
returns a sequence of six elements, two for each time the return
clause is evaluated. The parentheses and comma are used in the return
clause to indicate that a sequence of the two elements should be returned. If no parentheses or comma were used, the two
element constructors would not be considered part of the FLWOR.
When a variable is bound in a for
or let
clause, it can be referenced anywhere in that FLWOR after the clause that binds it. For example, if it is bound in a let
clause, it can be referenced anywhere in the FLWOR after that let
clause. This includes other subsequent let
or for
clauses, the where
clause, or the return
clause. It cannot be referenced in a for
clause that precedes the let
clause, and it should not be referenced in the let
clause itself, as in:
let $count := $count + 1
This is not illegal, but it will have unexpected results, as described in "Adding Sequence Numbers to Results" in Chapter 9.
If you bind two variables with the same name with the same containing expression, such as two for
or let
clauses that are part of the same FLWOR, you may again get unexpected results. This is because it will create two separate variables with the same name, where the second masks the first and makes it inaccessible.
[a] The at
clause, which allows for positional variables, is described in "Working with Positions and Sequence Numbers" in Chapter 9. An additional as
clause, useful for static typing, is allowed as part of the first variable declaration; this is described in "Type Declarations in FLWORs" in Chapter 14.
18.117.230.81