XRX/Autoincrement File ID
Motivation
editYou want to create a new file and automatically create a new identifier for the file.
Method
editCreate an XML file with a single counter in it. Use this counter as part of the file name. After you confirm that the file has been saved, increment the counter using the update function.
Sample XML File to Hold Next ID
edit<data>
<next-id>47</next-id>
</data>
Sample Code for save-new.xq
editThe following example works with an incoming HTTP POST request.
xquery version "1.0";
declare namespace exist = "http://exist.sourceforge.net/NS/exist";
declare namespace xmldb = "http://exist-db.org/xquery/xmldb";
declare namespace request="http://exist-db.org/xquery/request";
declare option exist:serialize "method=html media-type=text/html indent=yes";
(: save a new task - filename: save-new.xq - change base path if this changes. :)
(: this only works with POST data :)
let $my-doc := request:get-data()
(: the base directory URL for this XQuery. Use this to put links in the result page. :)
let $base-path := substring-before(request:get-url(), '/save-new.xq')
(: these paths must start with '/db' and are used for passing to doc() :)
let $data-collection := '/db/app/data'
let $next-id-file-path := '/db/app/edit/next-id.xml'
let $next-id := doc($next-id-file-path)/data/next-id/text()
(: use this as an arugment for for store command :)
let $new-term-base-file-name := concat($next-id, '.xml')
(: use this for doc :)
let $new-term-file-path := concat($data-collection, '/', $new-term-base-file-name)
(: add this line for testing in your local system or if you don't have group or RBAC set up :)
let $login := xmldb:collection($data-collection, 'mylogin', 'mypassword')
(: store the new document in the given collction :)
let $store-return-status := xmldb:store($data-collection, $new-term-base-file-name, $my-doc)
(: increment the next-id by one for the next document. You can also use "update value" :)
let $retCode1 := update replace doc($next-id-file-path)/data/next-id/text()
with ($next-id + 1)
(: note that next-id is now the next item so the current is next-id -1 :)
(: update the ID in the new document. Note that the saved document must have
the ID in the path /app/id. :)
let $retCode2 := update replace doc($new-term-file-path)/app/id with <id>{$next-id - 1}</id>
return
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Save Confirmation</title>
</head>
<body>
<h1>New file has been saved. id={$next-id - 1}.</h1>
</body>
</html>
Taking into account concurrent access
editSame as above but with locking the node holding the identifier.
The following example works with an incoming HTTP POST request.
xquery version "1.0";
declare namespace exist = "http://exist.sourceforge.net/NS/exist";
declare namespace xmldb = "http://exist-db.org/xquery/xmldb";
declare namespace request="http://exist-db.org/xquery/request";
declare option exist:serialize "method=html media-type=text/html indent=yes";
(: save a new task - filename: save-new.xq - change base path if this changes. :)
(: this only works with POST data :)
let $my-doc := request:get-data()
(: the base directory URL for this XQuery. Use this to put links in the result page. :)
let $base-path := substring-before(request:get-url(), '/save-new.xq')
(: these paths must start with '/db' and are used for passing to doc() :)
let $data-collection := '/db/app/data'
let $next-id-file-path := '/db/app/edit/next-id.xml'
(: Get next-id current value and increment the node value with an exclusive lock of the node :)
let $next-id := util:exclusive-lock(
doc($next-id-file-path)/data/next-id,
let $current-id := doc($next-id-file-path)/data/next-id/text()
let $retCode := update replace doc($next-id-file-path)/data/next-id/text() with ($current-id + 1)
return ($current-id - 1))
(: Warning - Pitfall : $current-id evaluation changes after the update :)
(: use this as an arugment for for store command :)
let $new-term-base-file-name := concat($next-id, '.xml')
(: use this for doc :)
let $new-term-file-path := concat($data-collection, '/', $new-term-base-file-name)
(: add this line for testing in your local system or if you don't have group or RBAC set up :)
let $login := xmldb:collection($data-collection, 'mylogin', 'mypassword')
(: store the new document in the given collction :)
let $store-return-status := xmldb:store($data-collection, $new-term-base-file-name, $my-doc)
(: update the ID in the new document. Note that the saved document must have
the ID in the path /app/id. :)
let $retCode2 := update replace doc($new-term-file-path)/app/id with <id>{$next-id}</id>
return
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Save Confirmation</title>
</head>
<body>
<h1>New file has been saved. id={$next-id}.</h1>
</body>
</html>
Adding and Incrementing an Attribute
editNote that the syntax for inserting an attribute is the following:
update insert attribute {$attrName} {$attrValue} into expression
The keyword attribute must be used and be followed with the attribute name and value.
If you want to store a new id in an attribute the syntax is:
update insert attribute {'id'} {$next-id} into $doc/root
Note: if the attribute
already exists in the target node
then insert
works as replace
And if you are updating the id you can use the replace function with the @ in the path expression:
update replace doc/root/@id with ($next-id + 1)