W3C

XQuery Scripting Extension 1.0 Use Cases

W3C Working Group Note 20 June 2017

This version:
https://www.w3.org/TR/2017/NOTE-xquery-sx-10-use-cases-20170620/
Latest version:
https://www.w3.org/TR/xquery-sx-10-use-cases/
Previous versions:
https://www.w3.org/TR/2008/WD-xquery-sx-10-use-cases-20081203/
https://www.w3.org/TR/2008/WD-xquery-sx-10-use-cases-20080328/
Editor:
John Snelson, MarkLogic <[email protected]>

Abstract

This document specifies usage scenarios for the XQuery Scripting Extension.

Status of this Document

This section describes the status of this document at the time of its publication. Other documents may supersede this document. A list of current W3C publications and the latest revision of this technical report can be found in the W3C technical reports index at https://www.w3.org/TR/.

This document is governed by the 1 March 2017 W3C Process Document.

This is a Working Group Note as described in the Process Document. It was developed by the W3C XML Query Working Group, which is part of the XML Activity.

This Working Group Note has been published in order to document the work that the XML Query Working Group has done in the area of providing an imperative extension to XQuery. The WG was unable to achieve consensus on several significant aspects of this technology and determined to terminate work on the project in mid-2014.

This document incorporates changes made against the previous publication of the Working Draft. Changes to this document since the previous publication of the Working Draft are detailed in B Revision Log.

Please report errors in this document using W3C's public Bugzilla system (instructions can be found at https://www.w3.org/XML/2005/04/qt-bugzilla). If access to that system is not feasible, you may send your comments to the W3C XSLT/XPath/XQuery public comments mailing list, [email protected]. It will be very helpful if you include the string “[SXUC]” in the subject line of your report, whether made in Bugzilla or in email. Please use multiple Bugzilla entries (or, if necessary, multiple email messages) if you have more than one comment to make. Archives of the comments and responses are available at https://lists.w3.org/Archives/Public/public-qt-comments/.

Publication as a Working Group Note does not imply endorsement by the W3C Membership. This is a draft document and may be updated, replaced or obsoleted by other documents at any time. It is inappropriate to cite this document as other than work in progress.

This document was produced by a group operating under the 5 February 2004 W3C Patent Policy. W3C maintains a public list of any patent disclosures made in connection with the deliverables of the group; that page also includes instructions for disclosing a patent. An individual who has actual knowledge of a patent which the individual believes contains Essential Claim(s) must disclose the information in accordance with section 6 of the W3C Patent Policy.


1 Use Cases for XQuery Scripting Extension

The use cases listed below were created by the XML Query Working Group to illustrate important applications for an XQuery scripting extension.

1.1 Use Case "R" - Scripting Relational Data

This use case is based on performing updates on the data used in Use Case "R" from the [XML Query Use Cases]. The DTD and sample data from this Use Case are copied below for convenience, and exactly match those found in the XQuery Use Cases.

The data represents a relational database used by an online auction. The auction maintains a USERS table containing information on registered users, each identified by a unique userid, who can either offer items for sale or bid on items. An ITEMS table lists items currently or recently for sale, with the userid of the user who offered each item. A BIDS table contains all bids on record, keyed by the userid of the bidder and the item number of the item to which the bid applies.

The three tables used by the online auction are below, with their column-names indicated in parentheses.

USERS ( USERID, NAME, RATING )
ITEMS ( ITEMNO, DESCRIPTION, OFFERED_BY, 
        START_DATE, END_DATE, RESERVE_PRICE ) 
BIDS ( USERID, ITEMNO, BID, BID_DATE )

1.1.1 Document Type Definition (DTD)

This use case is based on three separate input documents named users.xml, items.xml, and bids.xml. Each of the documents represents one of the tables in the relational database described above, using the following DTDs:

1.1.1.1 DTD for users.xml
                                            
