Loading TOC...
Application Developer's Guide (PDF)

Application Developer's Guide — Chapter 6

Library Services Applications

This chapter describes how to use Library Services, which enable you to create and manage versioned content in MarkLogic Server in a manner similar to a Content Management System (CMS). This chapter includes the following sections:

Understanding Library Services

The Library Services enable you to create and maintain versions of managed documents in MarkLogic Server. Access to managed documents is controlled using a check-out/check-in model. You must first check out a managed document before you can perform any update operations on the document. A checked out document can only be updated by the user who checked it out; another user cannot update the document until it is checked back in and then checked out by the other user.

Documents must be stored in a database to be versioned. If a document is created by a CPF application, such as entity enrichment, modular documents, conversion, or a custom CPF application, then the document will only be versioned if the CPF application uses Library Services to insert it into the database. By default, the CPF applications supplied by MarkLogic do not create managed documents.

When you initially put a document under Library Services management, it creates Version 1 of the document. Each time you update the document, a new version of the document is created. Old versions of the updated document are retained according to your retention policy, as described in Defining a Retention Policy.

The Library Services include functions for managing modular documents so that various versions of linked documents can be created and managed, as described in Managing Modular Documents in Library Services.

The following diagram illustrates the workflow of a typical managed document. In this example, the document is added to the database and placed under Library Services management. The managed document is checked out, updated several times, and checked in by Jerry. Once the document is checked in, Elaine checks out, updates, and checks in the same managed document. Each time the document is updated, the previous versions of the document are purged according to the retention policy.

Building Applications with Library Services

The Library Services API provides the basic tools for implementing applications that store and extract specific drafts of a document as of a particular date or version. You can also use the Library Services API, along with the other MarkLogic Server APIs, to provide structured workflow, version control, and the ability to partition a document into individually managed components. The security API provides the ability to associate user roles and responsibilities with different document types and collections. And the search APIs provide the ability to implement powerful content retrieval features.

Required Range Element Indexes

The range element indexes shown in the table and figure below must be set for the database that contains the documents managed by the Library Services. These indexes are automatically set for you when you create a new database. However, if you want to enable the Library Services for a database created in an earlier release of MarkLogic Server, you must manually set them for the database.

Scalar Type Namespace URI Local Name Range Value Position
dateTime http://marklogic.com/xdmp/dls created false
unsignedLong http://marklogic.com/xdmp/dls version-id false

Library Services API

This section describes the Library Services API and contains the following sections:

Library Services API Categories

The Library Services functions are described in the MarkLogic XQuery and XSLT Function Reference. The Library Services functions fall into the following categories:

Managed Document Update Wrapper Functions

All update and delete operations on managed documents must be done through the Library Services API. The Library Services API includes the following wrapper functions that enable you to make the same updates on managed documents as you would on non-managed document using their XDMP counterparts:

Security Considerations of Library Services Applications

There are two pre-defined roles designed for use in Library Services applications, as well as an internal role that the Library Services API uses:

  • dls-admin Role
  • dls-user Role
  • dls-internal Role

    Do not log in with the Admin role when inserting managed documents into the database or when testing your Library Services applications. Instead create test users with the dls-user role and assign them the various permissions needed to access the managed documents. When testing your code in Query Console, you must also assign your test users the qconsole-user role.

dls-admin Role

The dls-admin role is designed to give administrators of Library Services applications all of the privileges that are needed to use the Library Services API. It has the needed privileges to perform operations such as inserting retention policies and breaking checkouts, so only trusted users (users who are assumed to be non-hostile, appropriately trained, and follow proper administrative procedures) will be granted the dls-admin role. Assign the dls-admin role to administrators of your Library Services application.

dls-user Role

The dls-user role is a minimally privileged role. It is used in the Library Services API to allow regular users of the Library Services application (as opposed to dls-admin users) to be able to execute code in the Library Services API. It allows users, with document update permission, to manage, checkout, and checkin managed documents.

The dls-user role only has privileges that are needed to run the Library Services API; it does not provide execute privileges to any functions outside the scope of the Library Services API. The Library Services API uses the dls-user role as a mechanism to amp more privileged operations in a controlled way. It is therefore reasonably safe to assign this role to any user whom you trust to use your Library Services application. Assign the dls-user role to all users of your Library Services application.

dls-internal Role

