Search code examples
xsltweblogicxquery

XQuery to append node to existing node set


I am new to working with XQuery and XSLT. WebLogic is using XQuery 1.0.

I have a WebLogic composite and I have a collection of nodes and just want to add a node to an existing node set in an XQuery assignment. My current solution seems too heavyweight. I will be dealing with tens of thousands of records and it seems to me there should be a really easy way to just insert a node under another node and return it.

declare function local:func($svdPpl as element() (:: element(*,  sv:svdPpl) ::), 
                            $svdPsn as element() (:: element(*, sv:svdPsn) ::)) 
                            as element() (:: element(*,  sv:svdPpl) ::) {
   <sv:svdPpl>
   {for $psn in $svdPpl return $psn} {$svdPsn}
   </sv:svdPpl>
};

If I understand XQuery correctly that means millions of loops just to add records to existing node sets. I have tried using the insert node $svdPsn into $svdPpl syntax but that is causing validation errors in the WebLogic editor.

Any help is appreciated.


Solution

  • XQuery is a functional language and variables are immutable. It's not possible to change the value of a variable once an assignment has been made using standard XQuery.

    There is an extension to XQuery that allows updates called XQuery Update Facility, but not all processors implement it. I'm not familiar with WebLogic, but it looks like some versions support it. That would be the direct route.

    Failing that, a common pattern is to write a generic recursive typeswitch function that walks the tree and applies transformation logic as it generates a modified copy of the input. This is how things work implicitly in XSLT. It's unclear from your question exactly how that would be implemented, but just as an example:

    declare function local:add-after(
      $context as item(),
      $target-name as xs:QName,
      $payload as item()*
    ) as item()*
    {
      typeswitch ($context)
      case element() return (
        element { node-name($context) } {
          $context/@*, 
          for $n in $context/node()
          return local:add-after($n, $target-name, $payload)
        },
        if (node-name($context) = $target-name)
        then $payload
        else ())
      default return $context  
    };
    

    Given an XML node $xml with a value of:

    <x>
      <y>
        <z></z>
      </y>
    </x>
    

    You could use the above function local:add-after($xml, xs:QName('y'), <payload/>) to add a payload after the <y> element:

    <x>
      <y>
        <z></z>
      </y>
      <payload></payload>
    </x>