Updating JSON arrays in MarkLogic 9

1.1k views Asked by At

I'm having trouble working out how to write a bit of XQuery. I have a JSON structure in MarkLogic that looks like:

{
    "id": "pres003A10",
    "title": "A Course About Something",
    "description": "This course teaches people about some things they may not know.",
    "author": "A.N. Author",
    "updated": "2007-01-19",
    "decks": [
      {
        "id":"really-basic-stuff",
        "exclude": ["slide3", "slide12"]
      },
      {
        "id":"cleverer-stuff",
        "exclude": []
      }
    ]           
  }

The exclude array contains the identifiers for slides in decks (presentations are made up of one or more decks of slides). I'm trying to write a piece of code that will look for a slide id in that exclude list and remove it if present or add it if not (a toggle).

I can obtain the array node itself using:

let $exclude := doc('/presentations/presentation.json')/object-node()/decks[id = 'markup-intro']/array-node('exclude')

but I can't for the life of me see how I then update that array to either remove an item or add it. The intention is call a function something like:

local:toggle-slide($presentation) as object-node()
{
   (: xdmp:node-update(...) goes here :)
};

So, how do I update that array?

2

There are 2 answers

2
grtjn On BEST ANSWER

If you need to update the json in the database, you can use xdmp:node-replace. The catch with node-replace is though, that you have to feed it with a named node. To do that, you need to wrap the array-node in an object-node, and then grab the array-node inside the object-node on the fly. Here a working example:

xquery version "1.0-ml";

(: insert test data :)
xdmp:document-insert("/presentations/presentation.json", xdmp:unquote('{
    "id": "pres003A10",
    "title": "A Course About Something",
    "description": "This course teaches people about some things they may not know.",
    "author": "A.N. Author",
    "updated": "2007-01-19",
    "decks": [
      {
        "id":"markup-intro",
        "exclude": ["slide3", "slide12"]
      },
      {
        "id":"cleverer-stuff",
        "exclude": []
      }
    ]           
  }'
))
;

(: node-replace array-node :)
let $exclude := doc('/presentations/presentation.json')/object-node()/decks[id = 'markup-intro']/array-node('exclude')
return xdmp:node-replace($exclude, object-node{
  "exclude": array-node{ "other", "slides" }
}/node())
;

(: view if changed :)
doc('/presentations/presentation.json')

Note: consider looking at MarkLogic's Server-side JavaScript (SJS) support. Updating JSON might seem more natural that way, particularly if you need to make multiple changes in one go.

HTH!

1
ehennum On

In memory JSON node trees (and XML trees, for that matter) are immutable.

The way to modify a tree is to construct a new tree, copying the nodes that haven't changed and creating the parent node and ancestor node with the changes.

That said, there's an easier way to modify JSON. If you call xdmp:from-json() on the root node, you will get a mutable in-memory map / array structure.

You can then navigate to the array using map:get() on the maps and [ITEM_NUMBER] on the arrays and delete or insert items FOR the appropriate json:array object.

When you're done, call xdmp:to-json() to turn the root map back into a node.

Hoping that helps,