How to Replace multiple nodes of XML in one go ? DATA HUB FRAMEWORK

落花浮王杯 提交于 2019-12-11 08:44:57

问题


I am having several documents in STAGING DB.

Based on a condition i need to check if the document with same ID exist in FINAL.

If it does then i need to replace the multiple nodes of document from STAGING to FINAL and then insert it.

DOC from STAGING-
<root>
 <ID>1</ID>
 <value1>India</value1>
 <value2>USA</value2>
 <value3>Russia</value3>
 <value4>Srilanka</value4>
 <value5>Europe</value5>
 <value6>Antartica</value6>
 <value7>Spain</value7>
</root>

DOC from FINAL-
<root>
 <ID>1</ID>
 <value1></value1>
 <value2></value2>
 <value3></value3>
 <value4></value4>
 <value5>Europe</value5>
 <value6>Antartica</value6>
</root>

OUTPUT i am expecting in FINAL-

<root>
 <ID>1</ID>
 <value1>India</value1>
 <value2>USA</value2>
 <value3>Russia</value3>
 <value4>Srilanka</value4>
 <value5>Europe</value5>
 <value6>Antartica</value6>
 <value7>Spain</value7>
</root>

From the STAGING i just need to focus on (value1,value2,value,value4,value7) and replace it. For other values i am having some different conditions so i have to ignore them.

Logic i have written in WRITER.xqy-

  let $boolean := fn:false()
  let $var := if((......)
             then
                (
                    let $docs :=
                                   cts:search(doc(),cts:and-query((
                                                                        cts:element-value-query(xs:QName("ID"),$id),
                                                                        cts:collection-query(("MyCollection"))
                                                                   ))) 
                    let $temp := 
                                    if((fn:exists($result) eq fn:true())) then xdmp:set($boolean,fn:true()) else ()
                    return $docs


                ) 
            else ()

let $envelope := if($boolean) 
                    then
                        (
                        let $nodes := ("value1,value2,value,value4,value7")
                        let $tokenize := fn:tokenize($nodes,",")
                        let $values := for $i in $tokenize
                                       let $final :=  xdmp:value(fn:concat("$var//*:root/*:",$i))
                                       let $staging :=  xdmp:value(fn:concat("<",$i,">","{$envelope//*:root/*:",$i,"/text()}","</",$i,">"))
                                       let $envelope := mem:node-replace($final,$staging) 
                                       return $envelope
                        return $values
                        ) 
               else $envelope
return
xdmp:document-insert($id,$envelope, xdmp:default-permissions(), map:get($options, "entity"))

This gives me

ERROR- ARG2 for xdmp:document-insert is NOT a NODE.

I do understand it as my $envelope is iterating for all the NODES and returning multiple envelopes.

Any Suggestions to resolve this ?


回答1:


When you use the in-mem-update function, it returns the result of the modification.

If you are going to be making a sequence of changes to the document, you need to use the product of the previous mem:* method calls as the input for the next. You can achieve that with a recursive function that either calls itself with one less element name, or returns the final result when there are no more names.

Below is an example of how it could be done. I also simplified some of the logic to use XPath to select the desired elements with a predicate filter on the local-name(), instead of generating strings and evaluating with xdmp:value(). I think that it's more straightforward and easier to read.

import module namespace mem    = "http://xqdev.com/in-mem-update" 
    at '/MarkLogic/appservices/utils/in-mem-update.xqy';

declare function local:replace-elements($final-doc, $staging-doc, $element-names) {
  if (fn:empty($element-names)) then 
    $final-doc
  else
    let $name := fn:head($element-names)
    let $final :=  $final-doc//*:root/*[local-name() = $name]
    let $staging :=  $staging-doc//*:root/*[local-name() = $name]
    let $final-updated :=
      if ($final) then 
        mem:node-replace($final, $staging)
      else (: the element doesn't exist in the final doc :)
        (: insert the staging element as a child of the root element :)
        mem:node-insert-child($final-doc//*:root, $staging) 
        (: Otherwise, if you don't want to add the staging element, return $final-doc instead of inserting :)
    return
      local:replace-elements(document{$final-updated}, $staging-doc, fn:tail($element-names)) 
};

let $boolean := fn:false()
let $var := 
  if ((......) then
    (
      let $docs :=
        cts:search(doc(), cts:and-query((
          cts:element-value-query(xs:QName("ID"), $id),
          cts:collection-query("MyCollection")
        ))) 
      let $temp := 
        if ((fn:exists($result) eq fn:true())) then 
          xdmp:set($boolean,fn:true()) 
        else ()
      return $docs
    ) 
  else ()

let $envelope := 
  if ($boolean) then
    let $nodes := ("value1,value2,value3,value4,value7")
    let $element-names := fn:tokenize($nodes,",")
    return
      local:replace-elements($var, $envelope, $element-names)
  else $envelope

return
    xdmp:document-insert($id,$envelope, xdmp:default-permissions(), map:get($options, "entity"))


来源:https://stackoverflow.com/questions/51998598/how-to-replace-multiple-nodes-of-xml-in-one-go-data-hub-framework

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!