如何修改 eXist-db 中的内存文件?
How to modify an in-memory document in eXist-db?
我想知道如何修改存储在数据库中的原始文档的内存副本。我对 update
扩展非常满意,它允许我 search/replace 通过文本节点并永久更改它们。然而,这种行为并不总是我想要的。在某些特殊情况下,我需要即时导出文档并稍作修改。 eXist 似乎不支持 copy
,我会考虑。
对于永久更改,我使用:
declare function cust-utils:replace-spaces-hard($document as xs:string) as empty() {
let $doc := doc($document)/tei:TEI
let $match := '(^|\s| )([szkvaiouSZKVAIOU])[\s]'
for $i in (1 to 2)
return
for $text in $doc//text()
return
update value $text[matches(., $match)] with replace($text, $match, ' ')
};
(我重复了两次,因为 XPATH 2.0 似乎不允许在正则表达式中使用环顾四周,这里的匹配有时会重叠。)
临时一样怎么办?我尝试了 Datypic 中的有趣函数,但它只包含 return 个特定节点。我需要保留文档顺序。简单地说,我需要遍历文档树,替换特定的字符串和 return 文档以供以后使用,而不是在数据库中更新它。
更新
不幸的是,这个:
declare function cust-utils:copy($input as item()*) as item()* {
for $node in $input
return $node
};
与
完全一样
declare function cust-utils:copy($input as item()*) as item()* {
for $node in $input
return
typeswitch($node)
case element()
return
element { name($node) } {
for $att in $node/@*
return
attribute { name($att) } { $att }
,
(: output all the sub-elements of this element recursively :)
for $child in $node
return cust-utils:copy($child/node())
}
default return $node
};
... 好像return是没有真正遍历的document-node
eXist 的 XQuery 更新扩展将所有更新写入数据库并且不支持内存操作。这与 eXist 不支持的 W3C XQuery Update Facility 1.0+ 形成对比。因此,在 eXist 中,内存更新必须使用纯 XQuery 执行,即没有正式更新工具的附加语法和功能。
对于使用 eXist 的内存更新,传统路径是执行 "identity transformation",通常使用递归 typeswitch
操作;参见 https://en.wikipedia.org/wiki/Identity_transform#Using_XQuery。一个显示文本节点转换同时保留文档顺序的简单示例是:
xquery version "3.0";
declare function local:transform($nodes as node()*) {
for $node in $nodes
return
typeswitch ($node)
case document-node() return
local:transform($node/node())
case element() return
element {node-name($node)} {
$node/@*,
local:transform($node/node())
}
case text() return
replace($node, '[a-z]+', upper-case($node))
(: drop comment & processing-instruction nodes :)
default return
()
};
let $node :=
document {
element root {
comment { "sample document" },
element x {
text { "hello" },
element y {
text { "there" }
},
text { "friend" }
}
}
}
return
<results>
<before>{$node}</before>
<after>{local:transform($node)}</after>
</results>
结果:
<result>
<before>
<root>
<!-- sample document -->
<x>hello <y>there</y> friend</x>
</root>
</before>
<after>
<root>
<x>HELLO <y>THERE</y> FRIEND</x>
</root>
</after>
</result>
另一种方法是使用内存更新模块,例如 Ryan J. Dew 的 "XQuery XML Memory Operations" 模块,位于 https://github.com/ryanjdew/XQuery-XML-Memory-Operations. If you clone the repository (or download the repository's .zip file and unzip it) and upload the folder to eXist's /db
collection, the following code will work (adapted from this old exist-open post: http://markmail.org/message/pfvu5omj3ctfzrft):
xquery version "3.0";
import module namespace mem="http://maxdewpoint.blogspot.com/memory-operations"
at "/db/XQuery-XML-Memory-Operations-master/memory-operations-pure-xquery.xqy";
let $node := <x>hello</x>
let $copy := mem:copy($node)
let $rename := mem:rename($copy, $node, fn:QName("foo", "y"))
let $replace-value := mem:replace-value($rename, $node, "world")
return
mem:execute($replace-value)
结果:
<y xmlns="foo">world</y>
我想知道如何修改存储在数据库中的原始文档的内存副本。我对 update
扩展非常满意,它允许我 search/replace 通过文本节点并永久更改它们。然而,这种行为并不总是我想要的。在某些特殊情况下,我需要即时导出文档并稍作修改。 eXist 似乎不支持 copy
,我会考虑。
对于永久更改,我使用:
declare function cust-utils:replace-spaces-hard($document as xs:string) as empty() {
let $doc := doc($document)/tei:TEI
let $match := '(^|\s| )([szkvaiouSZKVAIOU])[\s]'
for $i in (1 to 2)
return
for $text in $doc//text()
return
update value $text[matches(., $match)] with replace($text, $match, ' ')
};
(我重复了两次,因为 XPATH 2.0 似乎不允许在正则表达式中使用环顾四周,这里的匹配有时会重叠。)
临时一样怎么办?我尝试了 Datypic 中的有趣函数,但它只包含 return 个特定节点。我需要保留文档顺序。简单地说,我需要遍历文档树,替换特定的字符串和 return 文档以供以后使用,而不是在数据库中更新它。
更新
不幸的是,这个:
declare function cust-utils:copy($input as item()*) as item()* {
for $node in $input
return $node
};
与
完全一样declare function cust-utils:copy($input as item()*) as item()* {
for $node in $input
return
typeswitch($node)
case element()
return
element { name($node) } {
for $att in $node/@*
return
attribute { name($att) } { $att }
,
(: output all the sub-elements of this element recursively :)
for $child in $node
return cust-utils:copy($child/node())
}
default return $node
};
... 好像return是没有真正遍历的document-node
eXist 的 XQuery 更新扩展将所有更新写入数据库并且不支持内存操作。这与 eXist 不支持的 W3C XQuery Update Facility 1.0+ 形成对比。因此,在 eXist 中,内存更新必须使用纯 XQuery 执行,即没有正式更新工具的附加语法和功能。
对于使用 eXist 的内存更新,传统路径是执行 "identity transformation",通常使用递归 typeswitch
操作;参见 https://en.wikipedia.org/wiki/Identity_transform#Using_XQuery。一个显示文本节点转换同时保留文档顺序的简单示例是:
xquery version "3.0";
declare function local:transform($nodes as node()*) {
for $node in $nodes
return
typeswitch ($node)
case document-node() return
local:transform($node/node())
case element() return
element {node-name($node)} {
$node/@*,
local:transform($node/node())
}
case text() return
replace($node, '[a-z]+', upper-case($node))
(: drop comment & processing-instruction nodes :)
default return
()
};
let $node :=
document {
element root {
comment { "sample document" },
element x {
text { "hello" },
element y {
text { "there" }
},
text { "friend" }
}
}
}
return
<results>
<before>{$node}</before>
<after>{local:transform($node)}</after>
</results>
结果:
<result>
<before>
<root>
<!-- sample document -->
<x>hello <y>there</y> friend</x>
</root>
</before>
<after>
<root>
<x>HELLO <y>THERE</y> FRIEND</x>
</root>
</after>
</result>
另一种方法是使用内存更新模块,例如 Ryan J. Dew 的 "XQuery XML Memory Operations" 模块,位于 https://github.com/ryanjdew/XQuery-XML-Memory-Operations. If you clone the repository (or download the repository's .zip file and unzip it) and upload the folder to eXist's /db
collection, the following code will work (adapted from this old exist-open post: http://markmail.org/message/pfvu5omj3ctfzrft):
xquery version "3.0";
import module namespace mem="http://maxdewpoint.blogspot.com/memory-operations"
at "/db/XQuery-XML-Memory-Operations-master/memory-operations-pure-xquery.xqy";
let $node := <x>hello</x>
let $copy := mem:copy($node)
let $rename := mem:rename($copy, $node, fn:QName("foo", "y"))
let $replace-value := mem:replace-value($rename, $node, "world")
return
mem:execute($replace-value)
结果:
<y xmlns="foo">world</y>