如何在 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!