FLWOR Expressions

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.

Syntax of a FLWOR

Figure 6-1. Syntax of a FLWOR

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.

Important

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.

The for Clause

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).

Syntax of a for clauseThe 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.

Figure 6-2. Syntax of a for clause[a]

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.

Range expressions

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.

Multiple for clauses

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.

The let Clause

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.

Syntax of a let clause

Figure 6-3. Syntax of a let clause

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

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.

Example 6-7. A where clause with multiple expressions

for $prod in doc("catalog.xml")//product
let $prodDept := $prod/@dept
where $prod/number > 100
      and starts-with($prod/name, "F")
      and exists($prod/colorChoices)
      and ($prodDept = "ACC" or $prodDept = "WMN")
return $prod

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

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.

The Scope of Variables

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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.117.230.81