XML RPN

Z39.50 can carry any type of query. In almost all cases, RPN also known as Type-1 is used. The Z39.50 standard only defines RPN in terms of ASN.1. For practical reasons a string representation, PQF, was defined. This was part of first release of YAZ in 1995. PQF is good for logging since it’s one line for a complete query; reasonably easy to type, but not an end-user language.

A query rewrite and validation facility was needed for Metaproxy. We thought this could be done via XSLT and so an XML representation of RPN was needed. The API uses the name xmlquery - not to be confused with W3C’s XML Query. We should have used a different name, but it is not worth changing now .. This API has been part of YAZ 2.1.14 from 2006.

The XML encoding

The encoding between RPN and XML is pretty straightforward.

General

The whole RPN is wrapped in a query tag with a “rpn” element inside. This allows the XML representation to carry other types than RPN in a future release. The RPN may also carry an attribute set with it which will apply to all attributes - except where they are overwritten by local ones.

The XML for Bib-1 RPN queries looks as follows:

   <query>
     <rpn set=”Bib-1”>
       subquery
     </rpn>
 </query>

Here subquery is either one of the of three types (Attributes Plus Term / Operator / ResultSet). The general outer tags, “rpn” and “query” will be omitted in the examples that follow.

Attribute Plus Term

For PQF the default term type is general. And this was the default behavior for Z39.50 version 2. Here are the mapping - expressed in PQF and XML.

  • @attr t1=v1 @attr t2=v2 … @attr tN=vN term

  <apt>
    <attr type=”tN” value=”vN”/>
     …
    <attr type=”t2” value=”v2”/>
    <attr type=”t1” value=”v1”/>
    <term type=”general>term</term>
 </apt>

PQF also allows a local attribute set to be defined for each attribute (@attr). In most cases it is not defined, but if it is that gets mapped to a “set” attribute for the attr tag.

  • @attr set1 t1=v1 @attr set2 t2=v2 … @attr setN tN=vN term

  <apt>
    <attr set=”setN” type=”tN” value=”vN”/>
     …
    <attr set=”set2” type=”t2” value=”v2”/>
    <attr set=”set1” type=”t1” value=”v1”/>
    <term type=”general>term</term>
 </apt>

Operator

Operators are recursively defined.. In this example the ‘a’ and ‘b’ are shown as terms but could be any sub query.

  • @and a b

  <operator type=”and”>
    <apt><term type=”general”>a</term></apt>
    <apt><term type=”general”>b</term></apt>
 </operator>

  • @or a b

  <operator type=”or”>
    <apt><term type=”general”>a</term></apt>
    <apt><term type=”general”>b</term></apt>
 </operator>

  • @prox 0 3 1 2 k 2 a b

  <operator type=”prox” exclusion=”false” distance=”3”
       ordered=”true” relationType=”2”
       knownProximityUnit=”2”>
    <apt><term type=”general”>a</term></apt>
    <apt><term type=”general”>b</term></apt>
 </operator>

ResultSet

  • @set ref

 <rset>1</rset>

API

The conversion utilities for XML RPN are defined in header yaz/xmlquery.h. Note that the utilities are only available if YAZ is compiled with Libxml2 support.

XML queries are encoded with function yaz_query2xml, declared as:

void yaz_query2xml(const Z_Query *q, xmlDocPtr *docp);

This function takes any query defined by the Z39.50 standard and it will create and return an XML Document if successful. If unsuccessful, *docp is NULL.

XML queries are decoded with function yaz_xml2query, declared as:

void yaz_xml2query(const xmlNode *xmlnodep,
    Z_Query **query, <span class="caps">ODR</span> odr,
    int *error_code, const char **addinfo);
This function takes an XML node pointer which must point to the query element of the XML query. It will decode the XML but will treat certain tags as “custom-diagnostics”. The custom diagnostics are used as a signal from the result of an XSL transform to denote an error in the original query.. such as unsupported use attribute. If one of those are met the error_code and additional information, addinfo, are returned . All memory is allocated using the ODR stream passed. If no diagnostic tags are met, the resulting query is encoded in *query.

Applications

If YAZ is compiled from sources, a utility program util/yaz-xmlquery will appear. This program allows us to test the API.

Metaproxy comes with filter query_rewrite which uses the XML RPN for validating queries using XSLT.

3 Comments

Why all the void functions?

Thanks, Adam, it's good to see this laid out, and refreshing to see an XML format that can be explained in a single page of A4 and doesn't involve half a dozen different namespaces!

Why are the API functions all void, with return values shoved into reference parameters? It makes me feel like I'm invoking GOSUB all over again :-)

Finally, it would be good to see the custom error-code XML structures as well, so I can build them into my own XML-PQF documents when necessary.

void functions and diagnostics

No good reasons for void functions. You can feel more functional like with:

xmlDocPtr mike_query2xml(const Z_Query *q) {
   xmlDocPtr doc;
   yaz_query2xml(q, &doc);
   return doc;
}
Similar for yaz_xml2query.

The custom diagnostic is triggered by element diagnostic with takes attributes code and addinfo.

This is an example of a query which would render ‘unsupported use attribute’ , ‘title’.

<query><rpn set=”Bib-1”><apt>
  <diagnostic code=”114” addinfo=”4”/>
  <attr type=”1” value=”4”/><term type=”general”>x</term>
</apt></rpn></query>
The attr, term etc. are not used.. yaz_xml2query simply returns the diagnostic and a NULL (void) query.

Brevity is the soul of awesomeness

"The custom diagnostic is triggered by element diagnostic with takes attributes code and addinfo."

I just LOVE it when schemas are simple enough to describe in a single 14-word sentence!