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/).
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 occuring 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 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:
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.
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";
You cannot use function mapping in the 0.9-ml
dialect; if you run code expecting it to map singletons to a sequence in 0.9-ml
(or in 1.0
or 1.0-ml
if function mapping is diabled), it will throw an exception because the sequence cannot be cast to a single string.
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.
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.
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:
xdmp:node-insert
, and so on)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)]
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.
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.
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).
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 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.
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.
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/.
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.
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.
A URI Qualified Name enables you to specify a namspace 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 qualifed 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.
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 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.
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.
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.
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/.
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:
%rapi:transaction-mode(
mode)
: Specify the transaction mode of a function in a REST Client API extension module. For details, see Controlling Transaction Mode in the REST Application Developer's Guide.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.
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.
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.
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 severale 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.
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 leve, 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.
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:
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 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.
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:
Except where noted, the items in this section apply all of the XQuery dialects supported in MarkLogic Server.
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
.
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.
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.
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:
cts:query
(with many subtypes such as cts:word-query, cts:element-query, and so on)cts:region
(with subtypes cts:box, cts:circle, cts:polygon
, and cts:point)json:array
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).
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.