Saturday 4 January 2014

How to write a BaseX XQuery with RESTXQ

Caution: technical stuff. Over holidays we managed to put up a BaseX XML database instance as a web application on several machines. But how to execute an XQuery there? A simple approach: use an already provided BaseX REST interface. However, the BaseX team seems more interested in RESTXQ, "a set of XQuery 3.0 Annotations and a small set of functions to enable XQuery to provide RESTful services, thus enabling Web Application development in XQuery" (from the unofficial RESTXQ draft). BaseX supports RESTXQ very well, but the existing documentation is somewhat sparse for a non-programmer like me. Conspicuously absent is an example of a "standard" XQuery search directed at a database (or, in XQuery parlance, a collection). This will be provided here.

The task. A BaseX war instance is deployed on a Jetty server (on my machine, which runs Debian Mint, in /var/lib/jetty8/webapps), accessible on the address http://localhost:8080/BaseX772. A database collection crobib was created and populated with several TEI XML files (with FRBR-structured bibliographical data on Croatian Latin authors, works, and manifestations). We want to execute the following query over the internet, finding the text under tei:persName element as child of all eleventh tei:person elements in the collection:

declare namespace tei = "http://www.tei-c.org/ns/1.0";
for $i in collection("crobib")//tei:person[11]
return element p { $i/tei:persName//text() }

The solution. An .xq script should be written and placed (in our case) under the root of the BaseX war archive. If all goes well, it is found and read by Jetty and BaseX when the server is restarted. This is the script (cbxq.xq). Note how the resulting sequence has to be wrapped in a div element:

import module namespace rest = "http://exquery.org/ns/restxq";
declare namespace page = 'http://basex.org/examples/web-page';
declare namespace tei = "http://www.tei-c.org/ns/1.0";
declare %rest:GET %rest:POST %rest:path("person")
function page:person() {
element div {
for $i in collection("crobib")//tei:person[11]
return ( element p { $i/tei:persName//text() } )
}
};
return

The script is requested over the following address: http://localhost:8080/BaseX772/person.

Going further. We want to search not just for eleventh tei:person element, but for whichever we want. The number of the element should be turned into a variable holding an integer, and the variable will be given as part of the HTML address request. The script now looks like this:

import module namespace rest = "http://exquery.org/ns/restxq";
declare namespace page = "http://basex.org/examples/web-page";
declare namespace tei = "http://www.tei-c.org/ns/1.0";
declare %rest:GET %rest:POST %rest:path("person")
%rest:query-param("var", "{$var}")
function page:person($var as xs:integer) {
element div {
for $i in collection("crobib")//tei:person[$var]
return ( element p { $i/tei:persName//text() } )
}
};
return

We had to declare query parameter var: %rest:query-param("var", "{$var}") and to instruct the function page:person to expect it: function page:person($var as xs:integer).

The script is requested with a call such as this (querying the hundredth tei:person): http://localhost:8080/BaseX772/person?var=100.

No comments:

Post a Comment