The dls-internal role is a role that is used internally by the Library Services API, but donot explicitly grant it to any user or role. This role is used to amp special privileges within the context of certain functions of the Library Services API. Assigning this role to users would give them privileges on the system that you typically do not want them to have; do not assign this role to any users.

Transactions and Library Services

The dls:document-checkout, dls:document-update, and dls:document-checkinfunctions must be executed in separate transactions. If you want to complete a checkout, update, and checkin in a single transaction, use the dls:document-checkout-update-checkin function.

Putting Documents Under Managed Version Control

In order to put a document under managed version control, it must be in your content database. Once the document is in the database, users assigned the dls-user role can use the dls:document-manage function to place the document under management. Alternatively, you can use the dls:document-insert-and-manage function to both insert a document into the database and place it under management.

When inserting a managed document, specify at least read and update permissions to the roles assigned to the users that are to manage the document. If no permissions are supplied, the default permissions of the user inserting the managed document are applied. The default permissions can be obtained by calling the xdmp:default-permissions function. When adding a collection to a document, as shown in the example below, the user will also need the unprotected-collections privilege.

For example, the following query inserts a new document into the database and places it under Library Services management. This document can only be read or updated by users assigned the writer and/or editor role and have permission to read and update the http://marklogic.com/engineering/specs collection.

(: Insert a new managed document into the database. :)
xquery version "1.0-ml";
import module namespace dls = "http://marklogic.com/xdmp/dls" 
        at "/MarkLogic/dls.xqy";
dls:document-insert-and-manage(
        "/engineering/beta_overview.xml",
        fn:true(),
        <TITLE>Project Beta Overview</TITLE>,
        "Manage beta_overview.xml",
        (xdmp:permission("writer", "read"),
         xdmp:permission("writer", "update"), 
         xdmp:permission("editor", "read"),
         xdmp:permission("editor", "update")),
        ("http://marklogic.com/engineering/specs"))

Checking Out Managed Documents

You must first use the dls:document-checkout function to check out a managed document before performing any update operations. For example, to check out the beta_overview.xml document, along with all of its linked documents, specify the following:

xquery version "1.0-ml";
import module namespace dls = "http://marklogic.com/xdmp/dls" 
        at "/MarkLogic/dls.xqy";
dls:document-checkout(
        "/engineering/beta_overview.xml", 
        fn:true(), 
        "Updating doc")

You can specify an optional timeout parameter to dls:document-checkoutthat specifies how long (in seconds) to keep the document checked out. For example, to check out the beta_overview.xml document for one hour, specify the following:

dls:document-checkout(
        "/engineering/beta_overview.xml", 
        fn:true(), 
        "Updating doc",
        3600)

Displaying the Checkout Status of Managed Documents

You can use the dls:document-checkout-status function to report the status of a checked out document. For example:

dls:document-checkout-status("/engineering/beta_overview.xml")

Returns output similar to:

<dls:checkout xmlns:dls="http://marklogic.com/xdmp/dls">
  <dls:document-uri>/engineering/beta_overview.xml</dls:document-uri>
  <dls:annotation>Updating doc</dls:annotation>
  <dls:timeout>0</dls:timeout>
  <dls:timestamp>1240528210</dls:timestamp>
  <sec:user-id xmlns:sec="http://marklogic.com/xdmp/security">
    10677693687367813363
  </sec:user-id>
</dls:checkout>

Breaking the Checkout of Managed Documents

Users with dls-admin role can call dls:break-checkout to un-checkout documents. For example, if a document was checked out by a user who has since moved on to other projects, the Administrator can break the existing checkout of the document so that other users can check it out.

Checking In Managed Documents

Once you have finished updating the document, use the dls:document-checkin function to check it, along with all of its linked documents, back in:

dls:document-checkin(
        "/engineering/beta_overview.xml", 
        fn:true() )

Updating Managed Documents

You can call the dls:document-update function to replace the contents of an existing managed document. Each time you call the dls:document-update function on a document, the document's version is incremented and a purge operation is initiated that removes any versions of the document that are not retained by the retention policy, as described in Defining a Retention Policy.

You cannot use node update functions, such as xdmp:node-replace, with managed documents. Updates to the document must be done in memory before calling the dls:document-update function. For information on how to do in-memory updates on document nodes, see Transforming XML Structures With a Recursive typeswitch Expression.

For example, to update the Project Beta Overview document, enter:

