XQuery 替换节点中的多个单词

XQuery replace multiple words from a node

以下是 XML 文件 -

<A>
  <B>
    <Data>John is a good</Data>
    <Data>James loves cricket</Data>
    <Data>John loves Hockey</Data>
  </B>
  <B>
    <Data>Stuart loves cricket</Data>
    <Data>Johny loves Hockey</Data>
  </B>
</A> 

我想替换节点 Data 中除姓名(James、John、Stuart)之外的所有单词。我正在尝试一次性完成这项工作。

以下是 XQuery -

for $words in ("Hockey", "crikcet", "is")
let $word := $words
   for $x in doc('file')//Data
      where contains($x, $word)
      return replace value of node $x with normalize-space(replace($x, $word, ''))

我遇到错误- [XUDY0017] Node can only be replaced once: element Data ...

我正在使用 BaseX 7.6

通过交换两个循环,确保每个节点只被触及一次。这是一种可能 lambda magic 一个接一个地进行所有替换:

let $words := ("Hockey", "cricket", "is")
for $data in doc('file')//Data
where some $word in $words satisfies contains($data, $word)
return
  replace value of node $data
  with normalize-space(fold-left($words, $data, replace(?, ?, '')))

首先,您应该更新到最新版本的 BaseX。它目前的版本是 8.2.x 和 7.6。很久没支持了

其次,您的整个方法并不特别像 XQuery。例如,而不是做

for $words in ("Hockey", "crikcet", "is")
let $word := $words

下一行的作用完全相同,但更短更容易

for $word in ("Hockey", "crikcet", "is")

由于有两个 for 循环,如果一个数据值包含多个命中,同一个节点可以在这里使用两次或更多次。这是您出现错误消息的时候(在您提供的示例数据集中实际上不会发生这种情况,因为每个数据值只包含一个搜索字符串)。

多次替换每个值不起作用,因为 XQuery 更新根据挂起更新列表 (PUL) 应用更新,即在查询结束时。如果要将同一个数据值替换两次,处理器当然不知道用什么替换。相反,您应该自己计算替换值,然后替换完整值。这是递归进来的,你的用例就是一个很好的例子。因此,以下应该有效:

declare function local:replace-word($word as xs:string, $search as xs:string*, $replace as xs:string*) as xs:string {
  if (empty($search)) then $word
  else replace(local:replace-word($word, tail($search), tail($replace)), head($search), head($replace))
};

let $words := ("Hockey", "cricket", "is")
let $replace := ("Replace1", "Replace2", "Replace3")
  for $x in //Data
  return replace value of node $x with normalize-space(local:replace-word($x, $words, $replace))

那么,它有什么作用?首先,我介绍了第二个序列,其中包含您要替换的值。在您的查询中,您总是用一个空字符串替换,即您删除了这个词,这不是您的问题所要求的。此外,我将您的错字替换为 cricket.

我们现在只有一个 for 循环,它遍历每个 Data 元素。它调用 local:replace-word 函数。此函数调用自身(因此:递归)直到序列中不再有 search/replace 个单词。 head() 获取序列中的第一项,而 last() 获取序列中的所有其他值。