Chapter 5. Programming with the Qizx/open API

Table of Contents

1. Compiling and running the code samples
2. Creating a work session
3. Compiling and executing queries
3.1. Compiling and running the code of this lesson

The API in Qizx/open is much simpler than in Qizx because it has not to deal with the management of XML libraries. This section is a simplified version of the tutorial found in Chapter 5, Programming with the Qizx/open API.

1. Compiling and running the code samples

The code samples used to illustrate this chapter (class OpenQuery.java) are found in the docs/samples/programming/qizxopen directory. Files containing XQuery scripts are found in the docs/samples/programming/qizxopen/queries sub-directory. These scripts are almost the same as those used in the Qizx tutorial, except that access to documents is performed through file paths instead of locations within an XML Library.

You'll need a recent version of ant, a Java-based build tool to compile and run the codes samples.

2. Creating a work session

To create a XML Query session, we need a factory, which is an instance of the class XQuerySessionManager. This class manages documents and XML Query modules, so it is recommended to use a single instance from which all sessions are created. The argument of the constructor is an URL used to locate XQuery modules: here it points to the current directory, but we could also use an HTTP URL pointing to a network server.

 File currentDir = new File(System.getProperty("user.dir")); 
 XQuerySessionManager sm = new XQuerySessionManager(currentDir.toURL());

 XQuerySession session = sm.createSession();

Tuning the document cache:

If you have to handle large documents or many documents in a Qizx/open application, it can be useful to tune the size of the document cache. This cache keeps the last documents parsed, so it avoids reloading documents in different sessions. However the cache detects a modification on a document in a file and reloads it.

Reminder: in Qizx/open, documents are always parsed into memory before processing. The functions that load documents are doc() and collection(). They are documented in the last section of this appendix: Differences with Qizx database engine.

Use XQuerySessionManager.setTransientDocumentCacheSize(int size) to specify a size in bytes for this cache. You can also use the system property com.qizx.docpool.maxsize (For example you would specify -Dcom.qizx.docpool.maxsize=100000000 on the command line).

3. Compiling and executing queries

Compiling and running a XML Query script is fairly easy:

Expression expr = session.compileExpression(script);1
ItemSequence results = expr.evaluate();2        
while (results.moveToNextItem()) {3
    Item result = results.getCurrentItem();

    /*Do something with result.*/
}

1

First compile an XQuery expression using XQuerySession.compileExpression. The argument script contains a XQuery query expression as a string. If no compilation errors (CompilationException) are found, this returns an Expression object.

2

Then evaluate the expression using Expression.evaluate. If no evaluation errors (EvaluationException) are found, this returns the results of the evaluation in the form of an ItemSequence.

3

An ItemSequence allows to iterate over a sequence of Items (see ???). A Item is either an atomic value or an XML Node.

Example (1.xq):

(: Compute and return 2 + 3 :)
2 + 3

evaluates to an ItemSequence containing a single atomic value (5).

Example (3.xq):

(: List all books by their titles. :)
declare namespace t = "http://www.qizx.com/namespace/Tutorial";

collection("../../book_data/Books/*.xml")//t:book/t:title

evaluates to an ItemSequence containing several t:title element Nodes.

The OpenQuery class implements a simple command-line tool allowing to run queries.

Excerpts of OpenQuery.java:

    private static Expression compileExpression(XQuerySession session, 
                                                String script,
                                                QName[] varNames,
                                                String[] varValues) 
        throws IOException, QizxException
    {
        Expression expr;
        try {
            expr = session.compileExpression(script);
        }
        catch (CompilationException e) {
            Message[] messages = e.getMessages();
            for (int i = 0; i < messages.length; ++i) {
                error(messages[i].toString());
            }
            throw e;
        }

        if (varNames != null) {
            for (int i = 0; i < varNames.length; ++i) {
                expr.bindVariable(varNames[i], varValues[i], /*type*/ null);1
            }
        }

        return expr;
    }

1

An XQuery expression can be further parametrized by the use of variables. Example (101.xq):

(: List all books containing the value of variable $searched 
   in their titles. :)
declare namespace t = "http://www.qizx.com/namespace/Tutorial";

declare variable $searched external;

collection("/Books")//t:book/t:title[contains(., $searched)]

Expression.bindVariable allows to give a variable its value, prior to evaluating the expression.

Some queries may return thousands of results. Therefore, displaying just a range of results (e.g from result #100 to result #199 inclusive) is a very common need.

    private static void evaluateExpression(Expression expr, 
                                           int from, int limit) 
        throws QizxException {
        ItemSequence results = expr.evaluate();
        if (from > 0) {
            results.skip(from);1
        }

        XMLSerializer serializer = new XMLSerializer();
        serializer.setIndent(2);

        int count = 0;
        while (results.moveToNextItem()) {
            Item result = results.getCurrentItem();

            System.out.print("[" + (from+1+count) + "] ");
            showResult(serializer, result);
            System.out.println();

            ++count;
            if (count >= limit)2
                break;
        }
        System.out.flush();
    }

1

ItemSequence.skip allows to quickly skip the specified number of Items.

2

This being done, you still need to limit the number of Items you are going to display.

In this lesson, we'll just show how to print the string representation of an Item.

    private static void showResult(XMLSerializer serializer,
                                   Item result) 
        throws QizxException {
        if (!result.isNode()) {1
            System.out.println(result.getString());2
            return;
        }
        Node node = result.getNode();3

        serializer.reset();
        String xmlForm = serializer.serializeToString(node);4
        System.out.println(xmlForm);
    }

1 3

Item.isNode returns true for a Node and false for an atomic value. Similarly, Item.getNode returns a Node when the Item actually is a Node and null when the Item is an atomic value.

2

Item.getString returns the string value of an Item (whether Node or atomic value). What precisely is the string value of an Item is specified in the XQuery standard.

4

The XMLSerializer.serializeToString convenience method is used to obtain the string representation of a Node.

3.1. Compiling and running the code of this lesson

  • Compile class Query by executing ant (see build.xml) in the docs/samples/programming/query/ directory.

  • Run ant run1 in the docs/samples/programming/qizxopen/ directory to perform this query:

    (: Find all books written by French authors. :)
    declare namespace t = "http://www.qizx.com/namespace/Tutorial";
    
    for $a in collection("/Authors")//t:author[@nationality = "France"]
        for $b in collection("/Books")//t:book[.//t:author = $a/t:fullName]
        return 
            $b/t:title

    You can execute all the queries by running ant run_all in docs/samples/programming/qizxopen/.