XQuery/Incremental Search of the Chemical Elements

From Wikibooks, the open-content textbooks collection

< XQuery
Jump to: navigation, search

In this example of AJAX in its AHAH form, an incremental search of the chemical elements is implemented. This is also an example of the use of the eXist free text index and matching with a limited regular expression.

The raw data is taken from a file [1]provided by Elliotte Rusty Harold.

Search the Elements

Contents

[edit] The main page

The main page is a simple HTML file. The div element with id list is where the generated contents will be placed. The JavaScript function getList() is called when any of several interface events occur.

<html xmlns="http://www.w3.org/1999/xhtml" >
   <head>
     <title>Chemical Element Data</title>
      <script language="javascript" src="ajaxelement.js"/>
   </head>
  <body>
    <h1>Chemical Element Data</h1>
    <p> Data provided by  
          <a href="http://www.cafeconleche.org/examples/periodic_table/allelements.xml"> Elliotte Rusty Harold </a>
    </p>
    <form onSubmit="getList(); return false">
       <span><label for="name">Element Name </label> 
             <input type="text" 
                    size="5"
                    name="name" 
                    id="name" 
                    title="e.g. Silver"
                    onkeyup="getList();" 
                    onfocus="getList();" 
             />
      </span>
   </form>   
    <div id="list"/>
    </body>
</html>

[edit] The JavaScript

The JavaScript implements the simple functionality of calling the server-side script getElement.xq with the string entered in the search box, and in the callback, pasting this value into the div.

function updateList() {
  if (http.readyState == 4) {
      var divlist = document.getElementById('list');
      divlist.innerHTML = http.responseText;
      isWorking = false;
  }
}
 
function getList() {
  if (!isWorking && http) {
    var name = document.getElementById("name").value;
    http.open("GET", "getElement.xq?name=" + name);
    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;

[edit] The server-side search

This script is called by the getlist() function when a partial atom name has been entered. The string is converted to a simple regular expression and used in the eXist free text matching function to retrieve matching atoms. The response depends on the number of matches found:

  • if only one match, return a table of details
  • if more than one match, return with a list of matches
  • if none return "no matches"

A generic function turns the elements of an XML node into rows in a table, using the element name as the legend. In the XML file, element names are uppercase, so a function is used to turn these into more readable mixed case roughly title-cased labels.

declare function local:string-to-title ( $string as xs:string?)  as xs:string {
        string-join(
            for $w in tokenize(lower-case($string),"_")
            return            
                    if ($w = ("of","a","in","on","the"))
                    then $w
                    else  
                         concat ( upper-case(substring($w,1,1)),
                                  substring($w,2)
                                 )
         ," ")                                   
} ;
 
declare function local:element-to-table($element) as element(table) {
   <table border="1">
      {for $node in $element/*
       let $label := local:string-to-title(name($node))   
       return 
            <tr>
                 <td>{$label}</td> 
                 <td>
                   { $node/text()   }
                   </td>
             </tr>       
        }
    </table>
 };
 
let $name := request:get-parameter("name",())
return
  if ($name != "")
  then
    (: convert the partial string to a regexp so that it can be used in the eXist free text match  &amp;= :)
      let $search := concat('^',$name,'*')
      let $matches :=  /PERIODIC_TABLE/ATOM[NAME &amp;= $search]
      return 
         if (count($matches) = 0)
         then 
            <span>No matches</span>
         else 
         if (count($matches) =1)
         then 
             local:element-to-table($matches)
         else 
         (: multiple matches :)
            <div>
               {for $match in $matches
                order by $match/NAME
                return 
                 <div>
                   {concat($match/NAME, " [", $match/SYMBOL, "],  atomic weight ",$match/ATOMIC_WEIGHT)}
                 </div>
              }
            </div>
  else ()

[edit] Discussion

Some naming problems here - needs tidying. Units need to be included