3

So I have an interesting problem, assume I have this document (example.xml) inside a MarkLogic database:

<Enrolls>
  <Enroll>
    <Status> Active </Status>
    <boom> boom2 </boom>
  </Enroll>
    <Enroll>
    <Status> Active </Status>
    <boom> boom </boom>
  </Enroll>
  <Enroll>
    <Status> Inactive </Status>
    <boom> boom </boom>
  </Enroll>
</Enrolls>

I want to replace all the "Active" Enroll elements with one node, so essentially my end result for this should be:

<Enrolls>
  <boom> boom for the actives </boom>
  <Enroll>
    <Status> Inactive </Status>
    <boom> boom </boom>
  </Enroll>
</Enrolls>

To get this done, this is the code I wrote:

xdmp:node-replace((doc("example.xml")/Enrolls/Enroll[Status eq " Active "]), <boom> boom for the actives </boom>)

But this is the result I get:

<Enrolls>
  <boom> boom for the actives </boom>
  <boom> boom for the actives </boom>
  <Enroll>
    <Status> Inactive </Status>
    <boom> boom </boom>
  </Enroll>
</Enrolls>

The code replaces each active enroll with the same node I specified to replace. I want it to replace both the nodes at the same time with only one node. How can I do that and get the result I want?

Andy Chan
  • 133
  • 7

1 Answers1

3

Consider doing a xdmp:node-delete on the active ones, and a separate xdmp:node-insert-child on the parent.

for $active in doc("example.xml")/Enrolls/Enroll[Status eq " Active "]
return 
  if ($active/following-sibling::Enroll[Status eq " Active "])
  then xdmp:node-delete($active)
  else xdmp:node-replace($active, <boom> boom for the actives </boom>)

Or do a xdmp:node-replace on the first, and xdmp:node-delete's on the others. You should be able to do all that in one request, so it would be just one commit.

let $enrolls := doc("example.xml")/Enrolls
return ( 
  $enrolls/Enroll[Status eq " Active "]/xdmp:node-delete(.),
  xdmp:node-insert-child($enrolls, <boom> boom for the actives </boom>)
)

You could also rebuild the parent node, and replace that in its entirety. That might be easier to reason with, and would likely be similar in performance.

let $enrolls := doc("example.xml")/Enrolls
return 
  xdmp:node-replace($enrolls, 
    <Enrolls>
      <boom> boom for the actives </boom>
      {$enrolls/* except $enrolls/Enroll[Status eq " Active "]}
    </Enrolls>)
Mads Hansen
  • 63,927
  • 12
  • 112
  • 147
grtjn
  • 20,254
  • 1
  • 24
  • 35
  • But is it possible at all to do this operation within one xdmp:node-replace() or is there some superman "copy, delete, and paste" function that exists in XQuery that can do it for me? I only want to read into disk one time, having to read in disk twice would be a pain. – Andy Chan Mar 05 '20 at 16:40
  • You can create a function that uses either of the code snippets in the answer to make your own superman copy, delete, and paste function. A 4th option would be to apply an XSLT – Mads Hansen Mar 05 '20 at 21:36
  • As mentioned above, and shown by the code snippets from Mads, you can do multiple node operations in one request, and they get combined in one commit automatically. No need to do the node operations in multiple requests. – grtjn Mar 06 '20 at 12:17