Xquery 如何一次性替换XML的多个节点?数据中心框架

Xquery 如何一次性替换XML的多个节点?数据中心框架,xquery,marklogic,marklogic-dhf,Xquery,Marklogic,Marklogic Dhf,我在暂存数据库中有几个文档 根据一个条件,我需要检查FINAL中是否存在具有相同ID的文档 如果确实如此,那么我需要将文档的多个节点从STAGING替换为FINAL,然后插入它 DOC from STAGING- <root> <ID>1</ID> <value1>India</value1> <value2>USA</value2> <value3>Russia</value3>

我在暂存数据库中有几个文档

根据一个条件,我需要检查FINAL中是否存在具有相同ID的文档

如果确实如此,那么我需要将文档的多个节点从STAGING替换为FINAL,然后插入它

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>
来自暂存的文档-
1.
印度
美国
俄罗斯
斯利兰卡
欧洲
南极洲
西班牙
来自决赛的文件-
1.
欧洲
南极洲
输出我期待在最后-

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

1.
印度
美国
俄罗斯
斯利兰卡
欧洲
南极洲
西班牙
从登台开始,我只需要关注(值1、值2、值、值4、值7)并替换它。对于其他值,我有一些不同的条件,因此我必须忽略它们。

我用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"))
let$boolean:=fn:false()
设$var:=if(……)
然后
(
让$docs:=
cts:搜索(doc()、cts:和查询((
cts:元素值查询(xs:QName(“ID”),$ID),
cts:集合查询((“MyCollection”))
))) 
让$temp:=
如果((fn:exists($result)eq fn:true()),那么xdmp:set($boolean,fn:true())else()
返回$docs
) 
else()
let$envelope:=if($boolean)
然后
(
让$nodes:=(“value1、value2、value、value4、value7”)
让$tokenize:=fn:tokenize($nodes,“”)
让$values:=对于$i,在$tokenize中
设$final:=xdmp:value(fn:concat($var/*:root/*:“,$i))
让$staging:=xdmp:value(fn:concat(“,”{$envelope//*:root/*:”,$i,“/text()},”)
让$envelope:=mem:node replace($final,$staging)
退回$信封
返回$value
) 
美元信封
返回
xdmp:documentinsert($id,$信封,xdmp:default-permissions(),map:get($options,“entity”))
这给了我

错误-xdmp的ARG2:文档插入不是节点

我确实理解这一点,因为我的
$envelope
正在迭代所有节点并返回多个信封


有没有解决此问题的建议?

当您使用in-mem update函数时,它会返回修改的结果

如果要对文档进行一系列更改,则需要使用上一个mem:*方法调用的乘积作为下一个mem:*方法调用的输入。可以使用递归函数实现这一点,该函数可以使用较少的元素名称调用自身,也可以在没有更多名称时返回最终结果

下面是一个如何实现的示例。我还简化了一些逻辑,使用XPath通过
local-name()
上的谓词过滤器选择所需的元素,而不是生成字符串并使用
xdmp:value()
进行计算。我认为这更简单易读

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"))

感谢您的回复。由于冲突,我无法在此处使用xdmp:node-replace()。此外,您建议的逻辑运行良好,但我面临的问题尚未解决。在node replace之后,我需要使用xdmp:document-insert()插入文档,但由于我的node-replace()将对每个$name进行迭代,因此返回“n”最后的文档数(每个$name对应一个文档)。在xdmp:document-insert()过程中,由于arg2不是node()类型,因此会出现错误。这是因为我试图一次插入多个文档。我需要一个可以一次替换所有$name的文档。我已更新了答案,以关注您的问题(文档序列)。您不仅需要一个结果文档,还需要使用对mem:*函数的每次调用的结果作为下一次调用的输入。@Mads--出于某种原因,它给了我错误
XDMP-NOTANODE:
for
$final doc//*:root/*[local-name()=$name]
。它正在获取所有值(值1到7)在里面once@Mads--这个问题现在已经解决了,但是我的代码需要更多的时间。你能看到这个问题并建议我分析我的代码吗-