Copyright © 2011 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 Working Group Note as described in the Process Document. It has been developed by the W3C XML Query Working Group, which is part of the XML Activity.
This document incorporates a number of use cases that guided the development of XQuery Update Facility 1.0 during its design and development.
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 “[UPDUC]” 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 http://lists.w3.org/Archives/Public/public-qt-comments/.
Publication as a Working Group Note does not imply endorsement by the W3C Membership. At the time of publication, work on this document was considered complete and no further revisions are anticipated. It is a stable document and may be used as reference material or cited from another document. However, this document may be updated, replaced, or made obsolete by other documents at any time.
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 Updates
1.1 Use Case "R" - Updating
Relational Data
1.1.1 Description
1.1.2 Document Type Definition (DTD)
1.1.2.1
Schema for users.xml
1.1.2.2
Schema for items.xml
1.1.2.3
Schema for bids.xml
1.1.3 Input Data
1.1.4 Updates and Results
1.1.4.1
Q1
1.1.4.2
Q2
1.1.4.3
Q3
1.1.4.4
Q4
1.1.4.5
Q5
1.1.4.6
Q6
1.1.4.7
Q7
1.1.4.8
Q8
1.1.4.9
Q9
1.2 Use
Case "Address Book" - synchronizing address book entries
1.2.1 Input Data
1.2.2 Q1
1.3 Use Case
"SOAP" - processing messages
1.3.1 Input Data
1.3.2 Q1
1.4 Use Case
"Namespaces" - moving elements from one namespace to
another
1.4.1 Description
1.4.2 Schema for the grant
application
1.4.3 Input Data
1.4.4 Q1
1.5 Use case
"Parts" - modifying recursive documents
1.5.1 Input data
1.5.2 Q1
1.5.3 Q2
1.5.4 Q3
1.5.5 Q4
1.5.6 Q6
1.6 Use case
"Nil"
1.6.1 XML Schema
1.6.2 Sample input data
("employees.xml")
1.6.3 Q1
A Normative
References
B Revision Log
(Non-Normative)
B.1 Changes in
internal WD
B.2 28 Aug 2007
Publication
The use cases listed below were created by the XML Query Working Group to illustrate important applications for an XML update facility. Each use case is focused on a specific application area, and contains a Document Type Definition (DTD) and example input data. Each use case specifies a set of updates that might be applied to the input data, and the expected resulting value of the modified input for each update. Since the English description of each query is concise, the expected results form an important part of the definition of each update directive. These use cases are inspired by the W3C XQuery Update Facility Requirements document.
These use cases represent a snapshot of an ongoing work. Some important application areas and important operations are not yet adequately covered by a use case. The XML Query Working Group reserves the right to add, delete, or modify individual queries or whole use cases as the work progresses. The presence of a query in this set of use cases does not necessarily indicate that the query will be expressible in the XQuery Update Facility to be created by the XML Query Working Group.
One important use of an XML update language will be to update data stored in relational databases. This use case describes a set of such possible updates.
This use case is based on performing updates on the data used in Use Case "R" from the [XML Query Use Cases]. The sample data from this Use Case is copied below for convenience, and exactly match the data found in the XQuery 1.0 Use Cases. Instead of DTDs, we describe this data with W3C XML Schemas.
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:
<?xml version="1.0" encoding="UTF-8"?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"> <xs:element name="users"> <xs:complexType> <xs:sequence> <xs:element minOccurs="0" maxOccurs="unbounded" ref="user_tuple"/> </xs:sequence> </xs:complexType> </xs:element> <xs:element name="user_tuple"> <xs:complexType> <xs:sequence> <xs:element ref="userid"/> <xs:element ref="name"/> <xs:element minOccurs="0" ref="rating"/> </xs:sequence> </xs:complexType> </xs:element> <xs:element name="userid" type="xs:string"/> <xs:element name="name" type="xs:string"/> <xs:element name="rating" type="xs:string"/> </xs:schema>
<?xml version="1.0" encoding="UTF-8"?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"> <xs:element name="items"> <xs:complexType> <xs:sequence> <xs:element minOccurs="0" maxOccurs="unbounded" ref="item_tuple"/> </xs:sequence> </xs:complexType> </xs:element> <xs:element name="item_tuple"> <xs:complexType> <xs:sequence> <xs:element ref="itemno"/> <xs:element ref="description"/> <xs:element ref="offered_by"/> <xs:element minOccurs="0" ref="start_date"/> <xs:element minOccurs="0" ref="end_date"/> <xs:element minOccurs="0" ref="reserve_price"/> </xs:sequence> </xs:complexType> </xs:element> <xs:element name="itemno" type="xs:string"/> <xs:element name="description" type="xs:string"/> <xs:element name="offered_by" type="xs:string"/> <xs:element name="start_date" type="xs:string"/> <xs:element name="end_date" type="xs:string"/> <xs:element name="reserve_price" type="xs:string"/> </xs:schema>
<?xml version="1.0" encoding="UTF-8"?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"> <xs:element name="bids"> <xs:complexType> <xs:sequence> <xs:element minOccurs="0" maxOccurs="unbounded" ref="bid_tuple"/> </xs:sequence> </xs:complexType> </xs:element> <xs:element name="bid_tuple"> <xs:complexType> <xs:sequence> <xs:element ref="userid"/> <xs:element ref="itemno"/> <xs:element ref="bid"/> <xs:element ref="bid_date"/> </xs:sequence> </xs:complexType> </xs:element> <xs:element name="userid" type="xs:string"/> <xs:element name="itemno" type="xs:string"/> <xs:element name="bid" type="xs:string"/> <xs:element name="bid_date" type="xs:string"/> </xs:schema>
The following data is an excerpt of the initial state for Q1. In this particular use case, each update begins with the state resulting from the prior update.
<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 tables:
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 |
The underlying database system has the following referential integrity constraints:
A foreign key on the BIDS table requires that BIDS.USERID contains a value that is found in USERS.USERID
A foreign key on the BIDS table requires that BIDS.ITEMNO contains a value that is found in ITEMS.ITEMNO
Add a new user (with no rating) to the users.xml view.
Solution in the XQuery Update Facility:
insert nodes <user_tuple> <userid>U07</userid> <name>Annabel Lee</name> </user_tuple> into doc("users.xml")/users
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> ... Snip ... <user_tuple> <userid>U06</userid> <name>Rip Van Winkle</name> <rating>B</rating> </user_tuple> <user_tuple> <userid>U07</userid> <name>Annabel Lee</name> </user_tuple> </users>
Enter a bid for user Annabel Lee on February 1st, 1999 for 60 dollars on item 1001.
Solution in the XQuery Update Facility:
let $uid := doc("users.xml")/users/user_tuple[name="Annabel Lee"]/userid return insert nodes <bid_tuple> <userid>{data($uid)}</userid> <itemno>1001</itemno> <bid>60</bid> <bid_date>1999-02-01</bid_date> </bid_tuple> into doc("bids.xml")/bids
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>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> <bid_tuple> <userid>U07</userid> <itemno>1001</itemno> <bid>60</bid> <bid_date>1999-02-01</bid_date> </bid_tuple> </bids>
Insert a new bid for Annabel Lee on item 1002, adding 10% to the best bid received so far for this item.
Solution in the XQuery Update Facility:
let $uid := doc("users.xml")/users/user_tuple[name="Annabel Lee"]/userid let $topbid := max(doc("bids.xml")/bids/bid_tuple[itemno=1002]/bid) return insert nodes <bid_tuple> <userid>{data($uid)}</userid> <itemno>1002</itemno> <bid>{$topbid*1.1}</bid> <bid_date>1999-02-01</bid_date> </bid_tuple> into doc("bids.xml")/bids
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>U07</userid> <itemno>1002</itemno> <bid>1320</bid> <bid_date>1999-03-05</bid_date> </bid_tuple> </bids>
The best bid for item 1002 had been at 1200, thus Annabel's bid is at 1320.
Set Annabel Lee's rating to B.
Solution in the XQuery Update Facility:
let $user := doc("users.xml")/users/user_tuple[name="Annabel Lee"] return if ($user/rating) then replace value of node $user/rating with "B" else insert node <rating>B</rating> into $user
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> ... Snip ... <user_tuple> <userid>U06</userid> <name>Rip Van Winkle</name> <rating>B</rating> </user_tuple> <user_tuple> <userid>U07</userid> <name>Annabel Lee</name> <rating>B</rating> </user_tuple> </users>
Place a bid for Annabel Lee 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. The first query illustrates the desired behavior if the limit is exceeded.
Solution in the XQuery Update Facility:
let $uid := doc("users.xml")/users/user_tuple[name="Annabel Lee"]/userid let $topbid := max(doc("bids.xml")/bids/bid_tuple[itemno=1007]/bid) where $topbid*1.1 <= 200 return insert nodes <bid_tuple> <userid>{data($uid)}</userid> <itemno>1007</itemno> <bid>{$topbid*1.1}</bid> <bid_date>1999-02-01</bid_date> </bid_tuple> into doc("bids.xml")/bids
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>U04</userid> <itemno>1007</itemno> <bid>225</bid> <bid_date>1999-02-12</bid_date> </bid_tuple> </bids>
In the above, adding 10% to the best bid on item 1007 would have required a bid of 237, which is more than the allowed limit of 200. Thus, the bids.xml document does not change.
Place a bid for Annabel Lee on item 1007, adding 10% to the best bid received so far on that item, but only if the bid amount does not exceed 500. This illustrates the behavior when the resulting value is within the limit.
Solution in the XQuery Update Facility:
let $uid := doc("users.xml")/users/user_tuple[name="Annabel Lee"]/userid let $topbid := max(doc("bids.xml")/bids/bid_tuple[itemno=1007]/bid) where $topbid*1.1 <= 500 return insert nodes <bid_tuple> <userid>{data($uid)}</userid> <itemno>1007</itemno> <bid>{$topbid*1.1}</bid> <bid_date>1999-02-01</bid_date> </bid_tuple> into doc("bids.xml")/bids
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>U04</userid> <itemno>1007</itemno> <bid>225</bid> <bid_date>1999-02-12</bid_date> </bid_tuple> <bid_tuple> <userid>U07</userid> <itemno>1007</itemno> <bid>237</bid> <bid_date>1999-04-01</bid_date> </bid_tuple> </bids>
Erase user Dee Linquent and the corresponding associated items and bids.
Solution in the XQuery Update Facility:
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, delete nodes $items, delete nodes $bids )
An alternative solution is:
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
The two solutions above highlight the fact that a list of delete operations is equivalent to deleting the list of nodes obtained by concatenating the operands on which the deletes applied.
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>
In the above, items 1005 and 1006, offered by user Dee Linquent, have been erased; item 1007 now directly follows item 1004. Notice that the whole subtrees rooted at the corresponding <item_tuple> elements have been erased.
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>
In the above, user Dee Linquent has been erased.
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>
No bids had been placed on items 1005 and 1006 offered by user Dee Linquent. However, Dee Linquent had placed a bid on item 1002; this bid has been erased.
Add the element <comment>This is a bargain !</comment> as the last child of the <item> element describing item 1002.
Solution in the XQuery Update Facility
declare revalidation strict; insert nodes <comment>This is a bargain !</comment> as last into doc("items.xml")/items/item_tuple[itemno=1002]
Expected resulting content of items.xml: The same as the original contents.
The items.xml document is unchanged and an error is raised. This update can not be applied without rendering the document invalid with respect to its schema.
Place a bid for Annabel Lee on item 1010, which does not exist in "items.xml". In this query, we assume that a referential integrity constraint in the underlying database system requires that no bid can be placed on an item unless it exists in the database.
Solution in XQuery:
let $uid := doc("users.xml")/users/user_tuple[name="Annabel Lee"]/userid return insert nodes <bid_tuple> <userid>{data($uid)}</userid> <itemno>1010</itemno> <bid>60</bid> <bid_date>2006-04-23</bid_date> </bid_tuple> into doc("bids.xml")/bids
Expected resulting content of bids.xml:the same as the original contents.
This update violates the previously-mentioned referential integrity constraint. Therefore, its execution raises a dynamic error (see Kinds of Errors in the XQuery specification).
Add a bid for Annabel Lee on item 1002, at a price 5 dollars below the current highest bid. A trigger in the underlying database ensures that a bid cannot be made at a lower price than the highest bid made so far on that item.
Solution in XQuery:
let $uid := doc("users.xml")/users/user_tuple[name="Annabel Lee"]/userid let $topbid := max(doc("bids.xml")//bid_tuple[itemno=1002]/bid) return insert nodes <bid_tuple> <userid>{data($uid)}</userid> <itemno>1002</itemno> <bid>{$topbid - 5.00}</bid> <bid_date>2006-04-23</bid_date> </bid_tuple> into doc("bids.xml")/bids
Expected resulting content of bids.xml: the same as the original contents.
This update causes the previously mentioned trigger to return an error. Therefore, its execution will raise a dynamic error (see Kinds of Errors in the XQuery specification).
In this scenario, an address book is synchronized among a central archive and two local copies. This scenario is inspired by the Unison file synchronizer. During synchronization, Address book entries with the same name element are considered to be the same entry. The order of entries is irrelevant. For simplicity, we assume that entries may be modified, but not inserted or deleted. When the two copies are synchronized, their state is saved into an archival version. Synchronization is performed as follows:
If an entry in one of the two copies is different from the archived one, but the other copy matches the archive, the modified copy is propagated to the archive and to the other copy.
If both copies differ from the archive, but they do not both modify the same element in the entry, or they modify the same element but the modified elements have the same value, then the changes from each copy are propagated to both the archive and the other copy.
If the copies have each modified the same entry, but modified it in different ways, a problem is reported in the log, and neither the archive nor the copies are changed. (For simplicity, we do not attempt to merge updates.)
The XQuery Update Facility is sufficient for the problem as scoped above. For more complex scenarios, the XQuery Scripting Extensions will simplify programming by allowing variable assignment and the ability to view the results of prior updates within a query.
This section describes the data prior to synchronization.
archive.xml: The central archive, before synchronization.
<archived-agenda> <last-synch-time>2005-10-05T10:00</last-synch-time> <entry> <name>Benjamin</name> <contact>benjamin@inria.fr</contact> </entry> <entry> <name>Dario</name> <contact>dario@uni-pisa.it</contact> </entry> <entry> <name>Anthony</name> <contact>tony@uni-toulon.fr</contact> </entry> </archived-agenda>
log.xml: The central log, before synchronization.
<log> </log>
copy1.xml: The first modified copy of the address book.
In this copy, Benjamin's contact has changed from INRIA to University of Versailles, Dario has moved from University of Pisa to University of Paris Sud, and Anthony has moved from University of Toulon to the ENA.
<agenda-version> <entry> <name>Benjamin</name> <contact>benjamin@uni-versailles.fr</contact> </entry> <entry> <name>Dario</name> <contact>dario@uni-parissud.fr</contact> </entry> <entry> <name>Anthony</name> <contact>tony@ena.fr</contact> </entry> </agenda-version>
copy2.xml: The second modified copy of the address book.
In this copy, Benjamin has also moved to University of Versailles, Dario has not moved, and Anthony has moved to the EHESS instead of the ENA:
<agenda-version> <entry> <name>Benjamin</name> <contact>benjamin@uni-versailles.fr</contact> </entry> <entry> <name>Dario</name> <contact>dario@uni-pisa.it</contact> </entry> <entry> <name>Anthony</name> <contact>tony@ehess.fr</contact> </entry> </agenda-version>
Synchronize the three logs as described in the description of this use case.
Solution in the XQuery Update Facility:
for $a in doc("archive.xml")/archived-agenda/entry, $v1 in doc("copy1.xml")/agenda-version/entry, $v2 in doc("copy2.xml")/agenda-version/entry where $a/name = $v1/name and $v1/name = $v2/name return if ($a/contact = $v1/contact and $v1/contact=$v2/contact) then () else if ($v1/contact = $v2/contact) then replace value of node $a/contact with $v1/contact else if ($a/contact = $v1/contact) then ( replace value of node $a/contact with $v2/contact, replace value of node $v1/contact with $v2/contact ) else if ($a/contact = $v2/contact) then ( replace value of node $a/contact with $v1/contact, replace value of node $v2/contact with $v1/contact ) else ( insert node <fail> <arch>{ $a }</arch> <v1>{ $v1 }</v1> <v2>{ $v2 }</v2> </fail> into doc("log.xml")/log ) , replace value of node doc("archive.xml")/*/last-synch-time with current-dateTime()
Expected Results:
Expected results of the agenda synchronization: A synchronization of the two versions of the agenda made on April 23th, 2006, at noon, should have the following impact on the archive, versions, and log.
Note:
In the following XML results, the comments (shown in italic font) were not created by the update directives, and are not part of the result documents. They have been added to explain where each part of a result document comes from.
archive.xml
<archived-agenda> <last-synch-time>2006-04-23T12:00</last-synch-time> <entry> <name>Benjamin</name> copied from the modified entries <contact>benjamin@uni-versailles.fr</contact> </entry> <entry> <name>Dario</name> copied from first modified version <contact>dario@uni-parissud.fr</contact> </entry> <entry> <name>Anthony</name> unchanged due to conflict <contact>tony@uni-toulon.fr</contact> </entry> </archived-agenda>
log.xml
<log> update failure details <fail> <arch> archived agenda version <entry> <name>Anthony</name> <contact>tony@uni-toulon.fr</contact> </entry> </arch> <v1> first modified version <entry> <name>Anthony</name> <contact>tony@ena.fr</contact> </entry> </v1> <v2> second modified version <entry> <name>Anthony</name> <contact>tony@ehess.fr</contact> </entry> </v2> </fail> </log>
copy1.xml
<agenda-version> <entry> <name>Benjamin</name> kept after synchronization <contact>benjamin@uni-versailles.fr</contact> </entry> <entry> kept after synchronization <name>Dario</name> <contact>dario@uni-parissud.fr</contact> </entry> <entry> kept after synchronization failure <name>Anthony</name> <contact>tony@ena.fr</contact> </entry> </agenda-version>
copy2.xml
<agenda-version> <entry> kept after synchronization <name>Benjamin</name> <contact>benjamin@uni-versailles.fr</contact> </entry> <entry> value taken from the other modified version <name>Dario</name> <contact>dario@uni-parissud.fr</contact> </entry> <entry> kept after synchronization failure <name>Anthony</name> <contact>tony@ehess.fr</contact> </entry> </agenda-version>
This use case processes the message found in Example 1 of the [SOAP Version 1.2 Part 0:Primer] to produce the message found in Example 2. The original message is not modified, but the new message is a modified copy of the original.
The input data is taken directly from Example 1 of the [SOAP Version 1.2 Part 0:Primer].
<?xml version='1.0' ?> <env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope"> <env:Header> <m:reservation xmlns:m="http://travelcompany.example.org/reservation" env:role="http://www.w3.org/2003/05/soap-envelope/role/next" env:mustUnderstand="true"> <m:reference>uuid:093a2da1-q345-739r-ba5d-pqff98fe8j7d </m:reference> <m:dateAndTime>2001-11-29T13:20:00.000-05:00 </m:dateAndTime> </m:reservation> <n:passenger xmlns:n="http://mycompany.example.com/employees" env:role="http://www.w3.org/2003/05/soap-envelope/role/next" env:mustUnderstand="true"> <n:name>Åke Jógvan Øyvind</n:name> </n:passenger> </env:Header> <env:Body> <p:itinerary xmlns:p="http://travelcompany.example.org/reservation/travel"> <p:departure> <p:departing>New York</p:departing> <p:arriving>Los Angeles</p:arriving> <p:departureDate>2001-12-14</p:departureDate> <p:departureTime>late afternoon</p:departureTime> <p:seatPreference>aisle</p:seatPreference> </p:departure> <p:return> <p:departing>Los Angeles</p:departing> <p:arriving>New York</p:arriving> <p:departureDate>2001-12-20</p:departureDate> <p:departureTime>mid-morning</p:departureTime> <p:seatPreference/> </p:return> </p:itinerary> <q:lodging xmlns:q="http://travelcompany.example.org/reservation/hotels"> <q:preference>none</q:preference> </q:lodging> </env:Body> </env:Envelope>
Moreover, we assume the dynamic context associates to "airports" the following sequence:
<AIRPORT><CITY>New York</CITY><CODE>JFK</CODE></AIRPORT> <AIRPORT><CITY>New York</CITY><CODE>LGA</CODE></AIRPORT> <AIRPORT><CITY>New York</CITY><CODE>EWR</CODE></AIRPORT> <AIRPORT><CITY>Los Angeles</CITY><CODE>LAX</CODE></AIRPORT> <AIRPORT><CITY>San Francisco</CITY><CODE>SFO</CODE></AIRPORT>
Check to see if the airports are unambiguously specified in the incoming message. Produce a SOAP response by transforming the incoming message, modifying the time and date to the current time, and replacing the body with a request to clarify which airport should be used for New York City.
Solution in the XQuery Update Facility:
declare namespace env="http://www.w3.org/2003/05/soap-envelope"; declare namespace m="http://travelcompany.example.org/reservation"; declare namespace n="http://mycompany.example.com/employees"; declare namespace p="http://travelcompany.example.org/reservation/travel"; (: A clarification is needed only if there are no : airports or more than one for a given city. If : there is precisely one, there is no need to : ask for information on that city. :) declare function local:airportChoices($city as xs:string) { let $airports := collection("airports")[CITY = $city] return if (count($airports) = 0) then <error> No airports found for {$city}!</error> else if (count($airports) > 1) then <airportChoices> { for $c in $airports/CODE return (string( $c ), " ") } </airportChoices> else () }; (: Make sure that each airport is unambiguous. If there is : more than one airport for a city, ask for clarification. : : The primer only shows the error condition, so it is not : clear what to do if there are no errors. Here, we simply : return the airports in the itinerary. :) declare function local:airports($in as element(env:Envelope)) { let $departureDeparting := $in//p:departure/p:departing let $departureDepartingAirports := collection("airports")[CITY = $departureDeparting] let $departureArriving := $in//p:departure/p:arriving let $departureArrivingAirports := collection("airports")[CITY = $departureArriving] let $returnDeparting := $in//p:return/p:departing let $returnDepartingAirports := collection("airports")[CITY = $returnDeparting] let $returnArriving := $in//p:return/p:arriving let $returnArrivingAirports := collection("airports")[CITY = $returnArriving] return if ( count($departureDepartingAirports)=0 or count($departureDepartingAirports)>1 or count($departureArrivingAirports)=0 or count($departureArrivingAirports)>1 or count($returnDepartingAirports)=0 or count($returnDepartingAirports)>1 or count($returnArrivingAirports)=0 or count($returnArrivingAirports)>1 ) then <p:itineraryClarification> <p:departure> <p:departing> { local:airportChoices($departureDeparting) } </p:departing> <p:arriving> { local:airportChoices($departureArriving) } </p:arriving> </p:departure> <p:return> <p:departing> { local:airportChoices($returnDeparting) } </p:departing> <p:arriving> { local:airportChoices($returnArriving) } </p:arriving> </p:return> </p:itineraryClarification> else <p:itinerary> <p:departure> <p:departing>{$departureDeparting}</p:departing> <p:arriving>{$departureArriving}</p:arriving> </p:departure> <p:return> <p:departing>{$returnDeparting}</p:departing> <p:arriving>{$returnArriving}</p:arriving> </p:return> </p:itinerary> }; declare variable $msg external; copy $out := $msg/env:Envelope modify ( replace value of node $out//m:dateAndTime with fn:current-dateTime(), replace node $out//env:Body with <env:Body> { local:airports($out) } </env:Body> ) return $out
Expected Result (from [SOAP Version 1.2 Part 0:Primer]):
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope"> <env:Header> <m:reservation xmlns:m="http://travelcompany.example.org/reservation" env:role="http://www.w3.org/2003/05/soap-envelope/role/next" env:mustUnderstand="true"> <m:reference>uuid:093a2da1-q345-739r-ba5d-pqff98fe8j7d </m:reference> <m:dateAndTime>2001-11-29T13:35:00.000-05:00 </m:dateAndTime> </m:reservation> <n:passenger xmlns:n="http://mycompany.example.com/employees" env:role="http://www.w3.org/2003/05/soap-envelope/role/next" env:mustUnderstand="true"> <n:name>Åke Jógvan Øyvind</n:name> </n:passenger> </env:Header> <env:Body> <p:itineraryClarification xmlns:p="http://travelcompany.example.org/reservation/travel"> <p:departure> <p:departing> <p:airportChoices> JFK LGA EWR </p:airportChoices> </p:departing> </p:departure> <p:return> <p:arriving> <p:airportChoices> JFK LGA EWR </p:airportChoices> </p:arriving> </p:return> </p:itineraryClarification> </env:Body> </env:Envelope>
This use case shows how (parts of) the elements in a document may be moved from one namespace to another.
An agriculture company and an university research lab are making a joint proposal to the National Agricultural Research Agency. An initial proposal has been made by cut and paste out of snippets provided by the two partners. Before being submitted, the proposal has to be moved to the National Agricultural Research Agency (NARA) namespace.
The grant application document must conform to the following schema:
<xsd:schema xmlns:xsd='http://www.w3.org/2001/XMLSchema' targetNamespace="http://www.anr.fr/nara" elementFormDefault="qualified"> <xsd:element name='grant'> <xsd:complexType> <xsd:sequence> <xsd:element ref='name'/> <xsd:element ref='lab' minOccurs='0' maxOccurs='unbounded'/> </xsd:sequence> </xsd:complexType> </xsd:element> <xsd:element name='lab'> <xsd:complexType> <xsd:sequence> <xsd:element ref='address' minOccurs='0' maxOccurs='1'/> <xsd:choice minOccurs='0' maxOccurs='unbounded'> <xsd:element ref='researcher'/> <xsd:element ref='PhD'/> <xsd:element ref='engineer'/> <xsd:element ref='lab' minOccurs="0" maxOccurs="unbounded"/> </xsd:choice> </xsd:sequence> <xsd:attribute name='name' type='xsd:string' use='required'/> </xsd:complexType> </xsd:element> <xsd:element name='PhD'> <xsd:complexType> <xsd:attribute name='advisor' type='xsd:IDREF' use='required'/> </xsd:complexType> </xsd:element> <xsd:element name='researcher'> <xsd:complexType> <xsd:attribute name='rid' type='xsd:ID' use='required'/> <xsd:attribute name='name' type='xsd:string' use='required'/> <xsd:attribute name='position' type='xsd:string' use='required'/> </xsd:complexType> </xsd:element> <xsd:element name='engineer'> <xsd:complexType> <xsd:attribute name='name' type='xsd:string' use='required'/> </xsd:complexType> </xsd:element> </xsd:schema>
The initial draft produced by the participants, "grant.xml", has the following content. The use of namespaces reflects the cut-and-paste approach used to create it:
<?xml version='1.0' ?> <grant xmlns:nara="http://www.anr.fr/nara"> <nara:lab name="AgroPlus"> <nara:address>Saclay, France</nara:address> <nara:researcher rid="r1" name="Fred" position="technical staff"/> <nara:researcher rid="r2" name="Liz" position="lab assistant" /> <coop:PhD xmlns:coop="http://www.gouv.fr/univ-industry-coop/" name="Marie" advisor="r1"/> <agro:lab xmlns:agro="http://www.agroplus.com" name="Dairy Dept"> <agro:engineer name="Marc"/> </agro:lab> </nara:lab> <univ:lab xmlns:univ="http://www.education.gouv.fr" name="Food Engineering Dept, Orsay U."> <univ:address>Orsay, France</univ:address> <univ:researcher rid="r3" name="Henry" position="associate professor"/> <univ:PhD name="Robert" advisor="r3"/> <PhD name="Julia" advisor="r1"/> </univ:lab> </grant>
Move all elements into the NARA namespace ("http://www.anr.fr/nara").
Solution in the XQuery Update Facility:
declare namespace nara = "http://www.anr.fr/nara"; for $e in doc("grant.xml")//* where not (namespace-uri($e) eq "http://www.anr.fr/nara") return rename node $e as QName("http://www.anr.fr/nara", concat("nara:",local-name($e)))
Expected Result:
<nara:grant xmlns:nara="http://www.anr.fr/nara"> <nara:lab name="AgroPlus"> <nara:address>Saclay, France</nara:address> <nara:researcher rid="r1" name="Fred" position="technical staff"/> <nara:researcher rid="r2" name="Liz" position="lab assistant" /> <nara:PhD xmlns:coop="http://www.gouv.fr/univ-industry-coop/" name="Marie" advisor="r1"/> <nara:lab name="Dairy Dept"> <nara:engineer name="Marc"/> </nara:lab> </nara:lab> <nara:lab name="Food Engineering Dept, Orsay U."> <nara:address>Orsay, France</nara:address> <nara:researcher rid="r3" name="Henry" position="associate professor"/> <nara:PhD name="Robert" advisor="r3"/> <nara:PhD name="Julia" advisor="r1"/> </nara:lab> </nara:grant>
This use case illustrates modifications to documents having a recursive structure.
Each update in this use case applies on the following documents. In these examples, we assume each update is applied to a fresh copy of the original data.
Document "part-tree.xml":
<parttree> <part partid="0" name="car"> <part partid="1" name="engine"> <part partid="3" name="piston"/> </part> <part partid="2" name="door"> <part partid="4" name="window"/> <part partid="5" name="lock"/> </part> </part> <part partid="10" name="skateboard"> <part partid="11" name="board"/> <part partid="12" name="wheel"/> </part> <part partid="20" name="canoe"/> </parttree>
Document "part-list.xml":
<?xml version="1.0" encoding="ISO-8859-1"?> <partlist> <part partid="0" name="car"/> <part partid="1" partof="0" name="engine"/> <part partid="2" partof="0" name="door"/> <part partid="3" partof="1" name="piston"/> <part partid="4" partof="2" name="window"/> <part partid="5" partof="2" name="lock"/> <part partid="10" name="skateboard"/> <part partid="11" partof="10" name="board"/> <part partid="12" partof="10" name="wheel"/> <part partid="20" name="canoe"/> </partlist>
Delete all parts in "part-tree.xml".
Solution in the XQuery Update Facility:
delete nodes doc("part-tree.xml")//part
Expected resulting document "part-tree.xml":
<parttree/>
Delete all parts belonging to a car in "part-tree.xml", leaving the car itself.
Solution in the XQuery Update Facility:
delete nodes doc("part-tree.xml")//part[@name="car"]//part
Expected resulting document "part-tree.xml":
<parttree> <part partid="0" name="car"/> <part partid="10" name="skateboard"> <part partid="11" name="board"/> <part partid="12" name="wheel"/> </part> <part partid="20" name="canoe"/> </parttree>
Delete all parts belonging to a car in "part-list.xml", leaving the car itself.
Solution 1 in the XQuery Update Facility (leveraging "part-tree.xml"):
for $pt in doc("part-tree.xml")//part[@name="car"]//part, $pl in doc("part-list.xml")//part where $pt/@partid eq $pl/@partid return delete nodes $pl
Solution 2 (using a recursive updating function):
declare updating function local:delete-subtree($p as element(part)) { for $child in doc("part-list.xml")//part where $p/@partid eq $child/@partof return ( delete nodes $child, local:delete-subtree($child) ) }; for $p in doc("part-list.xml")//part[@name="car"] return local:delete-subtree($p)
Note:
Because this data is not covered by a schema, an implementation
that supports static typing will raise an error for the comparison
$p/@partid eq $child/@partof
. This can be solved by
creating a schema for the data and importing it into the query.
Expected Result:
<partlist> <part partid="0" name="car"/> <part partid="10" name="skateboard"/> <part partid="11" partof="10" name="board"/> <part partid="12" partof="10" name="wheel"/> <part partid="20" name="canoe"/> </partlist>
Add a radio to the car in "part-tree.xml", using a part number that hasn't been taken.
Solution in the XQuery Update Facility:
let $next := max(doc("part-tree.xml")//@partid) + 1 return insert nodes <part partid="{$next}" name="radio"/> into doc("part-tree.xml")//part[@partid=0 and @name="car"]
Expected Result:
<parttree> <part partid="0" name="car"> <part partid="21" name="radio"/> <part partid="1" name="engine"> <part partid="3" name="piston"/> </part> <part partid="2" name="door"> <part partid="4" name="window"/> <part partid="5" name="lock"/> </part> </part> <part partid="10" name="skateboard"> <part partid="11" name="board"/> <part partid="12" name="wheel"/> </part> <part partid="20" name="canoe"/> </parttree>
Note:The position of the new element with respect to its siblings is implementation-dependent. If position is significant, and the user wants to ensure the element appears last, for example, "as last" should be used, as in the following query:
let $next := max(doc("part-tree.xml")//@partid) + 1 return insert nodes <part partid="{$next}" name="radio"/> as last into doc("part-tree.xml")//part[@partid=0 and @name="car"]
The head office has adopted a new numbering scheme. In "part-tree.xml", add 1000 to all part numbers for cars, 2000 to all part numbers for skateboards, and 3000 to all part numbers for canoes.
Solution in the XQuery Update Facility:
for $keyword at $i in ("car", "skateboard", "canoe"), $parent in doc("part-tree.xml")//part[@name=$keyword] let $descendants := $parent//part for $p in ($parent, $descendants) return replace value of node $p/@partid with $i*1000+$p/@partid
Expected result:
<parttree> <part partid="1000" name="car"> <part partid="1021" name="radio"/> <part partid="1001" name="engine"> <part partid="1003" name="piston"/> </part> <part partid="1002" name="door"> <part partid="1004" name="window"/> <part partid="1005" name="lock"/> </part> </part> <part partid="2010" name="skateboard"> <part partid="2011" name="board"/> <part partid="2012" name="wheel"/> </part> <part partid="3020" name="canoe"/> </parttree>
This use case demonstrates transform expressions which construct modified copies of some data, which must remain valid according to the original schema. In this use case, keeping the modified copy valid requires adding an xsi:nil attribute.
A employees data set is described by the following XML Schema:
<?xml version="1.0" encoding="UTF-8" ?> <xsd:schema targetNamespace="http://www.example.com/employees" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xsd:element name="employees"> <xsd:complexType> <xsd:element name="employee" minOccurs="0" maxOccurs="unbounded"> <xsd:complexType> <xsd:attribute name="mgr" type="xsd:boolean" default="false"/> <xsd:attribute name="dept" type="xsd:string"/> <xsd:sequence> <xsd:element name="name" type="xsd:string"/> <xsd:element name="salary" type="xsd:decimal" nillable="true"/> </xsd:sequence> </xsd:complexType> </xsd:element> </xsd:complexType> </xsd:element> </xsd:schema>
<employees> <employee mgr="true" dept="Toys"> <name>Smith</name> <salary>100000</salary> </employee> <employee dept="Toys"> <name>Jones</name> <salary>60000</salary> </employee> <employee mgr="true" dept="Shoes"> <name>Roberts</name> <salary>150000</salary> </employee> </employees>
Return all managers, omitting their salaries for confidentiality reasons. The returned document must be valid according to its XML Schema, so the query adds an xsi:nil attribute with value "true" to the salary element.
Solution in the XQuery Update Facility:
for $e in doc("employees.xml")//employee where $e/@manager = true() return copy $emp := $e modify ( replace value of node $emp/salary with "" , insert nodes (attribute xsi:nil {"true"}) into $emp/salary ) return $emp
Expected result:
<employee mgr="true" dept="Toys"> <name>Smith</name> <salary xsi:nil="true"/> </employee> <employee mgr="true" dept="Shoes"> <name>Roberts</name> <salary xsi:nil="true"/> </employee>
This log records the substantive changes that have been made to this document since the Working Draft of 8 May 2006. Minor editorial changes are not included in this log.
Clarified that the "application constraint" in 1.1.4.8 Q8 is a database integrity constraint, and added the constraint to the description of the data, in response to http://www.w3.org/Bugs/Public/show_bug.cgi?id=3796#add_comment.
Clarified that updates for Use Case "R" are cumulative (see http://www.w3.org/Bugs/Public/show_bug.cgi?id=3567#c1).
Fixed update conflict in Use Case "Address", and clarified that some things would be easier using updates together with the Scripting Extensions (see http://www.w3.org/Bugs/Public/show_bug.cgi?id=3578#c1).
Fixed several errors in Use Case "SOAP" (see http://www.w3.org/Bugs/Public/show_bug.cgi?id=3578#c3).
Fixed Use Case "R" Q4 so that it tests to see whether a rating already exists (see http://www.w3.org/Bugs/Public/show_bug.cgi?id=3578#c4).
Fixed Use Case "Parts" Q3, fixing several bugs, see (http://www.w3.org/Bugs/Public/show_bug.cgi?id=3578#c6).