let $contents :=  
<BOOK>
  <TITLE>Project Beta Overview</TITLE>
  <CHAPTER>
    <TITLE>Objectives</TITLE>
    <PARA>
    The objective of Project Beta, in simple terms, is to corner
    the widget market.
    </PARA>
  </CHAPTER>
</BOOK> 
return
    dls:document-update(
       "/engineering/beta_overview.xml",
       $contents,
       "Roughing in the first chapter",
       fn:true())

The dls:document-update function replaces the entire contents of the document.

Defining a Retention Policy

A retention policy specifies what document versions are retained in the database following a purge operation. A retention policy is made up of one or more retention rules. If you do not define a retention policy, then none of the previous versions of your documents are retained.

This section describes:

Purging Versions of Managed Document

Each update of a managed document initiates a purge operation that removes the versions of that document that are not retained by your retention policy. You can also call dls:purge to purge all of the documents or dls:document-purge to run purge on a specific managed document.

You can also use dls:purge or dls:document-purge to determine what documents would be deleted by the retention policy without actually deleting them. This option can be useful when developing your retention rules. For example, if you change your retention policy and want to determine specifically what document versions will be deleted as a result, you can use:

xquery version "1.0-ml";
import module namespace dls="http://marklogic.com/xdmp/dls" 
              at "/MarkLogic/dls.xqy";
dls:purge(fn:false(), fn:true())

About Retention Rules

Retention rules describe which versions of what documents are to be retained by the purge operation. When using dls:document-update or dls:document-extract-part to create a new version of a document, previous versions of the document that do not match the retention policy are purged.

You can define retention rules to keep various numbers of document versions, to keep documents matching a cts-query expression, and/or to keep documents for a specified period of time. Restrictions in a retention rule are combined with a logical AND, so that all of the expressions in the retention rule must be true for the document versions to be retained. When you combine separate retention rules, the resulting retention policy is an OR of the combined rules (that is, the document versions are retained if they are matched by any of the rules). Multiple rules do not have an order of operation.

The retention policy specifies what is retained, not what is purged. Therefore, anything that does not match the retention policy is removed.

Creating Retention Rules

You create a retention rule by calling the dls:retention-rule function. The dls:retention-rule-insert function inserts one or more retention rules into the database.

For example, the following retention rule retains all versions of all documents because the empty cts:and-query function matches all documents:

xquery version "1.0-ml";
import module namespace dls="http://marklogic.com/xdmp/dls" 
              at "/MarkLogic/dls.xqy";
dls:retention-rule-insert(
dls:retention-rule(
    "All Versions Retention Rule",
    "Retain all versions of all documents",
    (),
    (),
    "Locate all of the documents",
    cts:and-query(()) ) )

The following retention rule retains the last five versions of all of the documents located under the /engineering/ directory:

xquery version "1.0-ml";
import module namespace dls="http://marklogic.com/xdmp/dls" 
              at "/MarkLogic/dls.xqy";
dls:retention-rule-insert(
dls:retention-rule(
    "Engineering Retention Rule",
    "Retain the five most recent versions of Engineering docs",
    5,
    (),
    "Locate all of the Engineering documents",
    cts:directory-query("/engineering/", "infinity") ) )

The following retention rule retains the latest three versions of the engineering documents with Project Alpha in the title that were authored by Jim:

xquery version "1.0-ml";
import module namespace dls="http://marklogic.com/xdmp/dls" 
              at "/MarkLogic/dls.xqy";
dls:retention-rule-insert(
dls:retention-rule(
    "Project Alpha Retention Rule",
    "Retain the three most recent engineering documents with
     the title 'Project Alpha' and authored by Jim.",
    3,
    (),
    "Locate the engineering docs with 'Project Alpha' in the 
     title authored by Jim",
    cts:and-query((
       cts:element-word-query(xs:QName("TITLE"), "Project Alpha"),
       cts:directory-query("/engineering/", "infinity"),
       dls:author-query(xdmp:user("Jim")) )) ) )

The following retention rule retains the five most recent versions of documents in the specs collection that are no more than thirty days old:

xquery version "1.0-ml";
import module namespace dls="http://marklogic.com/xdmp/dls" 
              at "/MarkLogic/dls.xqy";
