2
votes

I have a requirement where i need to return the full document at last after all the process completes.

My Last statement is having an xdmp:node-replace() and hence it is returning an empty sequence.

The nodes are getting replaced in my final document but i am not able to see them on console in 1st run. When i am running it second time then i can able to see the replaced node.

Here is the sample code-

abc.xml--->
  <root>
       <id>abcd</id>
  </root>

let $doc := doc("abc.xml")
 (: Let $doc is having an Id node :)

let $replace := xdmp:node-replace($doc//id,<id>1234</id>)
  return $doc

Actual Output-->
 <root>
       <id>abcd</id>
  </root>

 Expected Output-->
   <root>
       <id>1234</id>
  </root>

If i will return $replace then it will give me empty sequence

I want to return the expected output in 1st run

Any Suggestions ?

3

3 Answers

3
votes

xdmp:node-replace replaces nodes in database, not in-memory. Also you cannot see database updates before a commit.

A simple solution would be to create a updated in-memory document and return that.

xquery version "1.0-ml";

xdmp:document-insert("abc.xml",
<root>
  <id>abcd</id>
  <name>Test</name>
</root>
);

let $doc := doc("abc.xml")
let $update := 
<root>
    <id>1234</id>
    {$doc/root/name}
</root>

return (
  xdmp:document-insert("abc.xml", $update), 
  $update
)

Edit:

Alternative using a in-memory replace function:

xquery version "1.0-ml";

import module namespace mem = "http://xqdev.com/in-mem-update" at '/MarkLogic/appservices/utils/in-mem-update.xqy';

let $doc := doc("abc.xml")
let $updatedDoc := mem:node-replace($doc//id, <id>1234</id>)

return (
  xdmp:node-replace($doc, $updatedDoc),
  $updatedDoc
)

Performance-wise i think mine and Mads Hansen's alternatives share the same performance characteristics. I'd say choose whatever you like most. I have used my approach for simple update use cases alot, for more complicated use cases which might also involve moving nodes or so, i'd rather use Mads Hansen's XSLT alternative.

2
votes

Another alternative for modifying in-memory would be via XSLT. Using an identity transformation, create templates to match the node(s) that you want to change:

xquery version "1.0-ml";
declare variable $XSLT := 
  <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="2.0">

    <xsl:template match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="id/text()">
        <xsl:text>1234</xsl:text>
    </xsl:template>

  </xsl:stylesheet>;

let $doc := doc("abc.xml")
let $modified := xdmp:xslt-eval($XSLT, $doc)
return 
  ( xdmp:node-replace($doc, $modified), $modified )
1
votes

You can use a multi-statement transaction, using a semi-colon to separate the write transaction from the read transaction, so that you can see the update.

xquery version "1.0-ml";
xdmp:node-replace(doc("abc.xml")//id, <id>1234</id>)
;

xquery version "1.0-ml";
doc("abc.xml")