You can configure MarkLogic Server to manage and query temporal data.
This chapter walks you through the procedures for configuring the Documents
database to store temporal documents and for inserting and querying temporal documents. The following are the main sections:
The valid and system axis each make use of metadata fields that define the start and end times. For example, the following query creates the metadata fields to be used to store the valid and system axes.
var admin = require("/MarkLogic/admin.xqy"); var config = admin.getConfiguration(); var dbid = xdmp.database("Documents"); var validStart = admin.databaseMetadataField("validStart"); var validEnd = admin.databaseMetadataField("validEnd"); var systemStart = admin.databaseMetadataField("systemStart"); var systemEnd = admin.databaseMetadataField("systemEnd"); config = admin.databaseAddField(config, dbid, validStart); config = admin.databaseAddField(config, dbid, validEnd); config = admin.databaseAddField(config, dbid, systemStart); config = admin.databaseAddField(config, dbid, systemEnd); admin.saveConfiguration(config);
xquery version "1.0-ml"; import module namespace admin = "http://marklogic.com/xdmp/admin" at "/MarkLogic/admin.xqy"; let $config := admin:get-configuration() let $dbid := xdmp:database("Documents") let $fieldspec1 := admin:database-metadata-field("validStart") let $fieldspec2 := admin:database-metadata-field("validEnd") let $fieldspec3 := admin:database-metadata-field("systemStart") let $fieldspec4 := admin:database-metadata-field("systemEnd") for $fieldspec in ($fieldspec1, $fieldspec2, $fieldspec3, $fieldspec4) let $new-config := admin:database-add-field($config, $dbid, $fieldspec) return admin:save-configuration($new-config)
The valid and system axis each make use of dateTime
range field indexes that define the start and end times. For example, the following query creates the field range indexes to be used to create the valid and system axes.
var admin = require("/MarkLogic/admin.xqy"); var config = admin.getConfiguration(); var dbid = xdmp.database("Documents"); var validStart = admin.databaseRangeFieldIndex( "dateTime", "validStart", "", fn.true() ); var validEnd = admin.databaseRangeFieldIndex( "dateTime", "validEnd", "", fn.true() ); var systemStart = admin.databaseRangeFieldIndex( "dateTime", "systemStart", "", fn.true() ); var systemEnd = admin.databaseRangeFieldIndex( "dateTime", "systemEnd", "", fn.true() ); config = admin.databaseAddRangeFieldIndex(config, dbid, validStart); config = admin.databaseAddRangeFieldIndex(config, dbid, validEnd); config = admin.databaseAddRangeFieldIndex(config, dbid, systemStart); config = admin.databaseAddRangeFieldIndex(config, dbid, systemEnd); admin.saveConfiguration(config);
xquery version "1.0-ml"; import module namespace admin = "http://marklogic.com/xdmp/admin" at "/MarkLogic/admin.xqy"; let $config := admin:get-configuration() let $dbid := xdmp:database("Documents") let $rangespec1 := admin:database-range-field-index( "dateTime", "validStart", "", fn:true() ) let $rangespec2 := admin:database-range-field-index( "dateTime", "validEnd", "", fn:true() ) let $rangespec3 := admin:database-range-field-index( "dateTime", "systemStart", "", fn:true() ) let $rangespec4 := admin:database-range-field-index( "dateTime", "systemEnd", "", fn:true() ) for $rangespec in ($rangespec1, $rangespec2, $rangespec3, $rangespec4) let $new-config := admin:database-add-range-field-index( $config, $dbid, $rangespec) return admin:save-configuration($new-config)
On the Documents database, create two axes, named valid and system, each to serve as a container for a named pair of field range indexes.
var temporal = require("/MarkLogic/temporal.xqy"); var validResult = temporal.axisCreate( "valid", cts.fieldReference("validStart", "type=dateTime"), cts.fieldReference("validEnd", "type=dateTime")); var systemResult = temporal.axisCreate( "system", cts.fieldReference("systemStart", "type=dateTime"), cts.fieldReference("systemEnd", "type=dateTime"));
xquery version "1.0-ml"; import module namespace temporal = "http://marklogic.com/xdmp/temporal" at "/MarkLogic/temporal.xqy"; temporal:axis-create( "valid", cts:field-reference("validStart", "type=dateTime"), cts:field-reference("validEnd", "type=dateTime")); xquery version "1.0-ml"; import module namespace temporal = "http://marklogic.com/xdmp/temporal" at "/MarkLogic/temporal.xqy"; temporal:axis-create( "system", cts:field-reference("systemStart", "type=dateTime"), cts:field-reference("systemEnd", "type=dateTime"))
Create a temporal collection, named kool, that uses the previously created system and valid axes.
var temporal = require("/MarkLogic/temporal.xqy"); var collectionResult = temporal.collectionCreate( "kool", "system", "valid");
xquery version "1.0-ml"; import module namespace temporal = "http://marklogic.com/xdmp/temporal" at "/MarkLogic/temporal.xqy"; temporal:collection-create("kool", "system", "valid")
Insert some documents into the temporal collection. In this example, a stock trader, John, places an order to buy some stock. The record of the trade is stored as a bi-temporal document, as follows:
declareUpdate(); var temporal = require("/MarkLogic/temporal.xqy"); var root = {"tempdoc": {"trader": "John", "price": 12} }; var options = {metadata: {validStart: "2014-04-03T11:00:00", validEnd: "9999-12-31T11:59:59Z"} }; temporal.documentInsert("kool", "koolorder.json", root, options);
xquery version "1.0-ml"; import module namespace temporal = "http://marklogic.com/xdmp/temporal" at "/MarkLogic/temporal.xqy"; let $root := <tempdoc> <trader>John</trader> <content>12</content> </tempdoc> let $options := <options xmlns="xdmp:document-insert"> <metadata> <map:map xmlns:map="http://marklogic.com/xdmp/map"> <map:entry key="validStart"> <map:value>2014-04-03T11:00:00</map:value> </map:entry> <map:entry key="validEnd"> <map:value>9999-12-31T11:59:59Z</map:value> </map:entry> </map:map> </metadata> </options> return temporal:document-insert("kool", "koolorder.xml", $root, $options)
You can use xdmp.documentGetMetadata to display the metadata for a temporal document. For example: xdmp.documentGetMetadata("koolorder.json")
declareUpdate(); var temporal = require("/MarkLogic/temporal.xqy"); var root = {"tempdoc": {"trader": "John", "price": "13"} }; var options = {metadata: {validStart: "2014-04-03T11:30:00", validEnd: "9999-12-31T11:59:59Z"} }; temporal.documentInsert("kool", "koolorder.json", root, options);
xquery version "1.0-ml"; import module namespace temporal = "http://marklogic.com/xdmp/temporal" at "/MarkLogic/temporal.xqy"; let $root := <tempdoc> <trader>John</trader> <content>13</content> </tempdoc> let $options := <options xmlns="xdmp:document-insert"> <metadata> <map:map xmlns:map="http://marklogic.com/xdmp/map"> <map:entry key="validStart"> <map:value>2014-04-03T11:30:00</map:value> </map:entry> <map:entry key="validEnd"> <map:value>9999-12-31T11:59:59Z</map:value> </map:entry> </map:map> </metadata> </options> return temporal:document-insert("kool", "koolorder.xml", $root, $options)
The result should be three documents with valid and system times as shown in the graphic below. Note that the second query resulted in a split on the Original document that resulted in a split document, as well as Version 2 that contains the new content.
The following query searches the temporal documents, using the cts:period-range-query function to locate the documents that were in the database between 11:10 and 11:15. ISO_CONTAINS
is one of the comparison operators described in ISO SQL 2011 Operators.
In this example, only the Original Document meets the search criteria.
cts.search(cts.periodRangeQuery( "system", "ISO_CONTAINS", cts.period(xs.dateTime("2014-04-03T11:10:00"), xs.dateTime("2014-04-03T11:15:00")) ));
xquery version "1.0-ml"; cts:search(fn:doc(), cts:period-range-query( "system", "ISO_CONTAINS", cts:period(xs:dateTime("2014-04-03T11:10:00"), xs:dateTime("2014-04-03T11:15:00")) ))
The following query searches the temporal documents, using the cts:period-range-query function to locate the documents that have a valid time period that starts after 10:30 and ends at 11:30. ALN_FINISHES
is one of the comparison operators described in Allen Operators.
In this example, only the Split document meets the search criteria.
cts.search(cts.periodRangeQuery( "valid", "ALN_FINISHES", cts.period(xs.dateTime("2014-04-03T10:30:00"), xs.dateTime("2014-04-03T11:30:00")) ));
xquery version "1.0-ml"; cts:search(fn:doc(), cts:period-range-query( "valid", "ALN_FINISHES", cts:period(xs:dateTime("2014-04-03T10:30:00"), xs:dateTime("2014-04-03T11:30:00")) ))
The following query searches the temporal documents, using the cts:period-range-query function to locate the documents that were in the database after 11:20. ALN_AFTER
is one of the comparison operators described in Allen Operators.
In this example, both the Split and Version 2 documents meet the search criteria.
cts.search(cts.periodRangeQuery( "system", "ALN_AFTER", cts.period(xs.dateTime("2014-04-03T11:00:00"), xs.dateTime("2014-04-03T11:20:00")) ));
xquery version "1.0-ml"; cts:search(fn:doc(), cts:period-range-query( "system", "ALN_AFTER", cts:period(xs:dateTime("2014-04-03T11:00:00"), xs:dateTime("2014-04-03T11:20:00")) ))
The following query searches the temporal documents, using the cts:period-compare-query function to locate the documents that were in the database when the valid time period is within the system time period. ISO_CONTAINS
is one of the comparison operators described in ISO SQL 2011 Operators.
In this example, only Version 2 meets the search criteria.
cts.search(cts.periodCompareQuery( "system", "ISO_CONTAINS", "valid" ))
xquery version "1.0-ml"; cts:search(fn:doc(), cts:period-compare-query( "system", "ISO_CONTAINS", "valid" ))
The following query uses the cts:and-query to AND two cts:collection-query functions to return the temporal document that is in the URI collection, koolorder.xml
, and the latest
collection.
In this example, Ver2 meets the search criteria.
cts.search(cts.andQuery([ cts.collectionQuery("koolorder.json"), cts.collectionQuery("latest")]))
xquery version "1.0-ml"; cts:search(fn:doc(), cts:and-query(( cts:collection-query(("koolorder.xml")), cts:collection-query(("latest")))))