Skip to main content

Getting Started with Optic

To Update Parts of Document Content

[v11.2.0 and up]

You can update parts of document content such as changing a specific value or adding a new property. You can do this without needing to provide any of the other data or metadata that you are not updating.

We want to change the employee document Status property from Active - Regular Exempt (Full-time) to Terminated for each employee terminated so far.

We also want to add a property: TerminationDate.

An Optic update like this one finds all the documents in a collection then replaces one of the values and adds a new property and value to each document found:

declareUpdate();
const op = require('/MarkLogic/optic');
op.fromDocUris(cts.collectionQuery('https://example.com/content/department/Attrition'))
  .joinDocCols(null, op.fragmentIdCol('fragmentId'))
  .patch(
    op.col('doc'),
    op.patchBuilder('/')
      .replaceValue("Status", "Terminated")
      .insertAfter("HiredDate", xdmp.toJSON({"TerminationDate": fn.currentDate()}).root.TerminationDate)
  )
  .write()
  .execute();

We used this update to replace the Status value and add the TerminationDate property--with the current date as its value--after the existing HiredDate property in all documents in the Attrition collection:

  • The Data Accessor Function fromDocUris() produces a row for each document with the column uri containing the document URIs and the column fragmentId contaning the document root Fragment ID:

    • The CTS Function cts.collectionQuery() matches documents in the specified collection, our Attrition collection.

    • With this CTS function as a parameter, fromDocUris() produces a row for each matched document. Each row has a uri column containing the URIs for the documents that we want to update: the employee documents currently in the Attrition collection. Each row also has a fragmentID column containing the Fragment ID of each document.

  • The Operator Function joinDocCols() retrieves the actual document.

    • null tells joinDocCols() to use default column names such as doc for document content.

    • fragmentIdCol() matches the value in joinDocCols()'s fragmentId column with the value in fromDocUri()'s fragmentId column.

  • The Operator Function patch() applies the patch, once it is built, to the specified areas of each document in memory:

    • col() identifies the column in its argument.

    • The Operator Function patchBuilder() defines a patch consisting of one or more patch builder plan functions:

      • / is an XPath expression that selects the root node of the document.

      • The Patch Builder Plan Function replaceValue() defines the first change to be applied: Changing the employee status property to terminated:

        • Status is an XPath expression selecting the property to update.

        • Terminated is the new value for that property.

      • The Patch Builder Plan Function insertAfter() defines the second change to be applied: Adding an employee termination date property with fn.currentDate() (today's date) determining its value:

        • HiredDate is an XPath expression identifying the sibling property after which to add the new property.

        • xdmp.toJSON() converts the new native JavaScript Object { "Termination":"<current date>" } into a MarkLogic DocumentObject.

        • .root.TerminationDate accesses the TerminationDate object node property to insert.

  • The Operator Function write() inserts each document, by default giving it the URI in the uri column created by the latest data accessor function.

  • The Executor Function execute() executes the update without returning any rows.

  • Optic data accessor functions always pull the document root Fragment ID into a fragmentId column. However, operator functions do not process that column and executor functions do not produce that column as part of the result unless a preceding operator function explicitly defines that column.

  • To test the update before writing the document, replace write() and execute() with result() to return the rows that fromDocDescriptors() has rendered.

  • You cannot insert a child on an existing property whose value is null.

  • You cannot add another attribute with the same name to an element.

  • You cannot add a JSON property of the same name under the same parent property.

  • A document cannot have more than one root. Therefore, you cannot insert a property before or after the root of a document.

  • You cannot add an XML element or attribute node to a JSON document nor a JSON property or array node to an XML document.

  • Each Patch Builder Function applies to all items matching the provided XPath expression:

    • If it matches more than one node in a document, then the patch applies to all instances.

    • If it matches no property in the document, no changes are applied.

The documents in the Attrition collection now have the old property Status updated to Terminated and the new property TerminationDate containing today's date.

  • To return rows, use result() instead of execute().