<!ELEMENT users (user_tuple*)> 
<!ELEMENT user_tuple (userid, name, rating?)> 
<!ELEMENT userid (#PCDATA)> 
<!ELEMENT name (#PCDATA)> 
<!ELEMENT rating (#PCDATA)>
 
1.1.1.2 DTD for items.xml
                  
<!ELEMENT items (item_tuple*)>
<!ELEMENT item_tuple (itemno, description, offered_by, 
                      start_date?, end_date?, reserve_price?)> 
<!ELEMENT itemno (#PCDATA)>
<!ELEMENT description (#PCDATA)>
<!ELEMENT offered_by (#PCDATA)>
<!ELEMENT start_date (#PCDATA)>
<!ELEMENT end_date (#PCDATA)>
<!ELEMENT reserve_price (#PCDATA)>
1.1.1.3 DTD for bids.xml
                      
<!ELEMENT bids (bid_tuple*)> 
<!ELEMENT bid_tuple (userid, itemno, bid, bid_date)> 
<!ELEMENT userid (#PCDATA)> 
<!ELEMENT itemno (#PCDATA)> 
<!ELEMENT bid (#PCDATA)>
<!ELEMENT bid_date (#PCDATA)>

                      

1.1.2 Input Data

Here is an abbreviated set of data showing the XML format of the instances:

<items>
  <item_tuple>
    <itemno>1001</itemno>
    <description>Red Bicycle</description>
    <offered_by>U01</offered_by>
    <start_date>1999-01-05</start_date>
    <end_date>1999-01-20</end_date>
    <reserve_price>40</reserve_price>
  </item_tuple>
    ... Snip ... 
</items>
<users>
  <user_tuple>
    <userid>U01</userid>
    <name>Tom Jones</name>
    <rating>B</rating>
  </user_tuple>
    ... Snip ... 
</users>
<bids>
  <bid_tuple> 
    <userid>U02</userid> 
    <itemno>1001</itemno> 
    <bid>35</bid> 
    <bid_date>1999-01-07</bid_date> 
    </bid_tuple> 
  <bid_tuple> 
    ... Snip ... 
</bids>
                    

The entire data set is represented by the following table:

USERS
USERID NAME RATING
U01 Tom Jones B
U02 Mary Doe A
U03 Dee Linquent D
U04 Roger Smith C
U05 Jack Sprat B
U06 Rip Van Winkle B
ITEMS
ITEMNO DESCRIPTION OFFERED_BY START_DATE END_DATE RESERVE_PRICE
1001 Red Bicycle U01 1999-01-05 1999-01-20 40
1002 Motorcycle U02 1999-02-11 1999-03-15 500
1003 Old Bicycle U02 1999-01-10 1999-02-20 25
1004 Tricycle U01 1999-02-25 1999-03-08 15
1005 Tennis Racket U03 1999-03-19 1999-04-30 20
1006 Helicopter U03 1999-05-05 1999-05-25 50000
1007 Racing Bicycle U04 1999-01-20 1999-02-20 200
1008 Broken Bicycle U01 1999-02-05 1999-03-06 25
BIDS
USERID ITEMNO BID BID_DATE
U02 1001 35 1999-01-07
U04 1001 40 1999-01-08
U02 1001 45 1999-01-11
U04 1001 50 1999-01-13
U02 1001 55 1999-01-15
U01 1002 400 1999-02-14
U02 1002 600 1999-02-16
U03 1002 800 1999-02-17
U04 1002 1000 1999-02-25
U02 1002 1200 1999-03-02
U04 1003 15 1999-01-22
U05 1003 20 1999-02-03
U01 1004 40 1999-03-05
U03 1007 175 1999-01-25
U05 1007 200 1999-02-08
U04 1007 225 1999-02-12

1.1.3 Queries and Results

1.1.3.1 Q1

Insert a new bid for Roger Smith on item 1002, adding 10% to the best bid received so far for this item, and report back what bid was just entered.

Solution in the XQuery Scripting Extension:

let $uid := doc("users.xml")/users/user_tuple[name = "Roger Smith"]/userid
let $topbid := max(doc("bids.xml")/bids/bid_tuple[itemno = 1002]/bid)
let $newbid := $topbid * 1.1
return (
  insert nodes
    <bid_tuple> 
      <userid>{ data($uid) }</userid> 
      <itemno>1002</itemno> 
      <bid>{ $newbid }</bid> 
      <bid_date>1999-03-03</bid_date> 
    </bid_tuple>
  into doc("bids.xml")/bids,
  <new_bid>{ $newbid }</new_bid>
)

Expected Result:

The best bid for item 1002 had been at 1200, thus Roger's bid is at 1320.

<new_bid>1320</new_bid>
            

Expected resulting content of bids.xml:

<bids>
  <bid_tuple> 
    <userid>U02</userid> 
    <itemno>1001</itemno> 
    <bid>35</bid> 
    <bid_date>1999-01-07</bid_date> 
  </bid_tuple> 
    ... Snip ... 
  <bid_tuple> 
    <userid>U01</userid> 
    <itemno>1002</itemno> 
    <bid>400</bid> 
    <bid_date>1999-02-14</bid_date> 
  </bid_tuple> 
    ... Snip ... 
   <bid_tuple> 
    <userid>U04</userid> 
    <itemno>1007</itemno> 
    <bid>225</bid> 
    <bid_date>1999-02-12</bid_date> 
  </bid_tuple> 
    ... Snip ... 
  <bid_tuple> 
    <userid>U04</userid> 
    <itemno>1002</itemno> 
    <bid>1320</bid> 
    <bid_date>1999-03-03</bid_date> 
  </bid_tuple> 
</bids>
						
1.1.3.2 Q2

Place a bid for Roger Smith on item 1007, adding 10% to the best bid received so far on that item, but only if the bid amount does not exceed a given limit. Otherwise return the current top bid.

Solution in the XQuery Scripting Extension:

let $uid := doc("users.xml")/users/user_tuple[name = "Roger Smith"]/userid
let $topbid := max(doc("bids.xml")/bids/bid_tuple[itemno = 1007]/bid)
let $newbid := $topbid * 1.1
return
  if($newbid <= 240) then (
    insert nodes
      <bid_tuple>
        <userid>{ data($uid) }</userid>
        <itemno>1002</itemno>
        <bid>{ $newbid }</bid>
        <bid_date>1999-03-03</bid_date>
      </bid_tuple>
    into doc("bids.xml")/bids,
    <new_bid>{ $newbid }</new_bid>
  ) else (
    <top_bid>{ $topbid }</top_bid>
  )

Expected Result:

Adding 10% to the best bid on item 1007 would require a bid of 247.5, which is more than the allowed limit of 240. Thus, the bids.xml document does not change.

<top_bid>225</top_bid>
            
1.1.3.3 Q3

Erase user Dee Linquent and the corresponding associated items and bids. Return a count of the items and bids deleted.

Solution in the XQuery Scripting Extension:

let $user := doc("users.xml")/users/user_tuple[name = "Dee Linquent"]
let $items := doc("items.xml")/items/item_tuple[offered_by = $user/userid]
let $bids := doc("bids.xml")/bids/bid_tuple[userid = $user/userid]
return (
  delete nodes ($user, $items, $bids),
  <deleted>
    <items>{ count($items) }</items>
    <bids>{ count($bids) }</bids>
  </deleted>
)

Expected Result:

<deleted>
  <items>2</items>
  <bids>2</bids>
</deleted>

Expected resulting content of items.xml:

<items>
  <item_tuple>
    <itemno>1001</itemno>
    <description>Red Bicycle</description>
    <offered_by>U01</offered_by>
    <start_date>1999-01-05</start_date>
    <end_date>1999-01-20</end_date>
    <reserve_price>40</reserve_price>
  </item_tuple>
    ... Snip ... 
  <item_tuple>
    <itemno>1004</itemno>
    <description>Tricycle</description>
    <offered_by>U01</offered_by>
    <start_date>1999-02-25</start_date>
    <end_date>1999-03-08</end_date>
    <reserve_price>15</reserve_price>
  </item_tuple>
  <item_tuple>
    <itemno>1007</itemno>
    <description>Racing bicycle</description>
    <offered_by>U04</offered_by>
    <start_date>1999-01-20</start_date>
    <end_date>1999-02-20</end_date>
    <reserve_price>200</reserve_price>
  </item_tuple>
  <item_tuple>
    <itemno>1008</itemno>
    <description>Broken bicycle</description>
    <offered_by>U01</offered_by>
    <start_date>1999-02-05</start_date>
    <end_date>1999-03-06</end_date>
    <reserve_price>25</reserve_price>
  </item_tuple>
</items>

Expected resulting content of users.xml:

<users>
  <user_tuple>
    <userid>U01</userid>
    <name>Tom Jones</name>
    <rating>B</rating>
  </user_tuple>
  <user_tuple>
    <userid>U02</userid>
    <name>Mary Doe</name>
    <rating>A</rating>
  </user_tuple>
   <user_tuple>
    <userid>U04</userid>
    <name>Roger Smith</name>
    <rating>C</rating>
  </user_tuple>
  ... Snip ... 
  <user_tuple>
   <userid>U06</userid>
   <name>Rip Van Winkle</name>
   <rating>B</rating>
  </user_tuple>
</users>

Expected resulting content of bids.xml:

<bids>
  <bid_tuple> 
    <userid>U02</userid> 
    <itemno>1001</itemno> 
    <bid>35</bid> 
    <bid_date>1999-01-07</bid_date> 
  </bid_tuple> 
  <bid_tuple> 
    <userid>U04</userid> 
    <itemno>1001</itemno> 
    <bid>40</bid> 
    <bid_date>1999-01-08</bid_date> 
  </bid_tuple> 
  ... Snip ... 
  <bid_tuple> 
    <userid>U02</userid> 
    <itemno>1002</itemno> 
    <bid>600</bid> 
    <bid_date>1999-02-16</bid_date> 
  </bid_tuple> 
  <bid_tuple> 
    <userid>U04</userid> 
    <itemno>1002</itemno> 
    <bid>1000</bid> 
    <bid_date>1999-02-25</bid_date> 
  </bid_tuple> 
  ... Snip ... 
  <bid_tuple> 
    <userid>U04</userid> 
    <itemno>1007</itemno> 
    <bid>225</bid> 
    <bid_date>1999-02-12</bid_date> 
  </bid_tuple> 
</bids>

1.1.3.4 Q4

Monitor the bidding on the helicopter. If Roger Smith does not have the current highest bid, add a bid for him that is one higher than the top bid, unless the bid amount exceeds a given limit. Stop monitoring when the auction has finished.

Solution in the XQuery Scripting Extension:

Editorial note 2008-01-25
A naive execution of this query would form a busy loop waiting for the auction to end. This is not very desirable, so it's possible an alternative solution should be found.
declare variable $uid := doc("users.xml")/users/user_tuple
  [name = "Roger Smith"]/userid;
declare variable $item := doc("items.xml")/items/item_tuple
  [description = "Helicopter"];
declare assignable variable $result :=
  "Error: The auction has already ended or no bids were placed";
declare assignable variable $maximumExceeded := false();

while(xs:date($item/end_date) >= fn:current-date() and not($maximumExceeded)) {
  let $bids := doc("bids.xml")/bids/bid_tuple[itemno = $item/itemno]
  let $topbid := max($bids/bid)
  let $newbid := $topbid + 1
  where $bids[bid = $topbid]/userid != $uid
  return
    if($newbid <= 60000) then (
      insert nodes
        <bid_tuple>
          { $uid, $item/itemno }
          <bid>{ $newbid }</bid> 
          <bid_date>{ fn:current-date() }</bid_date> 
        </bid_tuple>
      into doc("bids.xml")/bids;
      $result := concat("What a bargain! You got a helicopter for ",
        $newbid);
    ) else (
      $result := "Bidding exceeded 60000";
      $maximumExceeded := true();
    )
};

$result;

Expected Result:

Since this document was published after the end date for the auction ("1999-05-25"), the while loop will never be executed.

Error: The auction has already ended or no bids were placed

1.2 Use Case "XHTML" - AJAX Scripting with XHTML

XQuery is ideally suited to manipulating the XML trees of XHTML. This use case speculates that XQuery could be used in the way Javascript is now, to modify an XHTML web page whilst it is being displayed. In this way XQuery can be used to implement effects like those commonly referred to by the acronym AJAX.

This use case deals with a web page that can be used to perform a search in a book database. In all queries, there is an assumption that the xhtml document is supplied as the context item.

1.2.1 Input Data

The starting state of the web page consists of only a simple XHTML form.

<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>Book Search</title>
  </head>
  <body>
    <form>
      Book Name: <input type="text" id="txtBookName"/>
      <input type="button" id="btnSend" value="Start Search"/>
    </form>
  </body>
</html>

1.2.2 Queries and Results

1.2.2.1 Q1

Write a script to act as an "onclick" event handler for the "btnSend" form button. The script calls a web service to search for the book entered by the user and retrieve detailed information for the book. While the user is waiting for the web service the message "Loading Book" is displayed. This message is later replaced with the detailed information for the book.

Input web page:

The web browser has already updated the content of the form's text input with the user's search string, "XQuery".

<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>Book Search</title>
  </head>
  <body>
    <form>
      Book Name: <input type="text" id="txtBookName">XQuery</input>
      <input type="button" id="btnSend" value="Start Search"/>
    </form>
  </body>
</html>

Solution in the XQuery Scripting Extension:

The element that caused the event is supplied as an external variable called $eventNode. The web service is called using the external functions "book:search", and "book:get".

declare namespace xhtml="http://www.w3.org/1999/xhtml";
declare namespace book="http://www.example.com/booksearch";

declare simple function book:search($name as xs:string) as element(book)* external;
declare simple function book:get($isbn as xs:string) as element(bookinfo) external;

declare variable $eventNode as element() external;
declare assignable variable $searchResults := ();

insert node <xhtml:div>Loading Book</xhtml:div>
after /xhtml:html/xhtml:body/xhtml:form;

$searchResults :=
  book:search($eventNode/preceding-sibling::xhtml:input[1]);

if($searchResults) then (
  replace node /xhtml:html/xhtml:body/xhtml:div
  with <xhtml:div>{
    book:get($searchResults[1]/isbn)/html/node()
  }</xhtml:div>;
)
else (
  replace node /xhtml:html/xhtml:body/xhtml:div
  with <xhtml:div>No books found!</xhtml:div>;
);

Results returned to the query from the function call book:search("XQuery"):

<book>
  <title>XQuery from the Experts: A Guide to the W3C XML Query Language</title>
  <isbn>0321180607</isbn>
</book>
<book>
  <title>XQuery: The XML Query Language</title>
  <isbn>0321165810</isbn>
</book>

Results returned to the query from the function call book:get("0321180607"):

<bookinfo>
  <title>XQuery from the Experts: A Guide to the W3C XML Query Language</title>
  <isbn>0321180607</isbn>
  <html>
    <h2 xmlns="http://www.w3.org/1999/xhtml">XQuery from the Experts: A Guide to the W3C XML Query Language</h2>
    ISBN: 0321180607
  </html>
</bookinfo>

Expected intermediate content of the web page:

<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>Book Search</title>
  </head>
  <body>
    <form>
      Book Name: <input type="text" id="txtBookName">XQuery</input>
      <input type="button" id="btnSend" value="Start Search"/>
    </form>
    <xhtml:div xmlns:xhtml="http://www.w3.org/1999/xhtml">Loading book</xhtml:div>
  </body>
</html>

Expected resulting content of the web page:

<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>Book Search</title>
  </head>
  <body>
    <form>
      Book Name: <input type="text" id="txtBookName">XQuery</input>
      <input type="button" id="btnSend" value="Start Search"/>
    </form>
    <xhtml:div xmlns:xhtml="http://www.w3.org/1999/xhtml">
      <h2>XQuery from the Experts: A Guide to the W3C XML Query Language</h2>
      ISBN: 0321180607
    </xhtml:div>
  </body>
</html>
1.2.2.2 Q2

Write a script to act as an "onclick" event handler for the "btnSend" form button. The script calls a web service to search for the book entered by the user, and uses an additional web service to find which libraries have these books on the shelves. The information received is displayed asynchronously as it arrives.

Input web page:

The web browser has already updated the content of the form's text input with the user's search string, "XQuery".

<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>Book Search</title>
  </head>
  <body>
    <form>
      Book Name: <input type="text" id="txtBookName">XQuery</input>
      <input type="button" id="btnSend" value="Start Search"/>
    </form>
  </body>
</html>

Solution in the XQuery Scripting Extension:

The element that caused the event is supplied as an external variable called $eventNode. The web services are called using the external functions "book:search", and "library:find".

declare namespace xhtml="http://www.w3.org/1999/xhtml";
declare namespace book="http://www.example.com/booksearch";
declare namespace library="http://www.example.com/library";

declare simple function book:search($name as xs:string)
  as element(book)* external;
declare simple function library:find($isbn as xs:string)
  as element(library)* external;

declare variable $eventNode as element() external;
declare assignable variable $table := ();

insert node <xhtml:div><xhtml:table/></xhtml:div>
after /xhtml:html/xhtml:body/xhtml:form;
$table := //xhtml:table;

for $book in book:search($eventNode/preceding-sibling::xhtml:input[1])
return (
  insert node
    <xhtml:tr>
      <xhtml:td>{data($book/title)}</xhtml:td>
      <xhtml:td>{data($book/isbn)}</xhtml:td>
      <xhtml:td/>
    </xhtml:tr>
  as last into $table;
);

for $row in $table/xhtml:tr
return (
  replace value of node $row/xhtml:td[3]
  with string-join(library:find($row/xhtml:td[2])/name, ", ");
);

Results returned to the query from the function call book:search("XQuery"):

<book>
  <title>XQuery from the Experts: A Guide to the W3C XML Query Language</title>
  <isbn>0321180607</isbn>
</book>
<book>
  <title>XQuery: The XML Query Language</title>
  <isbn>0321165810</isbn>
</book>

Results returned to the query from the function call library:find("0321180607"):

<library>
  <name>Bodleian Library</name>
  <url>http://www.bodley.ox.ac.uk/</url>
  <lending>no</lending>
</library>
<library>
  <name>Radcliffe Science Library</name>
  <url>http://www.ouls.ox.ac.uk/rsl/</url>
  <lending>yes</lending>
</library>

Results returned to the query from the function call library:find("0321165810"):

<library>
  <name>Radcliffe Science Library</name>
  <url>http://www.ouls.ox.ac.uk/rsl/</url>
  <lending>yes</lending>
</library>

Expected resulting content of the web page:

The web page will be altered as the results are received from the web services, but the final result will look like this.

<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>Book Search</title>
  </head>
  <body>
    <form>
      Book Name: <input type="text" id="txtBookName">XQuery</input>
      <input type="button" id="btnSend" value="Start Search"/>
    </form>
    <xhtml:div xmlns:xhtml="http://www.w3.org/1999/xhtml">
      <xhtml:table>
        <xhtml:tr>
          <xhtml:td>XQuery from the Experts: A Guide to the W3C XML Query Language</xhtml:td>
          <xhtml:td>0321180607</xhtml:td>
          <xhtml:td>Bodleian Library, Radcliffe Science Library</xhtml:td>
        </xhtml:tr>
        <xhtml:tr>
          <xhtml:td>XQuery: The XML Query Language</xhtml:td>
          <xhtml:td>0321165810</xhtml:td>
          <xhtml:td>Radcliffe Science Library</xhtml:td>
        </xhtml:tr>
      </xhtml:table>
    </xhtml:div>
  </body>
</html>

1.3 Use Case "WS" - XQuery for Web Services

This use case deals with the implementation of a REST based web service, specifically a micro-blog.

1.3.1 Input Data

The default collection contains a forest of micro blog documents, one for each user. Initially the forest contains these documents.

John's micro-blog:

<micro-blog user="john">
  <entry timestamp="2007-10-17T10:02:53+01:00">
    <text>Still reading email...</text>
  </entry>
  <entry timestamp="2007-10-17T20:19:31+01:00">
    <text>Writing XQuery Scripting Extension use cases (ideas, anyone?)</text>
  </entry>
  <entry timestamp="2007-10-18T00:43:02+01:00">
    <text>Thinking of going to bed</text>
  </entry>
</micro-blog>

Emily's micro-blog:

<micro-blog user="emily">
  <entry timestamp="2007-10-16T18:12:51+01:00">
    <text>So how do you use a micro-blog?</text>
  </entry>
  <entry timestamp="2007-10-16T18:13:20+01:00">
    <text>Ah, I see</text>
  </entry>
  <entry timestamp="2007-10-17T07:33:44+01:00">
    <text>Morning, everybody!</text>
  </entry>
</micro-blog>

A document containing the blog access log:

<log>
</log>

1.3.2 Queries and Results

1.3.2.1 Q1

Process a request from John to add a blog entry, returning a confirmation XHTML page.

The element representing the request:

<request>
  <method>POST</method>
  <url>http://blog.example.com/john/add</url>
  <param name="text">Making a post to my XQuery blog engine</param>
</request>

Solution in the XQuery Scripting Extension:

The request element is bound to the external variable $request.

declare variable $request as element(request) external;

declare simple function local:error($message)
{
  <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
      <title>Error</title>
    </head>
    <body>
      <h1>Error</h1>
      <p>{ $message }</p>
    </body>
  </html>
};

if($request/method = "POST") then () else
  exit returning local:error(concat("You cannot use the ",
    $request/method, " method with this URL."));

let $user := replace($request/url, "^http://.*/([^/]+)/add$", "$1")
let $blog := collection()/micro-blog[@user = $user]
return (
  if($blog) then () else
    exit returning local:error("Unknown user");

  insert node
    <entry timestamp="{ current-dateTime() }">
      <text>{ $request/param[@name = "text"] }</text>
    </entry>
  as last into $blog;

  <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
      <title>Blog entry added for { $user }</title>
    </head>
    <body>
      <h1>Blog entry added for { $user }</h1>
      <p>{ $request/param[@name = "text"] }</p>
    </body>
  </html>;
);

Expected Result:

<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>Blog entry added for john</title>
  </head>
  <body>
    <h1>Blog entry added for john</h1>
    <p>Making a post to my XQuery blog engine</p>
  </body>
</html>

Expected resulting content of John's micro-blog:

<micro-blog user="john">
  <entry timestamp="2007-10-17T10:02:53+01:00">
    <text>Still reading email...</text>
  </entry>
  <entry timestamp="2007-10-17T20:19:31+01:00">
    <text>Writing XQuery Scripting Extension use cases (ideas, anyone?)</text>
  </entry>
  <entry timestamp="2007-10-18T00:43:02+01:00">
    <text>Thinking of going to bed</text>
  </entry>
  <entry timestamp="2007-10-18T08:53:9+01:00">
    <text>Making a post to my XQuery blog engine</text>
  </entry>
</micro-blog>
1.3.2.2 Q2

Process the same request from John, using a function to check that "john" is a valid user name and to log the event.

The element representing the request:

<request>
  <method>POST</method>
  <url>http://blog.example.com/john/add</url>
  <param name="text">Making a post to my XQuery blog engine</param>
</request>

Solution in the XQuery Scripting Extension:

The request element is bound to the external variable $request.

declare variable $request as element(request) external;

declare sequential function local:check-user-and-log($username as xs:string) 
  as element(micro-blog)?
{
  declare $entry as element() :=
    <access-attempt>
      <timestamp>{fn:current-dateTime()}</timestamp>
      <user-name>{$username}</user-name>
      <access-allowed/>
    </access-attempt>;
  declare $blog as element(micro-blog)? :=
    collection()/micro-blog[@user = $username];

  if($blog) then
    replace value of node $entry/access-allowed with "Yes"
  else
    replace value of node $entry/access-allowed with "No";

  insert node $entry as last into collection()/log;

  $blog;
}; 


declare simple function local:error($message)
{
  <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
      <title>Error</title>
    </head>
    <body>
      <h1>Error</h1>
      <p>{ $message }</p>
    </body>
  </html>
};

if($request/method = "POST") then () else
  exit returning local:error(concat("You cannot use the ",
    $request/method, " method with this URL."));

let $user := replace($request/url, "^http://.*/([^/]+)/add$", "$1")
let $blog := local:check-user-and-log($user)
return (
  if($blog) then () else
    exit returning local:error("Unknown user");

  insert node
    <entry timestamp="{ current-dateTime() }">
      <text>{ $request/param[@name = "text"] }</text>
    </entry>
  as last into $blog;

  <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
      <title>Blog entry added for { $user }</title>
    </head>
    <body>
      <h1>Blog entry added for { $user }</h1>
      <p>{ $request/param[@name = "text"] }</p>
    </body>
  </html>;
);

Expected Result:

<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>Blog entry added for john</title>
  </head>
  <body>
    <h1>Blog entry added for john</h1>
    <p>Making a post to my XQuery blog engine</p>
  </body>
</html>

Expected resulting content of John's micro-blog:

<micro-blog user="john">
  <entry timestamp="2007-10-17T10:02:53+01:00">
    <text>Still reading email...</text>
  </entry>
  <entry timestamp="2007-10-17T20:19:31+01:00">
    <text>Writing XQuery Scripting Extension use cases (ideas, anyone?)</text>
  </entry>
  <entry timestamp="2007-10-18T00:43:02+01:00">
    <text>Thinking of going to bed</text>
  </entry>
  <entry timestamp="2007-10-18T08:53:9+01:00">
    <text>Making a post to my XQuery blog engine</text>
  </entry>
</micro-blog>

Expected resulting content of the log:

<log>
  <access-attempt>
    <timestamp>2007-10-18T08:53:9+01:00</timestamp>
    <user-name>john</user-name>
    <access-allowed>Yes</access-allowed>
  </access-attempt>
</log>

A Normative References

XQuery 1.0
World Wide Web Consortium. XQuery 1.0: An XML Query Language. W3C Recommendation, 23 January 2007. See http://www.w3.org/TR/xquery/.
XML Query Use Cases
World Wide Web Consortium. XML Query Use Cases. W3C Working Group Note, 23 March 2007. See http://www.w3.org/TR/xquery-use-cases/.
XQuery Update Facility
World Wide Web Consortium. XQuery Update Facility. W3C Working Draft, 28 August 2007. See http://www.w3.org/TR/xquery-update-10/.
XQuery Scripting Extension 1.0 Requirements
World Wide Web Consortium. XQuery Scripting Extension 1.0 Requirements. W3C Working Draft, 23 March 2007. See http://www.w3.org/TR/2007/WD-xquery-sx-10-requirements-20070323.

B Revision Log (Non-Normative)

This log records the substantive changes that have been made to this document . Minor editorial changes are not included in this log.

B.1 Changes in internal WD

  • Added an editorial note detailing the problems with RDB Q4.

  • Added the WS Q2 use case.

  • 2008-07-10: Updated the syntax to the lastest editor's draft.

  • 2008-11-09: Updated the syntax to the lastest editor's draft.

  • 2010-03-02: Updated the syntax to the lastest editor's draft.