XRX/Autoincrement File ID

< XRX

Motivation edit

You want to create a new file and automatically create a new identifier for the file.

Method edit

Create 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 edit

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'

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 edit

Same 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 edit

Note 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)

Back: Regular Expression Builder Next: Move a Resource