dls:retention-rule-insert(
dls:retention-rule(
    "Specs Retention Rule",
    "Keep the five most recent versions of documents in the 'specs'
    collection that are 30 days old or newer",
    5,
    xs:duration("P30D"),
    "Locate documents in the 'specs' collection",
    cts:collection-query("http://marklogic.com/documents/specs") ) )

Retaining Specific Versions of Documents

The dls:document-version-query and dls:as-of-query constructor functions can be used in a retention rule to retain snapshots of the documents as they were at some point in time. A snapshot may be of specific versions of documents or documents as of a specific date.

For example, the following retention rule retains the latest versions of the engineering documents created before 5:00pm on 4/23/09:

xquery version "1.0-ml";
import module namespace dls="http://marklogic.com/xdmp/dls"
              at "/MarkLogic/dls.xqy";
dls:retention-rule-insert(
dls:retention-rule(
    "Draft 1 of the Engineering Docs",
    "Retain each engineering document that was update before
     5:00pm, 4/23/09",
    (),
    (),
    (),
    cts:and-query((
       cts:directory-query("/documentation/", "infinity"),
       dls:as-of-query(xs:dateTime("2009-04-23T17:00:00-07:00")) )) ))

If you want to retain two separate snapshots of the engineering documents, you can add a retention rule that contains a different cts:or-query function. For example:

cts:and-query((
    cts:directory-query("/documentation/", "infinity"),
    dls:as-of-query(xs:dateTime("2009-25-12T09:00:01-07:00")) ))

Multiple Retention Rules

In some organizations, it might make sense to create multiple retention rules. For example, the Engineering and Documentation groups may share a database and each organization wants to create and maintain their own retention rule.

Consider the two rules shown below. The first rule retains the latest 5 versions of all of the documents under the /engineering/ directory. The second rule, retains that latest 10 versions of all of the documents under the /documentation/ directory. The ORed result of these two rules does not impact the intent of each individual rule and each rule can be updated independently from the other.

xquery version "1.0-ml";
import module namespace dls="http://marklogic.com/xdmp/dls" 
              at "/MarkLogic/dls.xqy";
dls:retention-rule-insert((
dls:retention-rule(
    "Engineering Retention Rule",
    "Retain the five most recent versions of Engineering docs",
    5,
    (),
    "Apply to all of the Engineering documents",
    cts:directory-query("/engineering/", "infinity") ), 
dls:retention-rule(
    "Documentation Retention Rule",
    "Retain the ten most recent versions of the documentation",
    10,
    (),
    "Apply to all of the documentation",
    cts:directory-query("/documentation/", "infinity") ) ))

As previously described, multiple retention rules define a logical OR between them, so there may be circumstances when multiple retention rules are needed to define the desired retention policy for the same set of documents.

For example, you want to retain the last five versions of all of the engineering documents, as well as all engineering documents that were updated before 8:00am on 4/24/09 and 9:00am on 5/12/09. The following two retention rules are needed to define the desired retention policy:

xquery version "1.0-ml";
import module namespace dls="http://marklogic.com/xdmp/dls" 
              at "/MarkLogic/dls.xqy";
dls:retention-rule-insert((
dls:retention-rule(
    "Engineering Retention Rule",
    "Retain the five most recent versions of Engineering docs",
    5,
    (),
    "Retain all of the Engineering documents",
    cts:directory-query("/engineering/", "infinity") ),
dls:retention-rule(
    "Project Alpha Retention Rule",
    "Retain the engineering documents that were updated before
     the review dates below.",
    (),
    (),
    "Retain all of the Engineering documents updated before 
     the two dates",
    cts:and-query((
      cts:directory-query("/engineering/", "infinity"),
      cts:or-query((
        dls:as-of-query(xs:dateTime("2009-04-24T08:00:17.566-07:00")),
        dls:as-of-query(xs:dateTime("2009-05-12T09:00:01.632-07:00"))
        ))
     )) ) ))

It is important to understand the difference between the logical OR combination of the above two retention rules and the logical AND within a single rule. For example, the OR combination of the above two retention rules is not same as the single rule below, which is an AND between retaining the last five versions and the as-of versions. The end result of this rule is that the last five versions are not retained and the as-of versions are only retained as long as they are among the last five versions. Once the revisions of the last five documents have moved past the as-of dates, the AND logic is no longer true and you no longer have an effective retention policy, so no versions of the documents are retained.

xquery version "1.0-ml";
import module namespace dls="http://marklogic.com/xdmp/dls" 
              at "/MarkLogic/dls.xqy";
