Queries

In this chapter we implement a server extension function that compares two date or dateTime values. As we have seen in From Conceptual Model to Schema::Constraints across documents, the comparison of values of these datatypes using XPath's native facilities is awkward; anything that facilitates this is beneficial. Note that this is only a deficiency of XPath 1.0; in contrast, both XPath 2.0 and XQuery support XML Schema datatypes, including date and time formats. The module concept of XQuery offers a powerful mechanism for managing user-defined functions.

In addition, we implement a function that returns a dateTime string of the current time of day. (The complete source code including an Install.xml file and Javadoc HTML file is contained in the directory sxsjxsd in the documentation set.)

We implement these functions in Java, and because we are lazy we implement them by using Java's sophisticated calendar support. After all, object-oriented programming is about re-use.

We start the Tamino X-Tension Builder and create a new server extension which we call xsd. We call the package tamino.SXS.xsd and the class simply xsd. The tool will generate a code framework for a Tamino server extension.

We can now add our new query functions as new methods into this framework. To do so, we use the menu function Add Function:

  • The first function we add is called dtComp(). We define it as a query function. The tool asks us for the type of the result, which we set to integer. Then we add two operands, op1 and op2, of type charstr.

  • The second function is called current(). Again, we define it as a query function and set the result type to charstr. There are no operands.

The X-Tension Builder generates the required method code frames into the class code. We can now implement our custom logic (emphasized):

// xsd.java: Implementation of Server Extension xsd
//
// Tamino Server Extension xsd
//
// $javadoc:on

package tamino.SXS.xsd;
import com.softwareag.ino.sxs.*;
import java.text.SimpleDateFormat;
import java.util.Date; 

// Javadoc comments.
// TODO: Add more detailed description if necessary
/**
 * Tamino Server Extension xsd
 * @author My name
 * @version 1.0
 */

public class xsd extends ASXJBase {
        // Version information for current Server Extension xsd.
        // TODO: Change SXS Version here if necessary:
        static final SXSVersion sxsVersion = new SXSVersion (1, 0);

        // Description of current Server Extension:
        // TODO: Change this string if necessary
        static final String sxsAbout = "Tamino Server Extension xsd";

        // TODO: Enter further class variables here.

        /**
         * The default constructor
         * (No other constructor allowed.)
         */
        public xsd () {
                // TODO: enter SXS initialization here.
        }

        // Description of Server Extension Function dtcomp
        // TODO: Change description if necessary
        static final String dtcompAbout = "Query Function dtcomp";

       /**
         * comparison of XSD date/time  
         * @param op1 parameter (XML Schema date, dateTime, time)
         * @param op2 parameter (XML Schema date, dateTime, time)
         * @return comparison result: 
         *         -1 (op1 < op2), 0 (op1 = op2), 1 (op1 > op2)
         */
        public int dtcomp (String op1, String op2)
                         throws java.text.ParseException
        {
           // To delete dtcomp, remove it here and from Install.xml.
          Date d1 = toDate(op1);
          Date d2 = toDate(op2);
          return d1.compareTo(d2);

        }

        // Description of Server Extension Function current
        // TODO: Change description if necessary
        static final String currentAbout = "Query Function current";

        public String current ()
        {
            // To delete current, remove it here and from Install.xml.
           SimpleDateFormat df2 = 
           new SimpleDateFormat ( "yyyy-MM-dd'T'HH:mm:ss" );
           return df2.format(new Date());

        }

       /**
          * Convert lexical XML Schema date/time representation 
          * to Java Date format
          * @param s input string
          * @return Date value
          */ 
       private Date toDate(String s) 
                     throws java.text.ParseException
       { 
          // determine formatting string
          String f = (s.indexOf("T",0) >= 0 ? 
                       "yyyy-MM-dd'T'HH:mm:ss" : 
                       (s.indexOf(":", 1) >= 0 ? 
                                 "HH:mm:ss" : "yyyy-MM-dd"));
          // check for explicit time zone
          int p = Math.max (s.indexOf("+", 8),s.indexOf("-", 8));
          if (p >= 0) {
            f += "z"; 
          // indicate time zone in formatting string
            s = s.substring(0,p-1)+"GMT"+s.substring(p);  
                // keep SimpleDateFormat happy
          }
          else if (s.charAt(s.length()-1) == 'Z') {
                             // check for UTC time zone
            f += "z";
            s = s.substring(0,s.length()-1) + "UTC";
          }
          // create SimpleDateFormat object
          SimpleDateFormat df = new SimpleDateFormat ( f );
          // and use it as a parser
          return df.parse(s);
       } 

}

