Copyright © 2008 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C liability, trademark and document use rules apply.
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 http://www.w3.org/TR/.
This is a First Public Working Draft as described in the Process Document. It has been developed by the W3C XML Query Working Group, which is part of the XML Activity. The Working Group expects to eventually publish this document as a Working Group Note.
This document provides a number of use cases designed to evaluate the language defined in XQuery Scripting Extension 1.0 at the time that it was issued as a Working Draft on 28 March 2008. Organizations and individuals should review this document to ascertain whether or not adequate coverage of the requirements is provided by these use cases.
Please report errors in this document using W3C's public Bugzilla system (instructions can be found at http://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, public-qt-comments@w3.org. 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. Each Bugzilla entry and email message should contain only one error report. Archives of the comments and responses are available at http://lists.w3.org/Archives/Public/public-qt-comments/.
Publication as a Working Draft 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
1.1 Use Case "R" - Scripting
Relational Data
1.1.1 Document Type Definition (DTD)
1.1.1.1
DTD for users.xml
1.1.1.2
DTD for items.xml
1.1.1.3
DTD for bids.xml
1.1.2 Input Data
1.1.3 Queries and Results
1.1.3.1
Q1
1.1.3.2
Q2
1.1.3.3
Q3
1.1.3.4
Q4
1.2 Use Case "XHTML" -
AJAX Scripting with XHTML
1.2.1 Input Data
1.2.2 Queries and Results
1.2.2.1
Q1
1.2.2.2
Q2
1.3 Use Case "WS" -
XQuery for Web Services
1.3.1 Input Data
1.3.2 Queries and Results
1.3.2.1
Q1
1.3.2.2
Q2
A Normative
References
B Revision Log
(Non-Normative)
B.1 Changes in
internal WD
The use cases listed below were created by the XML Query Working Group to illustrate important applications for an XQuery scripting extension.
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 )
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:
<!ELEMENT users (user_tuple*)> <!ELEMENT user_tuple (userid, name, rating?)> <!ELEMENT userid (#PCDATA)> <!ELEMENT name (#PCDATA)> <!ELEMENT rating (#PCDATA)>
<!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)>
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:
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 |
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 |
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 |
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>
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; exit with <new_bid>{ $newbid }</new_bid>; } else { exit with <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>
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>
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. |
Editorial note | 2008-01-25 |
The fn:current-date() function is defined by XQuery as "stable". This means that the query's current dateTime/date/time will not change for the duration of the query. It needs to be decided if we intend queries to be able to have a notion of time passing, possibly by defining the "execution scope" differently for scripting. |
{ declare $uid := doc("users.xml")/users/user_tuple [name = "Roger Smith"]/userid; declare $item := doc("items.xml")/items/item_tuple [description = "Helicopter"]; declare $result := "Error: The auction has already ended or no bids were placed"; while(xs:date($item/end_date) >= fn:current-date()) { let $bids := doc("bids.xml")/bids/bid_tuple[itemno = $item/itemno] let $topbid := max($bids/bid) where $bids[bid = $topbid]/userid != $uid return let $newbid = $topbid + 1 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; set $result := concat("What a bargain! You got a helicopter for ", $newbid); } else { set $result := "Bidding exceeded 60000"; break loop; } }; exit with $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
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.
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>
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 $searchResults; insert node <xhtml:div>Loading Book</xhtml:div> after /xhtml:html/xhtml:body/xhtml:form; set $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>
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 $table; insert node <xhtml:div><xhtml:table/></xhtml:div> after /xhtml:html/xhtml:body/xhtml:form; set $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>
This use case deals with the implementation of a REST based web service, specifically a micro-blog.
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>
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> }; { declare $user, $blog; if($request/method = "POST") then () else exit with local:error(concat("You cannot use the ", $request/method, " method with this URL.")); set $user := replace($request/url, "^http://.*/([^/]+)/add$", "$1"); set $blog := collection()/micro-blog[@user = $user]; if($blog) then () else exit with local:error("Unknown user"); insert node <entry timestamp="{ current-dateTime() }"> <text>{ $request/param[@name = "text"] }</text> </entry> as last into $blog; exit with <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>
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; return value $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> }; { declare $user, $blog; if($request/method = "POST") then () else exit with local:error(concat("You cannot use the ", $request/method, " method with this URL.")); set $user := replace($request/url, "^http://.*/([^/]+)/add$", "$1"); set $blog := local:check-user-and-log($user); if($blog) then () else exit with local:error("Unknown user"); insert node <entry timestamp="{ current-dateTime() }"> <text>{ $request/param[@name = "text"] }</text> </entry> as last into $blog; exit with <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>