ml-modules.root.data-hub.5.builtins.steps.mapping.entity-services.xquery-lib.xqy Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of marklogic-data-hub Show documentation
Show all versions of marklogic-data-hub Show documentation
Library for Creating an Operational Data Hub on MarkLogic
(:
Copyright (c) 2021 MarkLogic Corporation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
:)
xquery version "1.0-ml";
(: This library is for operations for mapping that are more dificult to accomplish in JavaScript :)
module namespace xquery-lib = "http://marklogic.com/mapping/es/xquery";
import module namespace inst="http://marklogic.com/entity-services-instance" at "/MarkLogic/entity-services/entity-services-instance.xqy";
import module namespace es="http://marklogic.com/entity-services" at "/MarkLogic/entity-services/entity-services.xqy";
import module namespace json = "http://marklogic.com/xdmp/json" at "/MarkLogic/json/json.xqy";
declare namespace s = "http://www.w3.org/2005/xpath-functions";
declare function document-with-nodes($nodes as node()*) {
document {
$nodes
}
};
(:
Copy of this function from ML 10.0-3. The only change is the addition of user-params.
Note that "parms" in the code below should really be "options", which would then match the signature of xslt-eval.
I opened bug 54632 to improve the ES functions so that a map of params will be accepted. Then we can get rid of this
hack.
:)
declare function data-hub-map-to-canonical(
$source-instance as node(),
$mapping-uri as xs:string,
$user-params as map:map?,
$options as map:map
) as item()*
{
let $target-entity-name := $options=>map:get("entity")
let $format := $options=>map:get("format")
let $format :=
if (empty($format)) then
typeswitch ($source-instance)
case document-node() return
if ($source-instance/element()) then "xml"
else "json"
case element() return "xml"
default return "json"
else $format
let $input :=
typeswitch ($source-instance)
case document-node() return $source-instance
case element() return document { $source-instance }
default return document { $source-instance }
let $parms :=
if (empty($target-entity-name)) then ()
else map:map()=>map:with("template", $target-entity-name)
let $results :=
xdmp:xslt-invoke($mapping-uri||".xslt", $input, $user-params, $parms)
let $array-of-entity-instances-array := json:array()
(: All instances generated by a mapping are wrapped into element, i is a counter variable starting from 0
. Hence all xml/json instances gnerated by a mapping can be grouped and pushed into json array and returned :)
let $_ :=
for $entityInstances in $results/node()
let $entity-instances-array := json:array()
let $_ :=
if ($format="xml") then (
let $_ :=
for $element in $entityInstances/node()
let $entityInstanceObject := json:object()
return
(map:put($entityInstanceObject, 'value', inst:canonical-xml(xdmp:unquote(xdmp:quote($element/*:value/node())))),
map:put($entityInstanceObject, 'uri', $element/*:uri/text()),
json:array-push($entity-instances-array, $entityInstanceObject))
return ()
)
else(
(: This change is necessitated by DHFPROD-6219. In case the entity doesn't have strucutred properties, it
returns empty json object. If it has structured properties, the empty structured properties are removed.
inst:canonical-json() method introduces the '$ref'. :)
let $_ :=
for $element in $entityInstances/node()
let $entityInstanceObject := json:object()
return
if (empty($element/*:value/node()/*)) then
(map:put($entityInstanceObject, 'value',document{json:object()=>map:with(string(fn:node-name($element/*:value/node())), json:object())}),
map:put($entityInstanceObject, 'uri', $element/*:uri/text()),
json:array-push($entity-instances-array, $entityInstanceObject))
else
(map:put($entityInstanceObject, 'value',inst:canonical-json(xquery-lib:remove-empty-structured-properties($element/*:value/node()))),
map:put($entityInstanceObject, 'uri', $element/*:uri/text()),
json:array-push($entity-instances-array, $entityInstanceObject))
return ()
)
return json:array-push($array-of-entity-instances-array, $entity-instances-array)
return $array-of-entity-instances-array
};
declare function remove-empty-structured-properties($element as item()*) as document-node() {
document{
element {fn:node-name($element)} {
$element/namespace::node(),
$element/@*,
for $child in $element/element()
where not($child/element() instance of element() and empty($child/element()/element()))
return $child
}
}
};
declare %private variable $fetch := '
declare variable $uri as xs:string external;
fn:doc($uri)
';
declare %private variable $put := '
declare variable $uri as xs:string external;
declare variable $node as node() external;
declare variable $collection as xs:string external;
declare variable $xslt-uri as xs:string := $uri||".xslt";
declare variable $options :=
{xdmp:document-get-permissions($uri,"elements")}
{for $c in xdmp:document-get-collections($uri) return {$c} ,
{$collection}
}
;
xdmp:document-insert($xslt-uri, $node, $options)
';
(: xqueryLib.functionMetadataPut should be used instead of the OOTB es.functionMetadataPut in order to allow
sequence to be passed to javascript mapping functions. This function makes use of
/data-hub/5/mapping/entity-services/function-metadata.xsl (which is the modified version of
/MarkLogic/entity-services/function-metadata.xsl) to resolve https://project.marklogic.com/jira/browse/DHFPROD-5850
:)
declare function function-metadata-put(
$uri as xs:string
) as empty-sequence()
{
let $source :=
xdmp:eval(
$fetch,
map:map()=>map:with("uri",$uri),
map:map()=>map:with("database",xdmp:modules-database()))
let $compiled := xquery-lib:function-metadata-compile($source)
where fn:exists($compiled)
return
xdmp:eval($put,
map:map()=>map:with("uri",$uri)=>
map:with("node",$compiled)=>
map:with("collection",$es:FUNCTIONDEF_COLLECTION)
,
map:map()=>map:with("database",xdmp:modules-database()))
};
declare function function-metadata-compile(
$function-metadata as node()
) as node()
{
let $input := es:function-metadata-validate($function-metadata)
return
xdmp:xslt-invoke("/data-hub/5/mapping/entity-services/function-metadata.xsl", $input)
};
(: Determine functions available for mapping by calling fn:function-available on the function in an XSLT with xdmp:dialect="tde" :)
declare function detect-functions() {
let $functions := xdmp:xslt-invoke("/data-hub/5/builtins/steps/mapping/entity-services/detect-mapping-functions.xslt", document{ },
map:entry("functions", xdmp:functions#0)
=> map:with("function-signature", xdmp:function-signature#1)
=> map:with("function-name", xdmp:function-name#1)
=> map:with("function-available", fn:function-available#1)
=> map:with("function-arity", fn:function-arity#1)
=> map:with("local-name-from-QName", fn:local-name-from-QName#1)
)/root/function
for $fun in $functions
let $function-name := $fun/@name
let $function-arity := fn:number($fun/@arity)
let $all-arities := $functions[@name eq $function-name]/@arity ! xs:int(.)
let $min-arity := fn:min($all-arities)
let $max-arity := fn:max($all-arities)
where $function-arity eq $max-arity
order by fn:string($function-name)
return
let $signature := parse-function-signature(fn:string($fun), $min-arity, $max-arity)
let $name := fn:string($fun/@name)
return map:map() => map:with("signature", $signature) => map:with("functionName", $name)
};
declare function parse-function-signature($signature as xs:string, $min-arity as xs:int, $max-arity as xs:int) as xs:string {
if ($min-arity ne $max-arity) then
let $parsed-function := fn:analyze-string(
$signature,
"\("||fn:string-join((
for $arg-number in 1 to $min-arity
return
"[^,\s]+,\s"
), "")||"([^,\s]+(,\s)?)+\)"
)
return fn:string-join(
for $part in $parsed-function/*
return
typeswitch($part)
case element(s:match) return
for $node in $part/node()
return
typeswitch($node)
case element(s:group) return fn:replace(fn:string($node), "([^,]+)(,/s)?", "[$1]$2")
default return fn:string($node)
default return fn:string($part)
,
"")
else
$signature
};
declare function function-metadata-generate-with-namespace($namespace as xs:string, $uri as xs:string) {
es:function-metadata-generate($namespace, $uri)
};
declare function function-metadata-generate($uri as xs:string) {
es:function-metadata-generate($uri)
};
© 2015 - 2024 Weber Informatics LLC | Privacy Policy