1

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 Answer 1

2

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"))
Sign up to request clarification or add additional context in comments.

4 Comments

Thanks for replying. I can't use xdmp:node-replace() here because of conflicts. Also, the logic you suggested works well but the problem i am facing is not resolved. After node-replace i need to insert the document using xdmp:document-insert() but since my node-replace() will iterate for each of the $name, it is returning "n" number of document at last (One document for each $name). During xdmp:document-insert() it gives me error as arg2 is not of type node(). This is because i am trying to insert multiple document at once. I want something where i can replace all $name at once.
I have updated my answer to focus on your issue (the sequence of documents). You would not only want a single result document, but also need to be using the result of each call to the mem:* functions as the input for the next.
@Mads-- For some reason it gives me error XDMP-NOTANODE: for $final-doc//*:root/*[local-name() = $name]. It is getting all the values (value1 to 7) inside it at once
@Mads-- The issue is resolved now but my code is taking more time. Can you please see this SO question and suggest me to profile my code- stackoverflow.com/questions/52201151/…

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.