APIExample5: Using The ApplicationOntology Cache

Running The Example

You will have seen from the previous section, that coding TMRQL statements can require quite a lot of work in locating the topics that are input parameters into each query. You either end up writing code to find the topics using the Index API or you need to write long TMRQL queries that use sub-selects to find the topics. Most of the time, these topics are related to the system of topic, association, role and occurrence types in your topic map data and should be assigned URI subject identifiers to enable them to be found in the database. The ApplicationOntology object gives you a convenient API for managing these topics as well as an efficient local cache that can both improve query performance and make TMRQL queries more readable. You can read more about the ApplicationOntology object in the section called “The ApplicationOntology Object”. For the purposes of this section, you can think of the ApplicationOntology as a special form of cache in which short string keys can be used to retrieve full ITopic instances. The keys used can be directly tied to the URIs assigned to the topics in the topic map.

APIExample5 is a rewrite of APIExample4 using the ApplicationOntology object. The main order of processing is now:

  1. Import the XTM source file.

  2. Initialise the ApplicationOntology cache.

  3. Perform the two queries as before but using the ApplicationOntology object to find the input topics.

The initialisation of the ApplicationOntology object is performed in the method GetApplicationOntology(). The code for this method is shown below.

private ApplicationOntology GetApplicationOntology(ITopicMap tm)
{
  // Retrieve the inner ontology from the app settings file.
  ApplicationOntology innerOnt = System.Configuration.ConfigurationSettings.GetConfig("ontology") as ApplicationOntology;
  innerOnt.AddTopicMap(tm);
  // Create an outer ontology with the inner ontology nested.
  ApplicationOntology outerOnt = new ApplicationOntology(innerOnt);
  outerOnt.AddPrefix("monarchs", "http://www.example.com/psi/uk-monarchs/");
  outerOnt.AddTopicMap(tm);
  return outerOnt;
}

This code shows three important features of the ApplicationOntology object. Firstly an ApplicationOntology object can be constructed from the configuration settings for an application. This allows users to specify their ontology mappings using XML in the application's .config or Web.config file. Secondly, an ApplicationOntology can be intialised purely in code - this allows you to write code that uses specific mappings regardless of configuration file settings. Finally, ApplicationOntology objects can be nested one inside another - if the outer object cannot map an key to an ITopic instance, it passes the request to its nested ApplicationOntology object (which can in turn pass the request to its nested ApplicationOntology object and so on).

This code also shows how much of the cache mapping of the ApplicationOntology object works. You do not need to specify a cache key for every topic (although you can if you want to). Instead you declare a prefix string and the URI prefix that it maps to. You can then look up a topic using the prefix string followed by a colon and the rest of the URI. The ApplicationOntology will then expand the prefix and rest of URI into a full URI and search for a topic with that expanded URI as its subject identiifer. So in the code example above we add a prefix string "monarchs" for the URI prefix "http://www.example.com/psi/uk-monarchs/" - we can then find the topic for William I using the cache key "monarchs:william-i" which gets expanded to "http://www.example.com/psi/uk-monarchs/william-i" and locates the topic with that URI as a subject identifier. Once an ApplicationOntology object performs this expansion and finds a match, it then caches the direct topic reference, meaning that all future requests for the same item can be resolved without a look-up in the database. When you use the same topics time after time, this can provide a significant performance boost.

Looking up a topic using a cache key is simple in code - the ApplicationOntology acts like a normal .NET dictionary, returning ITopic instances from string keys, so you can write look-up code like this:

ITopic william = ontology["monarchs:william-i"];

However, the ApplicationOntology object also provides a method that assists in writing TMRQL statements by pre-processing them to replace cache key strings in curly braces with the object ID of the topic that is indexed against that cache key. This is shown in the QueryChildren() method by the following code:

private void QueryChildren(ITopicMap tm, ApplicationOntology ontology) 
{
  string query = 
    "SELECT r2p, dbo.tm_displayName(r2p) FROM tm_assoc2 WHERE" +
    "  topicmap = " + tm.ObjectID + " AND " +
    "  r1p={william} AND association_type={family:child-of} AND r1t={family:parent}";
  query = ontology.ReplaceReferences(query);
  try 
  {
    ITMCoreDataReader dr = tm.TopicMapSystem.ExecuteQuery(query);
  ...

The cache keys are written in the TMRQL statement surrounded by curly braces ({}). In this example the ApplicationOntology has been configured withs a direct mapping for the cache key "william" to the subject identifier "http://www.example.com/psi/uk-monarchs/william-i" as well as a prefix mapping for the string "family" to the URI prefix "http://www.example.com/psi/family/". The call to the ReplaceReferences() method of the ApplicationOntology object replaces these cache keys with the object identifiers of the topics "William I", "child-of" and "parent" respectively.

As you can see by comparing with the code from APIExample4, the ApplicationOntology object makes queries easier to write and to read and, because you avoid the need to do index lookups multiple times or to make use of SQL sub-selects in your queries, your code will execute faster too.