XQuery and XSLT Reference Guide (PDF)

XQuery and XSLT Reference Guide — Chapter 3

« Previous chapter
Next chapter »

MarkLogic Server Enhanced XQuery Language

The default XQuery dialect in MarkLogic Server is enhanced. (1.0-ml) The enhanced dialect includes all of the features in the strict XQuery 1.0 dialect, and adds several other features to make it easier to use XQuery as a programming language with which to create applications. This chapter describes the features of the enhanced dialect and includes the following sections:

For details on the XQuery language, see XQuery Language and the W3C XQuery specification (http://www.w3.org/TR/xquery/).

try/catch Expression

The try/catch extension allows you to catch and handle exceptions. MarkLogic Server exceptions are thrown in XML format, and you can apply an XPath statement to the exception if there is a particular part you want to extract. The exception is bound to the variable in the catch clause.

The following code sample uses a try/catch block to catch exceptions upon loading a document, and prints out the filename if an exception occurs.

try {
     let $filename := "/space/myfile.xml"
     let $options := <options xmlns="xdmp:document-load">
                         <uri>/myfile.xml</uri>
                         <repair>none</repair>
                      </options>
     return xdmp:document-load($filename, $options)
     }
catch ($exception) {
"Problem loading file, received the following exception: ",
$exception }

Most exceptions can be caught with a try/catch block, but the XDMP-CANCELED, SVC-CANCELED, and XDMP-DISABLED exceptions cannot be caught in a try/catch block.

When an exception is thrown by code within a try block, all actions taken in that block are rolled back. If you catch the exception (and do not throw another), then MarkLogic will evaluate expressions occurring after the try-catch expression.

For example, in the following code, the call to xdmp:document-set-metadata data throws an XDMP-CONFLICTINGUPDATES exception because it tries to update the document metadata twice in the same statement. The exception is trapped by the try-catch. The updates in the try block are lost, so doc.xml is not created. The hello expression is still evaluated.

xquery version "1.0-ml";
try {
  xdmp:document-insert('doc.xml', 
    <data/>, 
    map:map() => map:with("metadata", 
                          map:map() => map:with("a", 1) 
                                    => map:with("b",2))
  ),
  xdmp:document-set-metadata('doc.xml', map:map() => map:with("c", 3))
} catch($err) { },
"hello"

(: doc.xml is not inserted; query emits "hello"

By contrast, if you wrap only the call to xdmp:document-set-metadata in the try-catch block, then the initial document insert still occurs.

xquery version "1.0-ml";
xdmp:document-insert('doc.xml', 
  <data/>, 
  map:map() => map:with("metadata", 
                        map:map() => map:with("m", 1) 
                                  => map:with("n",2))
),
try {
  xdmp:document-set-metadata('doc.xml', map:map() => map:with("b", 1))
} catch($err) { },
"hello"

(: doc.xml is inserted with m & n metadata keys; query emits "hello" :)

Note that Server-Side JavaScript code does not handle JavaScript statements within a try block the same way as XQuery handles expressions in a try block. In JavaScript, statements in the try block that complete before the exception occurs are not rolled back if the exception is caught. For details, see Exception Handling in the JavaScript Reference Guide.

Function Mapping

Function mapping is an extension to XQuery that allows you to pass a sequence to a function parameter that is typed to take a singleton item, and it will invoke that function once for each item in the sequence. This section describes function mapping and includes the following parts:

Understanding Function Mapping

Function mapping is equivalent to iterating over the sequence like it was in a for clause of a FLWOR expression. The following is an example of function mapping:

xquery version "1.0-ml";

declare function local:print-word ($word as xs:string) { $word };

local:print-word( ("hello", "world") )
(: 
   evaluates the print-word function twice, once for "hello" 
   and once for "world", returning hello world 
:)

Function mapping also works on multiple singleton parameters, resulting in the cross product of all the values (equivalent to nested for clauses). In the case of multiple mappings, they occur left to right. For example, the following is evaluated like a nested for loop:

xquery version "1.0-ml";
(1 to 2) * (3 to 4)
(: returns the sequence (3, 4, 6, 8) :) 

One consequence of function mapping, which can be surprising the first time you see it, is that if the value passed for a parameter is the empty sequence, it could result in the function being called 0 times (that is, in the function never runs and results in the empty sequence. For example, if you entered the empty sequence as the parameter to the above function call, it returns empty, as follows:

xquery version "1.0-ml";

declare function local:print-word ($word as xs:string) { $word };

local:print-word( () )
(: 
   evaluates the print-word function zero times, resulting 
   in the empty sequence
:)

The local:print-word function is never called in this case, because it is iterating over the empty sequence, which causes zero invocations of the function. If your function calls are fed by code that can return the empty sequence (an XPath expression, for example), then you might see this behavior.

Enabling or Disabling Function Mapping

In 1.0-ml, function mapping is enabled by default. In 1.0, it is disabled by default. You can enable it in 1.0 by adding the following to the XQuery prolog:

declare namespace xdmp="http://marklogic.com/xdmp";
declare option xdmp:mapping "true";

Similarly, you can explicitly disable function mapping in 1.0-ml by adding the following to the prolog:

declare option xdmp:mapping "false";

If you run code expecting it to map singletons to a sequence if function mapping is disabled, it will throw an exception because the sequence cannot be cast to a single string.

If you use item operators on a sequence, in the 1.0-ml dialect they will perform function mapping, and the value will be the effective boolean value of the sequence of results. In the 1.0 dialect, they will throw an XDMP-MANYITEMSEQ exception if you try to compare a sequence of more than one item. For more details, see Item Operators.

Semi-Colon as Transaction Separator

In the enhanced dialect, you can add a semi-colon after one or more XQuery statements in the body of a main module and then add another one or more XQuery statement. The two sets of statements are then evaluated as two separate transactions. Each set of statements must be a main module; that is, they must all have their own prolog elements. All of the statements in the program must use the same XQuery dialect. For example, the following creates a document and then returns the contents of the document:

xquery version "1.0-ml";
xdmp:document-insert("/mydocs/sample.xml", 
            <some-element>content</some-element>) ;
xquery version "1.0-ml";
(: Note that the XQuery version must be the same for all 
   statements in the module :)
fn:doc("/mydocs/sample.xml")
(: returns the document created in the previous statement :)

Note that you cannot use the semi-colon as a transaction separator in the strict XQuery dialect (1.0). For more details on transactions, see Understanding Transactions in MarkLogic Server chapter in the Application Developer's Guide.

Private Function and Variable Definitions

In the 1.0-ml enhanced dialect, you can create library modules with functions and variables that are private to the library module. Private functions and variables are useful when you have certain code you do not want to expose to users of the library, but might be useful for functions for the library to use. To make functions and variables private, add private to the function or variable declaration syntax as follows:

declare private function ....
declare private variable ....

Note that functions and variables in a main module are private by definition, so declaring them private only makes sense for library modules.

Functions With Side Effects

The XQuery specification defines that XQuery programs produce only their return values, without producing any side effects; that is, without causing any changes to the run-time environment as a result of running the program (with the exception of fn:trace). MarkLogic Server has many enhancements that cause side effects. For example, there are functions that insert or update documents in a database. Since functions like the ones that update documents do more than functions that simply return values, they are extensions to the XQuery specification.

Side effects are extremely useful when building applications. Therefore, MarkLogic Server includes many functions that have side effects. The following are some examples of functions with side effects:

Shorthand Positional Predicate Syntax

MarkLogic Server enhanced mode supports the shorthand version of the positional predicate syntax, where you can specify the position numbers to include. For example, the following specifies the first three items in the sequence:

xquery version "1.0-ml";
(1, 2, 3, 4, 5, 5)[1 to 3]

In XQuery 1.0 strict mode (1.0), you must use the fn:position() function as in the following example:

xquery version "1.0";
(1, 2, 3, 4, 5, 5)[fn:position() = (1 to 3)]

Binary Node Constructor and Node Test

MarkLogic Server enhanced mode extends the XQuery types to include a binary node type. Binary nodes are used to store binary documents. To support this type, the MarkLogic Server enhanced XQuery dialect includes a node constructor (binary) to construct a binary node and a node test (binary()) to test whether a node is a binary node (for example, in a typeswitch expression). These extensions are not available in the 1.0 dialect.

validate as Expression

In the 1.0-ml dialect, you can use the validate as syntax to specify the type for a validate expression. The validate as expression is an extension to the XQuery 1.0 validate expression, and it is only available in 1.0-ml; it is not available in the 1.0 dialect. For details on the validate expression, see Validate Expression.

Serialization Options

You can set the serialization options in XQuery with the declare option XQuery prolog. In XSLT, you can set the serialization options using the <xsl:output> instruction. For details on setting the serialization options in XQuery, see Declaring Options. For XSLT output details, see the XSLT specification (http://www.w3.org/TR/xslt#output).

Importing a Stylesheet Into an XQuery Module

Using the 1.0-ml dialect, you can import a XSLT stylesheet into an XQuery module, allowing you access to the functions and variables defined by that stylesheet. To import a stylesheet in XQuery, use a prolog expression of the following form:

import stylesheet at "/path-to-stylesheet.xsl";

The following example shows an XQuery module that imports a stylesheet and runs a function in the stylesheet:

xquery version "1.0-ml";

(: assumes a stylesheet at /f.xsl with the following contents:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
   version="2.0" 	xmlns:foo="foo">
	<xsl:function name="foo:foo">foo</xsl:function>
</xsl:stylesheet>
:)

import stylesheet at "/f.xsl";
declare namespace foo="foo";

foo:foo()

(: Returns the string:
foo 
which is the output of the 
stylesheet function. :)

Similarly, you can import an XQuery module into an XSLT stylesheet, as described in Importing XQuery Function Libraries to a Stylesheet.

To use functions and variables from a stylesheet in XQuery, define them in a namespace in the stylesheet. In XQuery, it is difficult to call functions and variables in no namespace. Therefore, the best practice is, for functions and variables in a stylesheet that you plan to import into an XQuery module, define them in a namespace. Note that in an XQuery library module, all function and variable declarations must be in a namespace.

XQuery 3.x Features

MarkLogic supports the following subset of language features from XQuery 3.0 and XQuery 3.1. MarkLogic does not support the entire XQuery 3.0 or 3.1 standard. Unless otherwise noted, MarkLogic implements the same semantics for these features as described by the XQuery specification.

Arrow Operator

The arrow operator (=>) applies a function to the value of an expression. For example, you can use the arrow operator to chain together calls to map:with while initializing a map:map:

map:map() => map:with($key1, $value1)
          => map:with($key2, $value2)

In the above example, the map produced by calling map:map or map:with is implicitly the first param of the applied function (map:with, here).

Without the arrow operator, you would need to make repeated calls to map:put or repeated or nested calls to map:with, as shown below. The use of the arrow operator can result in more readable code.

(: using map:put :)
let $map := map:map()
let $_ := map:put($map, $key1, $value1)
let $_ := map:put($map, $key2, $value2)
return $map

(: using map:with :)
map:with(map:with(map:map(), $key1, $value1), $key2, $value2)

For more details, see the discussion of the arrow operator in the XQuery 3.1 specification at https://www.w3.org/TR/2017/REC-xquery-31-20170321/.

Simple Map Operator

The simple map operator (!) is used in expressions of the following form:

PathExpr1 ! PathExpr2

PathExpr1 is evaluated, and then each item in the resulting sequence acts as the inner focus when evaluating PathExpr2.

The following example finds all the //child elements of $nodes, and then uses each element as the context item (.) in a call to fn:concat.

xquery version "1.0-ml";
let $nodes := (
  <parent><child>a</child></parent>,
  <parent><child>b</child></parent>,
  <parent><child>c</child></parent>
)
return $nodes//child ! fn:concat("pfx-", .)

(: result: ("pfx-a", "pfx-b", "pfx-c") :)

For more details, see the discussion of the Simple Map Operator in the XQuery 3.0 specification at https://www.w3.org/TR/xquery-30/#id-map-operator.

String Concatenation Operator

The string concatenation operator (||) enables you to concatenate two strings, as if by calling fn:concat. For example:

"green eggs" || " and " || "ham"

(: result: "green eggs and ham" :)

For more details, see the discussion of String Concatenation Expressions in the XQuery 3.0 specification at https://www.w3.org/TR/xquery-30/#id-string-concat-expr.

URI Qualified Names

A URI Qualified Name enables you to specify a namespace URI literal along with a local name, instead of pre-defining a namespace prefix. You can use a URI qualified name anywhere you can use a lexical QName.

A qualified URI name has the form:

Q{namespaceURI}local_name

For example, the element <x:p/> in the following node can be referenced as Q{http://example.com/ns/foo}p.

xquery version "1.0-ml";
let $node :=
  <doc xmlns:x="http://example.com/ns/foo">
    <x:p/>
  </doc>
return
$node//Q{http://example.com/ns/foo}p

For more details, see the discussion of Expanded QNames in the XQuery 3.0 specification at https://www.w3.org/TR/xquery-30/#dt-expanded-qname.

Dynamic Function Invocation

This feature enables you to invoke a function through a function reference. For example:

xquery version "1.0-ml";
let $ref := fn:concat#2
return $ref("a","b")

(: returns "ab" :)

For more details, see the discussion of dynamic function calls in the XQuery 3.0 specification at https://www.w3.org/TR/xquery-30/#id-dynamic-function-invocation.

Inline Functions

Inline functions are defined in the place where you use them, rather than being separately declared. You can declare a function inline anywhere you can supply a function reference.

The following example passes an inline function as the first parameter of fn:map:

fn:map(function($n) {$n + $n}, (10, 20))

(: returns (20,40) :)

For more details, see the discussion of Inline Function Expressions in the XQuery 3.0 specification at https://www.w3.org/TR/xquery-30/#dt-inline-func.

Function Type Testing

You can use the typed function test feature to test that an expression is a function reference with a specific signature. The signature you test against can include parameter types and return type.

For more details, see the Function Test in the XQuery 3.0 specification at https://www.w3.org/TR/xquery-30/#id-function-test.

Named Function References

You can create a reference to a named function defined in the static context of a query. This feature enables you to create references to known functions, including distinguishing implementations that accept a different number of parameters.

For example, the following code creates a reference to the version of the function local:doSomething that accept two parameters, and then invokes the function through the reference:

xquery version "1.0-ml";
declare function local:doSomething(
  $a as xs:int, $b as xs:int
) as xs:int 
{  $a + $b };

declare function local:doSomething(
  $a as xs:int, $b as xs:int, $c as xs:int
) as xs:int 
{ $a * $b * $c };


let $ref := local:doSomething#2
return $ref(2,3)

You can also create references to functions defined by XQuery and MarkLogic. For example: fn:concat#3 signifies a reference to fn:concat expecting 3 parameters.

For more details, see the following topic in the XQuery 3.0 specification at https://www.w3.org/TR/xquery-30/#id-named-function-ref.

Partial Function Application

When creating a function reference, you can fill in specific values for some parameters and use a placeholder for others. When you make a function call using the reference, you pass in values only for the placeholder parameters. The other parameters use the explicit values previously bound to the reference.

For example, the following function reference specifies the value 10 for the first parameter of the referenced function, and uses a placeholder for the second parameter:

let $fref := local:doSomething(10, ?)

You can invoke the function through the reference and supply only one parameter, which will take the place of the placeholder parameter. For example:

xquery version "1.0-ml";
declare function local:doSomething(
  $a as xs:int, $b as xs:int
) as xs:int 
{  $a + $b };

let $ref := local:doSomething(10,?)
return $ref(3)

(: returns 13 :)

For more details, see the discussion of partial function application in the XQuery 3.0 specification at https://www.w3.org/TR/xquery-30/.

Function Annotations

A function annotation declares a property of a function. For example, XQuery defines the annotations %public and %private for indicating the visibility of a function outside of a module, such as in the following code snippet:

declare %private my:func($p as xs:int) ...

MarkLogic supports the annotations defined by the XQuery 3.0 specification. MarkLogic also defines the following implementation-specific annotations:

For more details, see the discussion of annotations in the Function Declaration topic of the XQuery 3.0 specification at https://www.w3.org/TR/xquery-30/#FunctionDeclns.

Default Values for External Variables

When you declare an external variable, you can includes a default value in the declaration. If the dynamic context during query evaluation does not include a value for the variable, the default is used.

The following example defines an external variable with the default value "my default value".

declare variable $exv as xs:string := "my default value";

For more details, see VarDefaultValue in the Variable Declaration topic of the XQuery 3.0 specification at https://www.w3.org/TR/xquery-30/#id-variable-declarations.

Unions in Typeswitch Case Descriptors

You can use a union of types in the case clause of a typeswitch statement instead of a single type. The case matches if any of the types in the union match. Use the union operator (|) to separate the types in the clause.

For example, the case clause in the following typeswitch matches either a name or address element.

typeswitch($some-node) 
  case $n as element(name) | element(address) return $n
  default return ()

For more details, see the discussion of SequenceTypeUnion in the Typeswitch topic of the XQuery 3.0 specification at https://www.w3.org/TR/xquery-30/#doc-xquery30-CaseClause.

Switch Statement

A switch statement enables you to choose one of several expressions to evaluate based on value. By contrast, a typeswitch enable you to choose one of several expressions based on type.

For example, the following code selects a code path based on the value of a variable.

xquery version "1.0-ml";
let $some-value := 2
return switch($some-value)
  case 1 return "one"
  case 2 return "two"
  default return "many"

(: returns "two" :)

You can use a switch statement that tests the value fn:true() as a shortcut for a nested set of if-then-else expressions. For example:

switch (fn:true)
  case ($a > 0) return "positive"
  case ($a < 0) return "negative"
  default return "zero"

For more details, see the Switch Expression topic in the XQuery 3.0 specification at https://www.w3.org/TR/xquery-30/#id-switch.

Validate Type Expressions

You can validate a node against in-scope schema definitions using the validate operator. You can specify a validation level (strict or lax) or a specific schema type. This feature is similar to calling xdmp:validate, except that it raises an error on the first validation failure, rather than returning a sequence of xdmp:validation-error elements.

The following example specifies a validation level. If you omit the level, strict is implied.

(: validate a structured query :)
xquery version "1.0-ml";
let $query := 
<query xmlns="http://marklogic.com/appservices/search">
  <word-query>
    <element name="body-color" ns="" />
    <text>black</text>
  </word-query>
</query>
return validate strict { $query }

The following example specifies a type instead:

validate type my:type { $some-node }

For more details, see the discussion of Validate Expressions in the XQuery 3.0 specification at https://www.w3.org/TR/xquery-30/#id-validate.

Error Handling with Try/Catch

A try/catch expression enables you to trap and handle errors. MarkLogic also supports a proprietary try/catch implementation, as described in try/catch Expression. You can use either form.

The MarkLogic-specific and XQuery standard try/catch expressions differ in the following ways:

  • In the standard implementation, the catch clause uses a name test to determine whether or not to trap a given error. This enables you to trap specific exceptions by name. The proprietary implementation traps any exception.
  • The standard implementation pre-defines several variables in the scope of the expression evaluated in the catch block. These variables provide details about the error. The proprietary implementation binds an error element to a variable you specify in your catch clause, and then you access error details through that variable.

You cannot trap MarkLogic errors such as XDMP-AS by name with the standard implementation because MarkLogic errors do not have QNames. However, you can trap the XQuery standard error codes or all errors (*) with the standard try/catch expression.

The following is an example of an XQuery standard try/catch expression. It traps all exceptions and prints out a message constructed from some of the implicitly defined variables.

xquery version "1.0-ml";
try {
    fn:error(fn:QName('http://www.w3.org/2005/xqt-errors', 'err:FOER0000'))
}
catch * {
  fn:concat($err:code, " at ", $err:line-number, ":", $err:column-number)
}

For more details, see the Try Catch Expressions discussion in the XQuery 3.0 specification at https://www.w3.org/TR/xquery-30/#id-try-catch.

Implementation-Defined Semantics

The XQuery specification lists items that may be defined by each implementation of XQuery:

http://www.w3.org/TR/xquery/#id-impl-defined-items

This section describes the following implementation-defined items as they are implemented in MarkLogic Server:

Automatic Namespace Imports for Predefined Namespaces

Each dialect has a set of namespace prefixes that are predefined. For those predefined namespaces, it is not necessary to declare the prefix. For example, the fn prefix is predefined in all of the dialects. For a list of predefined namespaces for each dialect, see Predefined Namespace Prefixes for Each Dialect.

The fn: prefix is bound to a different namespace in 1.0 and 1.0-ml than in 0.9-ml.

Namespace path

The namespace path is a list of namespace URIs. Any namespaces listed in a using namespace prolog declaration are added to a namespace path. When resolving any function call that doesn't use a prefix, the namespaces in the namespace path are used in turn.

For example, the following code adds http://marklogic.com/xdmp to the namespace path:

using namespace "http://marklogic.com/xdmp";

External Variables

External variables are one of the things that the XQuery standard refers to as implementation-defined. In MarkLogic Server, external variables are implemented such that you can pass nodes and values into an XQuery program. To use external variables, you pass in external variables to the XQuery program (via xdmp:invoke, xdmp:eval, xdmp:spawn, or via XCC). The variables are passed in as pairs of QNames and values.

An XQuery program that accepts external variables must declare the external variables in its prolog, as in the following code snippet:

declare variable $my:variable as xs:string* external;

You can create a default value for the variable by adding the := to the specification, as in the following code snippet:

declare variable $my:variable as xs:string* external 
  := "default value";

An XQuery program with this variable declaration would be able to use the string values passed into it via an external variable with the QName my:variable (where the namespace prefix my was declared somewhere in both the calling and called environments). You could then reference this variable in the XQuery program as in the following example:

xquery version "1.0-ml";
declare namespace my="myNamespace";
declare variable $my:variable as xs:string* external;

fn:concat("The value of $my:variable is: ", $my:variable)

If you then call this module as follows (assuming the module can be resolved from the path /extvar.xqy.

xquery version "1.0-ml";
declare namespace my="myNamespace";

xdmp:invoke("/extvar.xqy", (xs:QName("my:variable"), "my value"))

This example returns the following string:

The value of $my:variable is: my value

MarkLogic Server will not accept more than 1024 external variable value items over XDBC.

Collations

The XQuery specification allows collation names and default collation values to be determined by the implementation. MarkLogic Server uses collations to specify the sort order of strings, and it defines the URIs for the collations. Each query runs with a default collation, and that collation can come from the environment (each App Server has a default collation setting) or it can be specified in the XQuery program. Also, you can specify collations for string range indexes and for word lexicons to specify their sort order. For details about collations in MarkLogic Server, including the valid URIs for collations, see Encodings and Collations in the Search Developer's Guide.

Implementation-Defined Primitive XQuery Types

MarkLogic Server has extended the XQuery type system and added some primitive types. These types allow functions to operate on them and are very useful for programming. These types are not required by the XQuery specification, but neither are they in conflict with it because the specification allows implementation-specific primitive types. Therefore, these types are available in all of the XQuery dialects in MarkLogic Server (although in 1.0, you need to import the namespace prefixes). The following are some of the built-in types in MarkLogic Server:

Decimal Precision at Least 18 Digits, and is Not Configurable

MarkLogic Server does not include a facility to limit the maximum precision of a decimal. A decimal has a precision of at least 18 decimal digits (64-bits unsigned). For details, see the XML Schema specification (http://www.w3.org/TR/xmlschema-2/#decimal).

Library Modules Default Function Namespace Defaults to Library Namespace

The default function namespace of an XQuery library module is the namespace of the library module. This allows you to declare functions in the library namespace without prefixing the functions. You can override the default function namespace with a declare default function namespace declaration in the prolog of the library module. For library modules where you do not override the default function namespace (and as a general best-practice), prefix the XQuery-standard functions (functions with the fn: prefix, which is bound to the http://www.w3.org/2005/xpath-functions namespace) with the fn: prefix. Note that main modules default function namespace defaults to the fn: namespace, which is different from library modules.

« Previous chapter
Next chapter »
Powered by MarkLogic Server | Terms of Use | Privacy Policy