Virtual Documents

This page is rather technical. For theoretical aspects refer to the  XMLPrague 2010 paper, for complete description refer to the submitted to Balisage 2010  paper

RESTful methods can be found here. Please note that due to some bugs in DB XML, editing functionality does not work always properly.

VD Specifications and queries

The basis for Virtual Documents (VDs) is VD Specifications (VD Specs). VD Spec is basically a template for a VD with queries embedded or referenced. Let's start with an example:

<tnt:virtualdocument xmlns:tnt="http://tntbase.mathweb.org/ns">
  <tnt:skeleton xml:id="exercises">
    <omdoc xmlns="http://omdoc.org/ns" xmlns:dc="http://purl.org/dc/elements/1.1/">
      Text 1
      <!-- Comment 1 -->
      <dc:title>Exercises for GenCS</dc:title>
      <dc:creator>Michael Kohlhase</dc:creator>
      <omdoc>
        <dc:title>Acknowledgements</dc:title>
        <omtext>The following individuals have contributed material to this document</omtext>
        <tnt:xqinclude query="tnt:collection('/extcds/omstd.ext/tr*.omdoc')//dc:creator">
          <tnt:return>
             Test multiple nodes (including text) in the body of the tnt:return element
             <test>Test multiple elements in the body of the tnt:return element</test>
             <!-- Test comments nodes in the body of the tnt:return element -->
             <omtext>
              <tnt:result/>
             </omtext>
          </tnt:return>
        </tnt:xqinclude>
      </omdoc>
     <omdoc>
        <dc:title>SML Exercises</dc:title>
        <tnt:xqinclude>
          <tnt:query name="xq.SMLex"/>
          <tnt:return><exercise><tnt:result/></exercise></tnt:return>
        </tnt:xqinclude>
      </omdoc>
    </omdoc>
  </tnt:skeleton>
  <tnt:query name="xq.SMLex">
    for $i in tnt:collection('/gencs/SML/*.omdoc')//exercise
    return $i/*[local-name() ne 'solution']
    </tnt:query>
</tnt:virtualdocument>

The root element of a VD SPec is the tnt:virtualdocument element (note, that all TNTBase related elements are in the  http://tntbase.mathweb.org/ns namespace). This element may contain only one tnt:skeleton element and arbitrary number of tnt:query elements. The former comprises a 'template' for a VD, and the later - XQuery queries that can be referenced from the tnt:skeleton element. The dynamic context starts are enclosed into the tnt:xqinclude element. It may contain a query and the XML nodes that will be generated for every result in the query.

Obtaining a query

There are 3 ways to tell TNTBase VD engine where to get a query from:

  1. Attribute query of the tnt:xqinclue element. Suitable for short queries. If absent, step 2 is taken into account.
  2. Child element tnt:query It may contain either a reference using name attribute or a query as a text node. If reference is provided, then TNTBase VD engines searches for XQuries with a given name in the scope. What scope means will be explained further.

Example of a query in the attribute:

<tnt:xqinclude query="tnt:collection('/extcds/omstd.ext/tr*.omdoc')//dc:creator">
  ...
<tnt:xqinclude>

Example of a referenced query as a child element:

<tnt:xqinclude>
   <tnt:query name="xq.SMLex"/>
   ...
<tnt:xqinclude>
...
<tnt:query name="xq.SMLex">
  for $i in tnt:collection('/gencs/SML/*.omdoc')//exercise
    return $i/*[local-name() ne 'solution']
</tnt:query>

Example of a query as a text node:

<tnt:xqinclude>
   <tnt:query>
     for $i in tnt:collection('/gencs/SML/*.omdoc')//exercise
        return $i/*[local-name() ne 'solution']
   </tnt:query>
   ...
<tnt:xqinclude>

Defining a dynamic part

The second type of element in the tnt:xqinclude is the tnt:return element. It main contain any number of XML nodes (not only XML elements but text, comments, processing instructions and so on). Among those nodes there might be any number of the empty tnt:result elements, which define a place where to put the results of a query in. So for every query result the node of tnt:return are copied and the tnt:result element is substituted by the result it self. Assume we have the following tnt:return element:

<tnt:return>
  <omdoc>
   <author><tnt:result/></author>   
  </omdoc>
  A lot of work in between
  <new_omdoc>
   <creator><tnt:result/></creator>   
  </new_omdoc>
  <!-- who doubts? -->
</tnt:return>

and a query returned two results: "<guru>Michael</guru>" and "Florian". Then the tnt:xqinclude element will be substituted by:

  <omdoc>
   <author><guru>Michael</guru></author>   
  </omdoc>
  A lot of work in between
  <new_omdoc>
   <creator><guru>Michael</guru></creator>   
  </new_omdoc>
  <!-- who doubts? -->
  <omdoc>
   <author>Florian</author>   
  </omdoc>
  A lot of work in between
  <new_omdoc>
   <creator>Florian</creator>   
  </new_omdoc>
  <!-- who doubts? -->

Such a substitution is done for every tnt:xqinclude element in the skeleton. Note that this element can't be a direct child of 'tnt:skeleton' since otherwise it means multiple results which can't comprise the root of an XML document.

Skeleton references and XQuery overriding

In situations when a skeleton stays the same, but queries are being changed, then you can create skeletons which reference another skeletons. Moreover skeletons may contain the query with the same reference name. In this case the earliest one in the reference chain is chosen. Let's consider an example:

<tnt:virtualdocument xmlns:tnt="http://tntbase.mathweb.org/ns">
  <tnt:skeleton xml:id="exercises">
    <omdoc xmlns="http://omdoc.org/ns" xmlns:dc="http://purl.org/dc/elements/1.1/">
        <dc:title>SML Exercises</dc:title>
        <tnt:xqinclude>
          <tnt:query name="xq.SMLex"/>
          <tnt:return><exercise><tnt:result/></exercise></tnt:return>
        </tnt:xqinclude>
    </omdoc>
  </tnt:skeleton>
  <tnt:query name="xq.SMLex">
    for $i in tnt:collection('/gencs/SML/*.omdoc')//exercise
    return $i/*[local-name() ne 'solution']
    </tnt:query>
</tnt:virtualdocument>

This skeleton contains explicit skeleton body and the reference to the query xq.SMLex defined in the same skeleton. If you don't want to affect the VD appearance but intend to substitute a query, you can provide the following VD skeleton:

<tnt:virtualdocument xmlns:tnt="http://tntbase.mathweb.org/ns">
  <tnt:skeleton href="../fragments/skeleton.xml"/>
  <tnt:query name="xq.SMLex">
    for $i in tnt:collection('/eLearning/en/*.omdoc')//metadata return $i
    </tnt:query>
  <tnt:query name="xq.SMTH">
    for $i in tnt:collection('/eLearning/en/*.omdoc')//metadata return $i
    </tnt:query>
</tnt:virtualdocument>

You can reference another skeleton body by providing an absolute or relative path in the repository. In turn, the referenced skeleton may reference another one, and so on recursively. If queries with the same name occur along the path, the earliest wins. Such a mechanism is comparable to function overriding in Java or C++. If you want to look which skeleton is resolved to which skeleton body and queries, you can execute TNTBase XQuery function: tnt:get-skeleton($path as xs:string, ()).

Associating VD Spec with a file system entity

When VD Spec is ready, it can be associated with any number of file system entities that differ in file system path and parameters.

Getting and querying a VD

Just use the XQuery function tnt:vdoc($path as xs:string) as element(). $path is the path of the VD FS entity in the TNTBase repository. If VD Spec contains all needed for XQueries parameters, its content can be retrieved by XQuery function tnt:vd-skel($path as xs:string) as element(), where $path is the path of a VD Spec.

Modyfying a VD

Use function tnt:submit-vd($path as xs:string, $vd as element(), $msg as xs:string) as xs:string, where $path is the path of a VD, $vd is a modified VD (note, it is provided not via xs:string) and the $msg is the commit message if the propagation of changes went successfully.

A couple of things to note:

  1. Only dynamic content of a VD is allowed to be modified, that is those elements that have tnt:doc and tnt:xpath attributes, otherwise a dynamic XQuery error is raised.
  2. A user is allowed to modify any results element except its tnt:doc and tnt:xpath attributes.
  3. When committing a VD, then HEAD revision of the TNTBase repository must match the revision the VD has been created in. Otherwise an XQuery dynamic error is raised. This will not allow to overwrite somebody's changes that were committed after a VD was retrieved but before it was submitted. It is up to a user to retrieve a VD again and merge changes.

XQuery XML-diff

As a side effect, there is a function tntx:XMLDiff($e1 as element(), $e2 as element()) as element()* that returns diff-elements in a special format. For more details, write an email.