Tip:
A good way of developing such a server function is first to create and debug it in your preferred Java development environment, and then copy the code into the X-Tension Builder.

Along with the Java code, the X-Tension Builder has created a control file, called Install.xml. Remember that Tamino server extensions can be written in a variety of programming languages. This file describes the specific extension module in a generic, language-neutral format. It also specifies the datatypes of input and output parameters in terms of server extensions. The following table shows how Java datatypes relate to server extension specific types:

server extension type

Java input and result parameters

Java output and in/out parameters

ino:XML-OBJ (i.e. node list)

String StringBuffer

xs:string

String StringBuffer

xs:boolean

boolean BooleanRef

xs:int

int IntRef

xs:double

double DoubleRef

xs:float

float  

And this is how an Install.xml file looks:

<?xml version="1.0" encoding="ISO-8859-1"?>
<ino:Administration
    xmlns:ino="http://namespaces.softwareag.com/tamino/response2"
    xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <ino:Object
        Name="xsd"
        Id="tamino/SXS/xsd/xsd"
        Infrastructure="Java">
        <ino:About
            Author="My name"
            CreationDate="2008-08-14"
        />
        <ino:Function
            Name="dtcomp"
            Usage="Query">
            <ino:Parameter
                Name="op1"
                TSDType="xs:string"
            />
            <ino:Parameter
                Name="op2"
                TSDType="xs:string"
            />
            <ino:Parameter
                Name="result"
                TSDType="xs:int"
            />
            <ino:About>Query Function dtcomp</ino:About>
        </ino:Function>
        <ino:Function
            Name="current"
            Usage="Query">
            <ino:Parameter
                Name="result"
                TSDType="xs:string"
            />
            <ino:About>Query Function current</ino:About>
        </ino:Function>
    </ino:Object>
</ino:Administration>

As you can see, this file describes all the functions implemented in that particular server extension module, together with their respective parameters. This file must be modified manually if we subsequently decide to change the name of any function, the name and number of parameters, or their type.

After compiling, we pack this class into an .sxp file using the Pack function of the X-Tension Builder. With the help of the Tamino Manager we can now install the new server extension into our jazz database. Thereafter, we can use the new functions within queries, for example:

jazzMusician[xsd.dtComp(birthDate,"1920-01-01") >= 0]

which returns all jazz musicians born in 1920 or later;

jazzMusician[xsd.dtComp(birthDate,xsd.current()) > 0]

which should result in an empty set of documents.

In XQuery 4 we can similarly write:

for $j in input()/jazzMusician[xsd.dtComp(birthDate,"1920-01-01") >= 0]
  return $j

and:

for $j in input()/jazzMusician[xsd.dtComp(birthDate,xsd.current()) > 0]
  return $j

The first case can be expressed solely with the means of XQuery 4 because XQuery 4 supports XML Schema datatypes. We could write:

declare namespace xs="http://www.w3.org/2001/XMLSchema"
for $j in input()/jazzMusician[birthDate >= xs:date("1920-01-01")]
  return $j

The second case, however, still requires a server extension to fetch the current date.

Note that search criteria that involve server extension functions are always processed in the post-processing phase of a query (see From Schema to Tamino::Efficient Queries). So, even if birthDate is defined as an index, this search criterion still results in a scan through all documents.

Tip:
An alternative way to develop Tamino Server Extensions is to use the X-Tension Object Analyzer. This tool can be invoked from the Tamino Manager. Using this method, we would first develop the necessary Java classes using an IDE of our choice. Then, we can load the executable (.class or .jar file) into the X-Tension Object Analyzer. Now we can edit the details of the imported executable, such as the server extension name, description, author, and help files. The Pack Object function creates a server extension package file that can be installed in a Tamino database. This is a map-out function with the onCompose property defined.