All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ml-modules.root.data-hub.5.builtins.steps.mapping.entity-services.xquery-lib.xqy Maven / Gradle / Ivy

There is a newer version: 6.1.1
Show newest version
(:
  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