Using the At Keyword in XQuery FLWOR Loops

This article provides a comprehensive overview of how positional variables operate within XQuery FLWOR expressions using the at keyword. It covers the fundamental syntax, 1-based indexing mechanism, scoping rules, and practical examples of tracking item positions when querying XML collections and node sequences.


Understanding the at Keyword Syntax

In XQuery, a FLWOR (For, Let, Where, Order by, Return) expression processes sequences of nodes or atomic values. The for clause iterates over each item in a sequence. By appending the at keyword followed by a variable name, you bind a secondary variable that automatically captures the 1-based index of the current item being processed.

The basic syntax is structured as follows:

for $item at $position in $sequence
return ...

Key Operational Rules of Positional Variables

  1. 1-Based Indexing: Unlike many programming languages that use zero-based indexing, XQuery positional variables are 1-based. The first item in the sequence assigns $position = 1, the second $position = 2, and so on.
  2. Scope: The positional variable is scoped to the specific for iteration and is accessible throughout the subsequent where, order by, and return clauses of that iteration.
  3. Evaluation Timing with Filtering: The positional variable reflects the position of the item in the input sequence before any where filtering is applied. If a where clause evaluates to false, that position number is simply omitted from the final output; it does not reset or re-index remaining items.
  4. Multiple for Clauses: When nesting multiple for clauses, each can declare its own independent positional variable. The inner positional variable resets to 1 for each iteration of the outer loop.

Practical XML Example

Consider the following XML document (catalog.xml):

<catalog>
    <product category="hardware">Screwdriver</product>
    <product category="software">Editor</product>
    <product category="hardware">Hammer</product>
</catalog>

Example 1: Basic Enumeration

To assign a numbered rank to every product in the document:

for $prod at $i in doc("catalog.xml")/catalog/product
return
    <item index="{$i}">{$prod/text()}</item>

Output:

<item index="1">Screwdriver</item>
<item index="2">Editor</item>
<item index="3">Hammer</item>

Example 2: Using the Positional Variable in Predicates and Conditions

Positional variables can be used directly inside where or return conditional branches:

for $prod at $i in doc("catalog.xml")/catalog/product
where $i mod 2 = 1
return
    <odd-item position="{$i}">{$prod/text()}</odd-item>

Output:

<odd-item position="1">Screwdriver</odd-item>
<odd-item position="3">Hammer</odd-item>

Summary of Best Practices