Objects). JSON nodes are what are persisted in the database. They map one-to-one with the JSON data model and are immutable. When you get a document out the database it’s an instance of a node, literally
xdmp.toJSON(obj) or from a string using
NaN, that can’t be represented in JSON. Functions like
xdmp.toJSON(obj). To get an object from a node, use the
To see an example of this, let's first put a document into our database:
When we retrieve the document, we can can convert it from a node to an object, allowing us to make changes to it in memory. We can then persist those changes by writing them back to the database.
To summarize, the typical pattern for updating a document in the database is:
declareUpdate()at the top of your module.
- Get a
Documentfrom something like
cts:doc()or looping over
cts:search()(see "Iterators" below).
- Convert the
- Change the object instance like you would any other object, e.g.
obj.prop = "new"or
- Write the updated object back to the database using
xdmp.documentInsert(). It handles the conversion of the object instance to a
Nodejust as if you’d explicitly called
xdmp.toJSON()on the object.
Most functions that read data from the database return an instance of a
ValueIterator, rather than a full instantiation of the data. This allows the evaluation environment to lazily and asynchronously load data as it’s required, rather than up-front. Use the
.toArray() function on a
ValueIterator instance to eagerly load the entire contents into a new array.
ValueIterator interface implements the ECMAScript 6
Iterators interface. Like any iterator, you can loop through a
ValueIterator using a
for…of loop. Note that the
for…of loop is not at all the same as a
for…in loop, which loops over an object’s properties.
That’s a shorter way of explicitly using the
Generators are closely related to Iterators and are also part of ECMAScript 6. Generators are a special kind of function, which may be paused in the middle, once or many times, and resumed later, allowing other code to run during these paused periods. This is an incredibly powerful concept, especially paired with iterators, allowing you to lazily work with sequences of indeterminate length, such as those returned from
declareUpdate() function. This function must be called before the first update. In general, putting
declareUpdate() as the first statement is best practice, making it clear the reader that this module will make updates.
require() function and assigning the exports to module-level global variables, which behave like namespaces.
A main module imports a library module into a variable.
The library module declares the functions and variables that it exports.
- Import paths and precedence: The global
require()function looks up module paths according to a well-defined set of rules, coincidently shared by the XQuery engine. When a module starts with a slash (
C:\on Windows), MarkLogic will look for a file on the specified path relative to the
Modulesfile-system directory of the MarkLogic installation. If the module is not found there, it will look relative to the app server root for both file-system- and modules database-backed configurations. If the import path does not start with a slash, the path is resolved relative to the location of the module calling the
.sjsand XQuery (
application/vnd.marklogic-xdmp) defaults to any one of
- Export scope: It is possible to change the state of an exported variable in MarkLogic. However, that change only persists for the request in which it was made. Thus subsequent requests and other hosts won’t have access to modified exports. Imported modules are cached, but their state does not persist beyond the scope of an individual request. In general, changing exports at runtime is not a best practice because it makes code more difficult to reason and to be reflected globally would have to happen in every importing module. This is different than how exports work in Node.js. In Node.js, export state is global and changes are reflected in subsequent requests to the same Node process.