devxlogo

Introduction to XQuery (Part 4 of 4)

Introduction to XQuery (Part 4 of 4)

his is the final part of the four part series on Xquery. In this final section, you’ll see an explanation of conditional and quantified expressions as well as a quick tutorial on writing your own functions in XQuery.

Writing Conditional Expressions
As you would expect, XQuery provides an if-then-else clause that lets you branch execution based on a condition. The syntax of the if-then-else is:

   if  (  test-expression )  then   result-expression1     else result-expression2

For example, the following query checks whether customer 1001 ordered any item with a price over 200.

   # Query listing -- XQuery41.ixq in samples.zip   let $price :=       document("data/PO.xml")//po[customer/custno=      '1001']/lineitems/lineitem/item/price[. > 200 ]   return   if ( count($price) ) then               customer ordered an expensive item!          else              No expensive item was ordered.       

The variable $price represents a sequence of price items whose value is greater than 200. The if-then-else expression displays one of two different messages depending on the result (true or false) of evaluating the test expression count($price).Writing Quantified Expressions
XQuery quantified expressions support existential (meaning at least one value exists that satisfies the given condition) and universal (all values satisfy the given condition) quantification. The value of a quantified expression is either true or false. There are several types:

Existential Quantification Expressions

The syntax of the existential quantification expression is:

   some $v in seq-expression satisfies test-expression

A quantified expression returns a Boolean true or false by iteratively evaluating the items in seq-expression and returns true as soon as it finds one that meets (satisfies) the criteria in test-expression. If no item meets the criteria, the quantified expression returns false.

For example, the following query checks to see if customer 1001 has ordered any items where price is greater than 200.

   # see XQuery42.ixq in samples.zip   let $price :=      document("data/PO.xml")//po[customer/custno=     '1001']/lineitems/lineitem/item/price   return   if (some $v  in $price satisfies ( $v > 200 ) ) then                customer ordered an expensive item!          else               No expensive item is ordered.       

The existential quantification expression some $v in $price satisfies ($v > 200)) appears as the test condition in the if-then-else expression in the return clause. If the quantification expression returns true, the query result is:

          customer ordered an expensive item!   

Universal Quantification Expressions

The syntax of the universal quantification expression is:

   every  $v   in   seq-expression   satisfies        test-expression

The following query checks if the price of every item customer 1001 orders is over 200.

   # Query listing - XQuery43.ixq in samples.zip   let $price := document("data/PO.xml")      //po[customer/custno='1001']      /lineitems/lineitem/item/price   return   if (every $v  in $price satisfies ( $v > 200 ) ) then               customer always orders expensive items!          else               Customer does not always order expensive items      

Unless customer 1001 always orders items priced over 200, the result is

         Customer does not always order expensive items    

Using XQuery’s Built-in Functions
XQuery provides a set of built-in functions for number (integer, short, float, double), string, Boolean, datetime, Qname, node and sequence data types.

You can augment the built-in function library by defining your own functions.

Here are some of the most useful built-in functions.

Document() function. Function document(string uri) returns the root node of the referenced document. The URI reference format is implementation dependent. The Ipedo XML Database XQuery implementation uses collectionname/documentname as the parameter to the document() function. For example, document(“data/PO.xml”) returns the root node of the document PO.xml in the collection named data in an Ipedo XML Database.

Aggregate functions. XQuery provides count, avg, max, min and sum aggregate functions. Function count returns the number of items in the sequence. Function avg returns the average (mean) of a sequence of numbers. Function sum returns the sum of a sequence of numbers. Function max returns the number with maximum value from a sequence while function min returns the number with minimum value.

For example, the following query calculates the number of items in the purchase order 0002.

   # --see XQuery44.ixq in samples.zip   let $po2 := document("data/PO.xml")//po[@id='0002']   let $items := $po2/lineitems/lineitem/item   return     {count($items) } 

In the preceding XQuery, the variable $po2 represents the purchase order with purchase order id 0002. The variable $items represents all items in the purchase order ?in other words, a sequence of node item. The function count($items) returns the count of item in that sequence. The return clause constructs an element that looks like this:

 2 

As another example, the following query calculates the average unit price of all items in the purchase order 0002.

   # --see XQuery45.ixq in samples.zip   let $po2 := document("data/PO.xml")//po[@id='0002']   let $unitprices := $po2/lineitems/lineitem/item/price   return     {avg($unitprices) } 

The result looks like this:

    105.575 

String functions. XQuery provides the following string functions: concat, startswith, ends-with, contains, substring, string-length, normalize, upper-case, and lower-case.

The function starts-with(str1, str2) returns true if beginning of str1 matches the characters in str2. The function ends-with(str1, str2) returns true if the ending characters in str1 match the characters in str2. The function contains(str1, str2) returns true if the str1 contains str2.

The following query uses the contains() function to find items in the document items.xml whose description contains “Ping Pong”.

   # --see XQuery46.ixq in samples.zip   for  $i in document("data/items.xml")//item   where contains($i/description, "Ping Pong")   return $i

