Table of Contents
In the previous chapter, we described how to transform an XML file into another by means of tree tranformations defined by templates. We have also seen how to extract information from an XML file. But now that XML files are used for keeping data similarly to databases, an alternate way of querying XML information has been defined, resulting in a language close to the well-known SQL.
It is called XQuery and is aimed at selecting information from XML databases that are often too large to be stored in a single XML file for which a random access would not necessarily be efficient. XQuery can also easily combine information from many XML documents, which is a bit awkward to do in XSLT. The output of an XQuery query is a sequence of XML nodes that lend themselves to the full power of XPath functions and XML constructors in order to further convert them into other XML nodes.
For the simple use cases we have shown in the previous chapter, transforming an XML document into an XHTML one by means of XSLT templates can also be seen as querying an input document to select some information which is then inserted into an XHTML template. This chapter illustrates some simple uses of XQuery to solve the problems shown previously.
Contrarily to XML Schema and XSLT, XQuery is not a fully XML based notation. Some readers might find it less verbose or rebarbative than XSLT. Relax NG Compact notation is also a non-XML-based notation for schemas of XML files when compared to XML Schema.
An XQuery program is called a query. It is an expression that selects or constructs a sequence of XML nodes. A query can take one of the following forms:
The simplest kind of query is an XPath 2.0 expression returning a sequence
of nodes, but this is limited to the extraction of XML information from the
input document without change. XQuery can use all XPath 2.0 functions
(in fact, both XQuery and XPath functions are defined in the same
document [33]). When applied to our cellar-book
instance document (Example 2.2), the following query (the
same as line 2‑
of Example 4.1) returns the
sequence of wine elements for which there are less than 2 bottles left in the cellar.
Note that the less-than sign can be used without having to type the
<
entity because an XQuery query is not an
XML expression.
/cellar-book/cellar/wine[quantity<2]
A more powerful type of query is a FLWOR expression[1] (for
, let
, where
, order
by
, return
) which is patterned after the SQL
SELECT - FROM - WHERE
instruction. It allows the reordering of results, the
creation of new values, etc.
for $w in /cellar-book/cellar/wine, $cat-w in /cellar-book/cat:wine-catalog/cat:wine let $q := $w/quantity where $w/@code = $cat-w/@code order by $q return concat($q,":",$cat-w/@name)
Like in XSLT, variables must be prefixed by a dollar sign to differentiate
them from element names. A FLOWR query bears some resemblance with the
for
loop in XPath 2.0. The for
in this query
(equivalent to line 11‑
of Example 4.1) with its
two embedded loops performs a join between all the wines in the cellar and the ones
of the catalog;
let
creates a variable $q
to keep track of the
wine quantity in the cellar; where
keeps only wines sharing
the same code
attributes in the cellar and in the catalog;
order
ensures that the output will be in increasing order of
quantity; return
builds the resulting string composed of the string
value of $q
and the name of the wine separated by a colon.
An XML element that can be created either directly by writing an XML
tag in the query (this is called a direct constructor) or
by using a computed constructor. The following query uses
two direct constructors: cheap-wines
and cheap-wine
.
Braces within a direct constructor are used to embed non-XML XQuery
code. The following example extracts French wines from the catalog that cost
less than 20 dollars (like line 14‑
of Example 4.1). It creates as output a single XML element called
cheap-wine
containing a list a wines for which we have changed
the cat:wine
tag by cheap-wine
. The attributes and
elements of cat:wine
are copied to the cheap-wine
with
another nested XPath expression.
<cheap-wines> {for $w in /cellar-book/cat:wine-catalog/cat:wine where $w/cat:origin/cat:country='France' and $w/cat:price < 20.0 return <cheap-wine>{$w/@*,$w/*}</cheap-wine>} </cheap-wines>
The next query is equivalent to the previous one but uses computed
constructors. A computed constructor is created by an element
followed by an identifier and an expression within braces. Attributes can be
similarly constructed with an attribute
construct. The element or
attribute identifier can also be computed by writing an expression within braces
instead of an identifier.
element cheap-wines{ for $w in /cellar-book/cat:wine-catalog/cat:wine where $w/cat:origin/cat:country='France' and $w/cat:price < 20.0 return element cheap-wine{$w/@*,$w/*} }
A sequence of the above XQuery expressions separated by commas, parentheses can be used to nest sequences within others.
A query is often preceded by a "prolog" composed of declarations separated by a semicolon. For example, in the previous examples, the query should have been preceded by
declare namespace cat = "http://www.iro.umontreal.ca/lapalme/wine-catalog";
in order to define the cat
namespace prefix used in the query. Declarations
can also used to define variables, functions and other options to control the processing of the
query. Most often the prolog is much longer that the query itself as there can be only one
query in a file. This is not really a limitation because multiple queries can be
merged into a single one: one only needs to create a sequence of queries by separating them
with commas.
For complete details on the syntax of a query, see EBNF grammar of XQuery.