如何在 MarkLogic Javascript 中复制和修改文档?
How to copy and modify a document in MarkLogic Javascript?
我想复制一个文档并修改其中的一个节点,最好是在一个事务中。看起来最好的方法是克隆旧文档,使用 in-mem-update 库修改它,然后将它写入新的 URI。但是我到目前为止只是写了原始文档,没有修改。
doc1.xml
:
<root>
<foo>bar</foo>
</root>
我目前拥有的:
declareUpdate();
const mem = require("/custom-modules/utils/in-mem-update.xqy");
let oldUri = "/test/doc1.xml";
let newUri = "/test/doc2.xml";
let oldDoc = cts.doc(oldUri);
let newDoc = fn.head(xdmp.unquote(JSON.parse(JSON.stringify(oldDoc))));
let nb = new NodeBuilder();
nb.startElement("foo");
nb.addText("baz");
nb.endElement();
mem.nodeReplace(fn.head(newDoc.xpath("/root/foo")), nb.toNode());
xdmp.documentInsert(newUri, newDoc);
有更好的方法吗?
in-mem-update 库可能很慢且效率低下,尤其是在有很多更改且输入文档很大的情况下。
您可以通过带有恒等变换和 foo/text()
:
专用模板的 XSLT 相当轻松地做到这一点
declareUpdate();
const oldUri = "/test/doc1.xml";
const newUri = "/test/doc2.xml";
const oldDoc = cts.doc(oldUri);
const xslt = fn.head(xdmp.unquote(
'<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">\n\
<xsl:template match="foo/text()"><xsl:text>baz</xsl:text></xsl:template>\n\
<xsl:template match="@*|node()"><xsl:copy><xsl:apply-templates select="@*|node()"/></xsl:copy></xsl:template>\n\
</xsl:stylesheet>'));
const newDoc = xdmp.xsltEval(xslt, oldDoc.root);
xdmp.documentInsert(newUri, newDoc);
您也可以使用 xsl:param
发送要将文本更改为的值。
declareUpdate();
const oldUri = "/test/doc1.xml";
const newUri = "/test/doc2.xml";
const oldDoc = cts.doc(oldUri);
const xslt = fn.head(xdmp.unquote(
'<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">\n\
<xsl:param name="foo-val"/>\n\
<xsl:template match="foo/text()"><xsl:value-of select="$foo-val"/></xsl:template>\n\
<xsl:template match="@*|node()"><xsl:copy><xsl:apply-templates select="@*|node()"/></xsl:copy></xsl:template>\n\
</xsl:stylesheet>'));
const newDoc = xdmp.xsltEval(xslt, oldDoc.root, {"foo-val": "baz"});
xdmp.documentInsert(newUri, newDoc);
您还可以考虑将 XSLT 保存在数据库中并使用 xdmp.xsltInvoke
而不是从 JavaScript 中取消引用序列化的 XSLT 字符串。只是将其全部内联以使其更易于演示。
我同意 Mads 的观点,内存更新库往往很慢。它会为您应用的每个更改构建一个新的节点树。有一个较新的副本可以一次性完成所有更改,从而提高效率。这可能是 Mads 描述的 XSLT 方法的一个很好的替代方法:
https://github.com/ryanjdew/XQuery-XML-Memory-Operations
也就是说,你很接近。您错过了这样一个事实,即 mem 函数 return 是节点树的更改副本,而不是像 xdmp 节点函数那样就地更改它。只需在 mem.nodeReplace
调用前添加一个 newDoc =
即可使您的代码正常工作:
declareUpdate();
const mem = require('/MarkLogic/appservices/utils/in-mem-update.xqy');
let oldUri = "/test/doc1.xml";
let newUri = "/test/doc2.xml";
let oldDoc = cts.doc(oldUri);
let newDoc = fn.head(xdmp.unquote(JSON.parse(JSON.stringify(oldDoc))));
let nb = new NodeBuilder();
nb.startElement("foo");
nb.addText("baz");
nb.endElement();
newDoc = mem.nodeReplace(fn.head(newDoc.xpath("/root/foo")), nb.toNode());
//xdmp.documentInsert(newUri, newDoc);
newDoc
HTH!
我想复制一个文档并修改其中的一个节点,最好是在一个事务中。看起来最好的方法是克隆旧文档,使用 in-mem-update 库修改它,然后将它写入新的 URI。但是我到目前为止只是写了原始文档,没有修改。
doc1.xml
:
<root>
<foo>bar</foo>
</root>
我目前拥有的:
declareUpdate();
const mem = require("/custom-modules/utils/in-mem-update.xqy");
let oldUri = "/test/doc1.xml";
let newUri = "/test/doc2.xml";
let oldDoc = cts.doc(oldUri);
let newDoc = fn.head(xdmp.unquote(JSON.parse(JSON.stringify(oldDoc))));
let nb = new NodeBuilder();
nb.startElement("foo");
nb.addText("baz");
nb.endElement();
mem.nodeReplace(fn.head(newDoc.xpath("/root/foo")), nb.toNode());
xdmp.documentInsert(newUri, newDoc);
有更好的方法吗?
in-mem-update 库可能很慢且效率低下,尤其是在有很多更改且输入文档很大的情况下。
您可以通过带有恒等变换和 foo/text()
:
declareUpdate();
const oldUri = "/test/doc1.xml";
const newUri = "/test/doc2.xml";
const oldDoc = cts.doc(oldUri);
const xslt = fn.head(xdmp.unquote(
'<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">\n\
<xsl:template match="foo/text()"><xsl:text>baz</xsl:text></xsl:template>\n\
<xsl:template match="@*|node()"><xsl:copy><xsl:apply-templates select="@*|node()"/></xsl:copy></xsl:template>\n\
</xsl:stylesheet>'));
const newDoc = xdmp.xsltEval(xslt, oldDoc.root);
xdmp.documentInsert(newUri, newDoc);
您也可以使用 xsl:param
发送要将文本更改为的值。
declareUpdate();
const oldUri = "/test/doc1.xml";
const newUri = "/test/doc2.xml";
const oldDoc = cts.doc(oldUri);
const xslt = fn.head(xdmp.unquote(
'<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">\n\
<xsl:param name="foo-val"/>\n\
<xsl:template match="foo/text()"><xsl:value-of select="$foo-val"/></xsl:template>\n\
<xsl:template match="@*|node()"><xsl:copy><xsl:apply-templates select="@*|node()"/></xsl:copy></xsl:template>\n\
</xsl:stylesheet>'));
const newDoc = xdmp.xsltEval(xslt, oldDoc.root, {"foo-val": "baz"});
xdmp.documentInsert(newUri, newDoc);
您还可以考虑将 XSLT 保存在数据库中并使用 xdmp.xsltInvoke
而不是从 JavaScript 中取消引用序列化的 XSLT 字符串。只是将其全部内联以使其更易于演示。
我同意 Mads 的观点,内存更新库往往很慢。它会为您应用的每个更改构建一个新的节点树。有一个较新的副本可以一次性完成所有更改,从而提高效率。这可能是 Mads 描述的 XSLT 方法的一个很好的替代方法:
https://github.com/ryanjdew/XQuery-XML-Memory-Operations
也就是说,你很接近。您错过了这样一个事实,即 mem 函数 return 是节点树的更改副本,而不是像 xdmp 节点函数那样就地更改它。只需在 mem.nodeReplace
调用前添加一个 newDoc =
即可使您的代码正常工作:
declareUpdate();
const mem = require('/MarkLogic/appservices/utils/in-mem-update.xqy');
let oldUri = "/test/doc1.xml";
let newUri = "/test/doc2.xml";
let oldDoc = cts.doc(oldUri);
let newDoc = fn.head(xdmp.unquote(JSON.parse(JSON.stringify(oldDoc))));
let nb = new NodeBuilder();
nb.startElement("foo");
nb.addText("baz");
nb.endElement();
newDoc = mem.nodeReplace(fn.head(newDoc.xpath("/root/foo")), nb.toNode());
//xdmp.documentInsert(newUri, newDoc);
newDoc
HTH!