Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Writing Programs with XQuery

XQuery is XPath grown into a programming language. Every XPath expression is an XQuery query, and XQuery adds:

  • FLWOR expressions, a powerful form of for and let, with where, order by, group by, count and windows;
  • constructors that build new XML, written as XML with expressions inside;
  • a prolog of declarations: functions (which can call themselves), variables, namespaces, options and imported modules;
  • updates, which insert, delete, replace and rename nodes.

This chapter works on the same document as Finding Things with XPath: the Vane protocol, an Internet-Draft written in XML. Its requirements are paragraphs with an anchor and a BCP 14 keyword:

    <section anchor="pause">
      <name>Pause</name>
      <t anchor="req-7">After three failed checks in a row, a service
      <bcp14>MUST</bcp14> put the flag in a <xref target="term-pause">pause</xref>.</t>
      <t anchor="req-8">A pause <bcp14>SHOULD</bcp14> last five minutes.
      <cref anchor="issue-1" source="AM">Should an owner be able to
      change the duration?</cref></t>
      <t anchor="req-11">A service <bcp14>MAY</bcp14> report a pause to the
      owners of the flag as soon as it begins.</t>
    </section>

There is an earlier version of the draft too, vane-00.xml, and the chapter compares the two. The examples run in docs/book/examples/spec, the directory of the specification project. By the end of the chapter we'll have a small toolkit for that project: a module that knows the draft's structure, a check that finds its mistakes, a list of what changed between versions, and updates that edit the draft in place, one named node at a time, instead of rewriting it.

Running a query

A query usually lives in a file, ending in .xq. sxml xquery runs it:

$ sxml xquery --context-item=vane.xml keywords.xq
1. MUST (5): req-1, req-2, req-4, req-6, req-7
2. MAY (2): req-9, req-11
3. SHOULD (2): req-3, req-8

Here is the query:

keywords.xq:

declare option output:method "text";
declare option output:item-separator "&#10;";

for $requirement in //t[@anchor]
group by $keyword := string($requirement/bcp14)
let $count := count($requirement)
order by $count descending, $keyword
count $rank
where $rank le 3
return `{$rank}. {$keyword} ({$count}): {string-join($requirement/@anchor, ", ")}`

The first two lines are the prolog: here, two options for the output, which ask for plain text with one item on each line (&#10; is a line feed, written as an XML character reference). The rest is the query body, one FLWOR expression that groups the requirements by keyword, counts them, sorts and ranks the groups, and keeps the first three. By the end of the next section you'll be able to read every clause of it.

The same query runs on the earlier draft, which had no MAY yet:

$ sxml xquery --context-item=vane-00.xml keywords.xq
1. MUST (5): req-1, req-2, req-4, req-6, req-7
2. SHOULD (2): req-3, req-8
3. MUST NOT (1): req-5

For short queries, --expression saves writing a file. Unlike sxml xpath, sxml xquery writes its result with the xml output method by default, as a query's result is usually XML; --method chooses another:

$ sxml xquery --context-item=vane.xml --expression='//section[@anchor = "pause"]/name'
<name>Pause</name>
$ sxml xquery --expression='for $i in 1 to 3 return <item>{ $i }</item>'
<item>1</item><item>2</item><item>3</item>
$ sxml xquery --method=adaptive --expression='"one", 2'
"one"
2

Comments

XQuery comments are written between (: and :), and can be nested. Comments that start with (:~ document the declaration after them:

(: A comment. (: A nested one. :) :)

What's in this chapter

Full-text search, with contains text, is part of XQuery in sxml too; it is in Working with Text, with the other ways of working with text. Windows, the FLWOR clauses for runs of neighbouring items, are taught on trace data in Who Calls Whom, and How Often, and Project: Generating Vane's SDKs from a Schema uses XQuery to build the tree of a TypeScript program.