Node function. XQuery’s node-equal, node-before, node-after, copy, and shallow functions act specifically on nodes. The function node-equal provides functionality equivalent to the == and !== operators for nodes. The node-before function works the same way as the << operator. The node-after function backs up the >> operator, and the node-equal() function returns true if two nodes have the same identity; otherwise it returns false.

The copy() function copies a node including its attribute and descendents. Those of you familiar with XSLT?be careful! The XQuery copy() function produces a deep copy?the equivalent of in XSLT. The following examples illustrate the process of copying nodes in XQuery.

   # Query listing - XQuery47.ixq in samples.zip   let $customer :=       document("data/customers.xml")//customer      [custno='1001]   let $customercopy := copy($customer)   return node-equal($customer, $customercopy)

In the preceding example query, the variable $customer represents the customer element with customer number 1001. The variable $customercopy will hold a copy of that customer node. The expression copy($customer) creates a node that is a deep copy of the value of variable $customer but gives it a different node identity, so node-equal($customer, $customercopy) returns false.

In contrast, the shallow() function copies a node and its attributes, but not its descendents.

   # Query listing - XQuery48.ixq in samples.zip   let $customer := document("data/customers.xml")      //customer[custno='1001']   let $customercopy := shallow($customer)   return  {$customercopy } 

Sequence functions. XQuery provides the sequence functions item-at, index-of, empty, exists, union, intersect, and except. The union function backs up the union and the “|” operator. The intersect function backs up the intersect operator (the intersect operator is the keyword “intersect”). The except function backs up the except operator.

The exists function tests whether a sequence is an empty sequence (contains no nodes). The function item-at returns the item located at the specified index within a sequence. The index is 1-based (not 0-based).

The following query uses the empty() function to check whether customer 1001 ordered any item with a price over 300.

   # Query listing - XQuery49.ixq in samples.zip   let $v := document("data/PO.xml")//po[customer/custno=      '1001']/lineitems/lineitem/item/price[. > '300']   return empty($v)

The query returns true because customer 1001 hasn’t ordered any such item.User-Defined Functions
You can augment the built-in function library by defining your own functions. A user-defined function begins with a define function clause followed by the body of the function enclosed in curly braces ({}). Here’s a simple function example:

   define function greetings() returns element {      Hello World!!   }

The define function clause defines the signature of the function? the name of the function, the number and types of parameters, and the return value type. In the preceding example, the greetings() function takes no parameters, but it returns an element named greetings. Here’s the same function defined to accept a parameter:

   define function greetings(xsd:string $message)       returns element {      {$message}   }

Now the function takes a string parameter and returns a greetings element with the parameter value as its content.

Function signatures
A function signature describes the interface to a function. There are two important components to a function signature, the input parameter list and the return type. The input parameter list is a comma-separated list of formal parameters that the function will accept. Each parameter is bound to a variable that can be used within the function body.

The XQuery specification is a work in progress, but almost all the features described in this series are stable, and you can expect them to appear unchanged in the finished specification.

Here are a few sample function signatures:

   define function trim(xsd:string $str)      returns xsd:string {...}

The ellipsis (…) represents the body of the function (not shown in the example). Even without the function body, you can tell from the signature that the function accepts a string parameter and returns a string as output. The function assumes that the prefix xsd is mapped to the XML schema namespace using the namespace clause.

   define function xyz($p) {...}

Notice that this function signature is missing the data type of the parameter and return values, meaning the parameter and return value can be any type.

   define function formatItem(element item $item)       returns element {...}

This function accepts only elements named item. Passing any other parameter type or any element type not named item causes an error.

   define function formatItem(element of type ix:itemType       $item) returns element {...}

This function accepts only items of the type itemType in the target namespace mapped by the prefix ix, and shows that XQuery supports strongly-typed functions that use custom types.

Function body
The function body is enclosed in curly braces ({}). It can be arbitrarily complex in the sense that it can include any of the different types of expressions discussed in this series, but it must return a value of the type declared in the function signature.

Pointers to the future
This series was intended to give you a quick, yet reasonably comprehensive introduction to XQuery. Hopefully, you’ve seen enough to get started writing queries in this simple yet powerful query language. The XQuery specification is a work in progress, but almost all the features described in this series are stable, and you can expect them to appear unchanged in the finished specification. In the future, features such as update syntax, a well-defined type system etc., will be added to the query language. As more applications choose to maintain their data in XML, XQuery may well become the SQL of the future.

devxblackblue

About Our Editorial Process

At DevX, we’re dedicated to tech entrepreneurship. Our team closely follows industry shifts, new products, AI breakthroughs, technology trends, and funding announcements. Articles undergo thorough editing to ensure accuracy and clarity, reflecting DevX’s style and supporting entrepreneurs in the tech sphere.

See our full editorial policy.

About Our Journalist