When constructing queries we could reach a level when we should raise the abstraction. This is the point of the functions. We could break down complexity with them and could be reused in any point. In an XQuery document we could create as many as we want just need to be care about the name conflicts. (To avoid this its recommended not to use the built in 'fn' namespace, rather than create locale ones. )
The declaration is done by the following syntax:
declare function NAMESPACE:FUNCTION_NAME( $parameter1 [ as DATATYPE[CARDIANALTY] ) ], … ) as RETURNTYPE { };The NAMESPACE cannot be empty. If we do not want to use our custom one than we could use the built-in default namespace called 'local'. The name of the function could be freely selected. The parameter list is declares the type of the input parameters and we could specify one return type. The cardinality shows the acceptable number of elements for one parameter.
We could use the following cardinalities:
? : zero or one
+: one or more
*: zero or more
For the type of the parameters we could use the following ones:
node(): any node
element():
element( NAME ): only elements with a NAME start tag accepted
without any parameter it will accept all elements
scheme-element( NAME ): only elements defined in a scheme
attribute(): attributes
examples for functions:
declare default function namespace "http://www.inf.unideb.hu/FAT/XQuery/functions";
declare function path($n as node()) as xs:string
{ fn:string-join(for $a in $n/ancestor-or-self::*
return fn:name($a), "/")
};
declare function reverse($seq)
{ for $i at $p in $seq
order by $p descending
return $i
};
// using our default examples
// countng the average high of the patients
declare function local:avg-height($p as element(persons) ) as xs:double {
avg($p/person/information/medicalInformation/height)
};
// look for a patient with a given name
declare function local:filter($persons as element(persons), $filter as xs:string ) as xs:string* {
$persons/person/information/personalInformation/name[../contains(name,$filter)]
};As we see trees during all the solutions and recursion is the most trivial way to deal with them we can see an example for it to simulate thee ancestor axe:
declare function ancestors($n as node()?) as node()*
{ if (fn:empty($n)) then ()
else (ancestors($n/..), $n/..)
}and a more complex one - renaming a given attribute in all the elements in a tree:
declare function local:rplc_attr($n as node(),
$from as xs:string,
$to as xs:string)
{ typeswitch ($n)
case $e as element() return
let $a := ($e/@*)[name(.) eq $from]
return
element
{ node-name($e) }
{ $e/(@* except $a),
if ($a) then attribute {$to} {data($a)}
else (),
for $c in $e/node()
return local:rplc_attr($c, $from, $to) }
default return $n
};