Search code examples
marklogicmarklogic-10

MarkLogic xdmp.nodeInsertChild how to insert an array in json object


I have the following scaffold code with will insert nodes inside an existing object in database. The code assumes that any fields present in xpath sequence is update or it is an insert into the parentNode. This works for simple objects but if you try to insert an array through nodeBuilder it throws an exception on xdmp.insertNodeChild. It does work if the node uses an update statement. Is this a bug or am I missing something

{
"saying": {
"quote": "I'd rather regret the things I've done than regret the things I haven't done", 
"person": "Lucille Ball", 
"born": "Jamestown, New York, USA", 
"dob": "1911-08-05", 
"category": ["fun", "inspirational"], 
   "likes": 3, 
   "foo": "bar", 
   "babson": [1, 2, 3]
  }
}

Here is the code I tries to run you can change babson to babo and will throw an exception. Running with babson as an update works as expected

'use strict';
declareUpdate();
let node = new NodeBuilder();
let nb = node.addNode({"likes": 3,"dob":"1911-08-05","foo":"bar","babson": [1,2,3]}).toNode();

let props = []
for(const [key, value] of Object.entries(nb)) {
  props.push(key)
}
let doc = cts.doc("/sayings/lb_regret.json")

//console.log(`======${xdmp.random()}======`)

for(let prop of props) {
  let propPath = `/saying/${prop}`
  let propSeq = doc.xpath(propPath)
  if(propSeq.toArray().length !== 0) {
    xdmp.nodeReplace(propSeq,nb[prop])
  } else {
    let cnb = new NodeBuilder()
    let child = {}
    child[prop] = nb[prop];
    let childNode = cnb.addNode(child)
    xdmp.nodeInsertChild(doc.xpath('/saying'), childNode.toNode().xpath(`/${prop}`))
  }
}

if I try to insert an array it throws the following error

[javascript] XDMP-ARGTYPE: xdmp.nodeInsertChild(Sequence(xdmp.unpath("fn:doc('/sayings/lb_regret.json')/saying")), Sequence(NumberNode(1), NumberNode(2), NumberNode(3))) -- arg2 is not of type Node

Stack Trace
In undefined on line 24
In xdmp.nodeInsertChild(Sequence(xdmp.unpath("fn:doc('/sayings/lb_regret.json')/saying")), Sequence(NumberNode(1), NumberNode(2), NumberNode(3)))

Solution

  • The reason you are getting that error is because of a subtle difference of how XPath against a JavaScript array works in MarkLogic.

    When you use an XPath like:

    /saying/babson
    

    it returns a sequence of items that are in that array, but not the array node itself. That is why the error says "arg2 is not of type Node", because it's actually a sequence of Number nodes.

    If you want to select and return the array-node() then you need to use the array-node() and specify the name:

    /saying/array-node('babson')
    

    And you can use the more generic node() selector as you iterate over the named properties:

    for (let prop of props) {
      let propPath = `/saying/node('${prop}')`
      let propSeq = doc.xpath(propPath)
      xdmp.nodeReplace(propSeq, nb[prop])
    }