XQuery/Navigating Collections

Motivation edit

You want to browse collections using an HTML web page and narrow your choices as you type.

Method edit

We will first create a server-side script that takes a single parameter. This is the collection path that the user is entering into an input field in a web page. With each character the user types the list of possible sub-collections is narrowed.

There are three parts to this script: 1) the server side XQuery script 2) the HTML form 3) the JavaScript file that implements the JavaScript with AJAX functions.

Sample Server-Side Script edit

get-child-collections.xq edit

xquery version "1.0";
 
declare function local:substring-before-last-slash($arg as xs:string?)  as xs:string {
if (matches($arg, '/'))
   then replace($arg,'^(.*)/.*','$1')   (: by default matching is eager :)
   else ''
 };


(: if we don't get any value then use the root collection :)
let $collection := request:get-parameter("collection", '')
let $parent := local:substring-before-last-slash($collection)
let $leaf := substring-after($collection, concat($parent, '/'))

let $sub-collections := xmldb:get-child-collections($parent)


return
<div class="results">{
   if (count($sub-collections) = 0)
     then 
        <h1>There are no subcollections of {$collection}</h1>
     else
      <div class="selections">{
         for $child in $sub-collections
         let $child-path := concat($parent, '/', $child)
         order by $child
         return
            if (starts-with($child, $leaf))
              then
                 <div class="selection"><a href="browse-collection.xq?collection={$child-path}/">{$child}</a></div>
            else ()
       }</div>
}</div>

browse-collection.xq edit

xquery version "1.0";
declare option exist:serialize "method=xhtml media-type=text/html omit-xml-declaration=no indent=yes 
        doctype-public=-//W3C//DTD&#160;XHTML&#160;1.0&#160;Transitional//EN
        doctype-system=http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd";

let $title := "Browse Collections (AJAX)"

let $collection := request:get-parameter("collection", '/db/')

return
<html xmlns="http://www.w3.org/1999/xhtml" >
    <head>
        <title>{$title}</title>
        <script type="text/javascript" src="ajax-collection.js"/>
        <style type="text/css">
            td {{background-color: #efe; font-size:14px;}}
            th {{background-color: #ded; text-align: right; padding:3px; font-size:12px;}}
        </style> 
    </head>
    <body onload="getList();">
       <h1>{$title}</h1>
       <p>{$collection}</p>
       <form onsubmit="getList(); return false" action="get">
            <span>
                <label for="collection">Collection:</label> 
                <input type="text" size="50" name="collection" id="collection" title="collection"
                    onkeyup="getList();" onfocus="getList();" value="{$collection}"/>
            </span>
        </form>
        <!-- this is where the results are placed -->
        <div id="results"/>
    </body>
</html>

ajax-collection.js edit

function updateList() {
  if (http.readyState == 4) {
      var divlist = document.getElementById('results');
      divlist.innerHTML = http.responseText;
      isWorking = false;
  }
}
 
function getList() {
  if (!isWorking && http) {
    var collectionid = document.getElementById("collection").value;
    http.open("GET", "get-child-collections.xq?collection=" + collectionid);
    http.onreadystatechange = updateList;  
          // this sets the call-back function to be invoked when a response from the HTTP request is returned
    isWorking = true;
    http.send(null);
  }
}
 
function getHTTPObject() {
  var xmlhttp;
  /*@cc_on
  @if (@_jscript_version >= 5)
    try {
      xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
    } catch (e) {
      try {
        xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
      } catch (E) {
        xmlhttp = false;
      }
    }
  @else
  xmlhttp = false;
  @end @*/
  if (!xmlhttp && typeof XMLHttpRequest != 'undefined') {
    try {
      xmlhttp = new XMLHttpRequest();
      xmlhttp.overrideMimeType("text/xml"); 
    } catch (e) {
      xmlhttp = false;
    }
  }
  return xmlhttp;
}
 
var http = getHTTPObject(); //  create the HTTP Object
var isWorking = false;