基于 XQuery 中的字符串动态重写 MarkLogic xml 文档

Rewrite MarkLogic xml document dynamically based on strings in XQuery

我需要一个能够重写文档的 XQuery 函数,特别是 return 子节点的子集,基于指定要检索的节点名称的字符串数组。如果应该满足如下定义并且需要使用任意文档。

local:apply-node-includes($document, $includedNodeNames as xs:string*)

鉴于我有一些 xml 文档

let $doc := 
  <foo a="b">
    <bar>hello</bar>
    <baz>1</baz>
    <bang>
      <text>world</text>
    </bang>
  </foo>')

然后该函数应该转换文档,以便只有在 $includedNodes 中指定名称的子节点被 returned。

示例 local:apply-node-includes($doc, 'baz') 将 return

<foo a="b">
  <baz>1</baz>
</foo>

示例 local:apply-node-includes($doc, ('bar','bang'))) 将 return

<foo a="b">
  <bar>hello</bar>
  <bang>
    <text>world</text>
  </bang>
</foo>

我尝试在节点上迭代,and/or 使用某种形式的递归类型切换,但到目前为止无法正确完成。如果它完全递归地工作,那将是非常酷的,所以 'bang.text' 将只包括孙子文本节点而不包括它们的任何兄弟姐妹,但也许这要求太多了!

我不确定这是否是最优雅的解决方案,但似乎可以满足您的要求。此处的函数重新创建传递的文档的根元素,然后包括元素名称与传递的列表中的任何字符串匹配的任何直接子元素(及其所有属性和子元素)。

declare function local:apply-node-includes( $doc as item(), $includedNodeNames as xs:string*) as item()
{
   (: Recreate the root element :)
   element {name($doc)} 
   { (: Replicate root element's attributes :)
     (for    $attribute in $doc/@* return $attribute),
     (: Replicate root element's immediate children having any of given names :)
     (for    $element in $doc/* where name($element) = $includedNodeNames 
      return $element)
   }
};

let $doc := 
  <foo a="b">
    <bar>hello</bar>
    <baz>1</baz>
    <bang>
      <text>world</text>
    </bang>
  </foo>
return local:apply-node-includes($doc, ('bar','bang'))

输出:

<foo a="b"><bar>hello</bar><bang><text>world</text></bang></foo>

@DavidDeneberg 给出了一个很好的答案,但我设法使用一些 xpath 进一步简化了它,所以为其他人发帖。

declare function local:apply-node-includes($doc as element(), $includedNodeNames as xs:string*) as element()?
{
  element {node-name($doc) }
  {
    $doc/@*,
    $doc/*[name(.)=$include-names]
  }
};

此外,这本书在这个主题上非常有用 https://en.wikibooks.org/wiki/XQuery/Filtering_Nodes 并演示了您需要能够处理问题的孙子部分的那种递归身份转换。