dls:retention-rule-insert(
dls:retention-rule(
    "Project Alpha Retention Rule",
    "Retain the 5 most recent engineering documents",
    5,
    (),
    "Retain all of the Engineering documents updated before 
     the two dates",
    cts:and-query((
      cts:directory-query("/engineering/", "infinity"),
      cts:or-query((
        dls:as-of-query(xs:dateTime("2009-04-24T08:56:17.566-07:00")),
        dls:as-of-query(xs:dateTime("2009-05-12T08:59:01.632-07:00"))
       )) )) ) )

Deleting Retention Rules

You can use the dls:retention-rule-remove function to delete retention rules. For example, to delete the Project Alpha Retention Rule, use:

xquery version "1.0-ml";
import module namespace dls="http://marklogic.com/xdmp/dls" 
              at "/MarkLogic/dls.xqy";
dls:retention-rule-remove("Project Alpha Retention Rule")

To delete all of your retention rules in the database, use:

xquery version "1.0-ml";
import module namespace dls="http://marklogic.com/xdmp/dls" 
              at "/MarkLogic/dls.xqy";
dls:retention-rule-remove(fn:data(dls:retention-rules("*")//dls:name))

Managing Modular Documents in Library Services

As described in Reusing Content With Modular Document Applications, you can create modular documents from the content stored in one or more linked documents. This section describes:

Creating Managed Modular Documents

As described in Reusing Content With Modular Document Applications, you can create modular documents from the content stored in one or more linked documents. The dls:document-extract-part function provides a shorthand method for creating modular managed documents. This function extracts a child element from a managed document, places the child element in a new managed document, and replaces the extracted child element with an XInclude reference.

For example, the following function call extracts Chapter 1 from the Project Beta Overview document:

dls:document-extract-part("/engineering/beta_overview_chap1.xml", 
    fn:doc("/engineering/beta_overview.xml")//CHAPTER[1], 
    "Extracting Chapter 1",
    fn:true() )

The contents of /engineering/beta_overview.xml is now as follows:

<BOOK>
  <TITLE>Project Beta Overview</TITLE>
  <xi:include href="/engineering/beta_overview_chap1.xml"/>
</BOOK>

The contents of /engineering/beta_overview_chap1.xml is as follows:

<CHAPTER>
  <TITLE>Objectives</TITLE>
  <PARA>
   The objective of Project Beta, in simple terms, is to corner
   the widget market.
  </PARA>
</CHAPTER>

The newly created managed document containing the extracted child element is initially checked-in and must be checked out before you can make any updates.

The dls:document-extract-part function can only be called once in a transaction for the same document. There may be circumstances in which you want to extract multiple elements from a document and replace them with XInclude statements. For example, the following query creates separate documents for all of the chapters from the Project Beta Overview document and replaces them with XInclude statements:

xquery version "1.0-ml";
import module namespace dls="http://marklogic.com/xdmp/dls" 
              at "/MarkLogic/dls.xqy";
declare namespace xi="http://www.w3.org/2001/XInclude";
let $includes := for $chap at $num in
   doc("/engineering/beta_overview.xml")/BOOK/CHAPTER
return (
   dls:document-insert-and-manage(
     fn:concat("/engineering/beta_overview_chap", $num, ".xml"),
     fn:true(),
     $chap),
   <xi:include href="/engineering/beta_overview_chap{$num}.xml"
    xmlns:xi="http://www.w3.org/2001/XInclude"/>
  )
let $contents := 
   <BOOK>
      <TITLE>Project Beta Overview</TITLE>
      {$includes}
   </BOOK>
return
   dls:document-update(
      "/engineering/beta_overview.xml",
      $contents,
      "Chapters are XIncludes",
      fn:true() )

This query produces a Project Beta Overview document similar to the following:

<BOOK>
   <TITLE>Project Beta Overview</TITLE>
   <xi:include href="/engineering/beta_overview_chap1.xml"
    xmlns:xi="http://www.w3.org/2001/XInclude"/>
   <xi:include href="/engineering/beta_overview_chap1.xml"
    xmlns:xi="http://www.w3.org/2001/XInclude"/>
   <xi:include href="/engineering/beta_overview_chap2.xml"
    xmlns:xi="http://www.w3.org/2001/XInclude"/>
</BOOK>

Expanding Managed Modular Documents

Modular documents can be expanded so that you can view the entire node, complete with its linked nodes, or a specific linked node. You can expand a modular document using dls:node-expand, or a linked node in a modular document using dls:link-expand .

When using the dls:node-expand function to expand documents that contain XInclude links to specific versioned documents, specify the $restriction parameter as an empty sequence.

For example, to return the expanded beta_overview.xml document, you can use:

xquery version "1.0-ml";
import module namespace dls="http://marklogic.com/xdmp/dls" 
              at "/MarkLogic/dls.xqy";
let $node := fn:doc("/engineering/beta_overview.xml")
return dls:node-expand($node, ())

To return the first linked node in the beta_overview.xml document, you can use:

xquery version "1.0-ml";
import module namespace dls="http://marklogic.com/xdmp/dls" 
              at "/MarkLogic/dls.xqy";
declare namespace xi="http://www.w3.org/2001/XInclude";
let $node := fn:doc("/engineering/beta_overview.xml")
return dls:link-expand(
               $node, 
               $node/BOOK/xi:include[1], 
               () )

The dls:node-expand and dls:link-expand functions allow you to specify a cts:query constructor to restrict what document version is to be expanded. For example, to expand the most recent version of the Project Beta Overview document created before 1:30pm on 4/6/09, you can use:

xquery version "1.0-ml";
import module namespace dls="http://marklogic.com/xdmp/dls" 
              at "/MarkLogic/dls.xqy";
let $node := fn:doc("/engineering/beta_overview.xml")
return dls:node-expand(
         $node, 
         dls:as-of-query(
            xs:dateTime("2009-04-06T13:30:33.576-07:00")) )

Managing Versions of Modular Documents

Library Services can manage modular documents so that various versions can be created for the linked documents. As a modular document's linked documents are updated, you might want to take periodic snapshots of the entire node.

For example, as shown in Creating Managed Modular Documents, the Project Beta Overview document contains three chapters that are linked as separate documents. The following query takes a snapshot of the latest version of each chapter and creates a new version of the Project Beta Overview document that includes the versioned chapters:

xquery version "1.0-ml";
import module namespace dls="http://marklogic.com/xdmp/dls" 
              at "/MarkLogic/dls.xqy";
declare namespace xi="http://www.w3.org/2001/XInclude";
(: For each chapter in the document, get the URI :)
let $includes := 
  for $chap at $num in doc("/engineering/beta_overview.xml") //xi:include/@href
(: Get the latest version of each chapter :)
let $version_number :=
  fn:data(dls:document-history($chap)//dls:version-id)[last()]
let $version :=  dls:document-version-uri($chap, $version_number)
(: Create an XInclude statement for each versioned chapter :)
  return
    <xi:include href="{$version}"/>
(: Update the book with the versioned chapters :)
let $contents := 
  <BOOK>
    <TITLE>Project Beta Overview</TITLE>
    {$includes}
  </BOOK>
return
  dls:document-update(
    "/engineering/beta_overview.xml",
    $contents,
    "Latest Draft",
    fn:true() )

The above query results in a new version of the Project Beta Overview document that looks like:

<BOOK>
  <TITLE>Project Beta Overview</TITLE>
  <xi:include href="/engineering/beta_overview_chap1.xml_versions/4-beta_overview_
chap1.xml" xmlns:xi="http://www.w3.org/2001/XInclude"/>
  <xi:include href="/engineering/beta_overview_chap2.xml_versions/3-beta_overview_
chap2.xml" xmlns:xi="http://www.w3.org/2001/XInclude"/>
  <xi:include href="/engineering/beta_overview_chap3.xml_versions/3-beta_overview_
chap3.xml" xmlns:xi="http://www.w3.org/2001/XInclude"/>
</BOOK>

When using the dls:node-expand function to expand modular documents that contain XInclude links to specific versioned documents, specify the $restriction parameter as an empty sequence.

You can also create modular documents that contain different versions of linked documents. For example, in the illustration below, Doc R.xml, Version 1 contains the contents of:

  • Doc A.xml, Version 1
  • Doc B.xml, Version 2
  • Doc C.xml, Version 2

While Doc X, Version 2 contains the contents of:

  • Doc A.xml, Version 2
  • Doc B.xml, Version 2
  • Doc C.xml, Version 3

« Previous chapter
Next chapter »