cts:document-root-query()

Released in MarkLogic 11

personClever Llamas
calendar_today2023-02-08

Before this feature, we could do a few things when querying about an element name:

  • cts:element-query(xs:XName("my-element"), cts:true-query()). However, this does not say if the element exists as root - just that it exists.
  • You could do a cts:search(doc()/my-element, cts:true-query(), "unfiltered") with the first param using an xPath to the root directory. However, this proves to be inefficient and could lead to filling the expanded tree cache. Furthermore, in server-side javascript, that is not possible.
  • You could create a TDE template with a context of / and then create a column or triple with the root node name. However, this only works for XML and not JSON.

To test the feature fully, we set up some sample documents including enough to run some negative tests.

Initial Setup

  • XML with an expected root node
  • JSON - with a single top level property with expected same name as root node
  • XML with a different root node
  • JSON with a single top level property with a different name
xquery version "1.0-ml";

let $permissions := xdmp:default-permissions((), "objects")
let $options := 	 map:entry("permissions", $permissions) => map:with("collections", "/clever-llamas/test/cts-document-root-query")
return(
  xdmp:document-insert("/clever-llamas/test/cts-document-root-query/sample-llama.json", xdmp:unquote('{ llama : { species : "Suri Llama"}}'), $options),
  xdmp:document-insert("/clever-llamas/test/cts-document-root-query/sample-llama.xml", element llama { element species {"Suri Llama"}}, $options),
  xdmp:document-insert("/clever-llamas/test/cts-document-root-query/sample-not-llama.json", xdmp:unquote('{ bird : { species : "Emu"}}'), $options),
  xdmp:document-insert("/clever-llamas/test/cts-document-root-query/sample-not-llama.xml", element bird { element species {"Emu"}}, $options)
);

fn:collection("/clever-llamas/test/cts-document-root-query") !function ($doc){(xdmp:node-uri($doc), $doc)}(.)

List of documents from setup script

Test

Simple test of the feature:

xquery version "1.0-ml";

let $query := cts:and-query((
  cts:collection-query("/clever-llamas/test/cts-document-root-query"),
  cts:document-root-query(xs:QName("llama"))
))

return for $uri in cts:uris((), (), $query)
  return ($uri, fn:doc($uri))

Result:Result showing expected documents

In this case, things look like they are working perfectly. However, we were still contemplating the concept of a query for the "document root" against a JSON document. Having already had interesting experiences with JSON and TDE templates with a root context (/), it seemed prudent to dive a bit deeper.

Let's look at what MarkLogic returns when we access the root node of a JSON document:

xquery version "1.0-ml";
doc("/clever-llamas/test/cts-document-root-query/sample-llama.json")/node() ! xdmp:node-kind(.)

Result:Result showing node-kind = Object

There is no single "root" element in a JSON document that has a name. We tested some more:

Setup 2

We added a document with 2 different top-level properties

xquery version "1.0-ml";

let $permissions := xdmp:default-permissions((), "objects")
let $options := 	 map:entry("permissions", $permissions) => map:with("collections", "/clever-llamas/test/cts-document-root-query")
return  xdmp:document-insert("/clever-llamas/test/cts-document-root-query/sample-more-than-llamas.json", xdmp:unquote('{ llama : { species : "Suri Llama"}, snake : { species : "Amazon Tree Boa"}}'), $options)
;

fn:doc("/clever-llamas/test/cts-document-root-query/sample-more-than-llamas.json") !function ($doc){(xdmp:node-uri($doc), $doc)}(.)

Document with 2 properties

When we run the same test again:

xquery version "1.0-ml";

let $query := cts:and-query((
  cts:collection-query("/clever-llamas/test/cts-document-root-query"),
  cts:document-root-query(xs:QName("llama"))
))

return for $uri in cts:uris((), (), $query)
  return ($uri, fn:doc($uri))

Result:Document with 2 JSON properties also returned

This result shows, that for JSON documents, MarkLogic looks at the name of any top level property when resolving cts:document-root-query().

We did test further to ensure that XML nodes are only matching root level and that JSON is only matching top level properties by taking our set as above and just nesting them one level deeper.

Conclusion

For XML, this feature work as one would expect - resolves root nodes

However, for JSON, it will resolve against any top-level property with that name. This may seem logical to some and odd to others and give unexpected results. Of course, if you model your JSON content and implement the concept of a named root by always having one-and-only one top level property, then it would work the same as XML.

Need Some Help?


Looking for more information on this subject or any other topic related to MarkLogic? Contact Us (info@cleverllamas.com) to find out how we can assist you with consulting or training!