DTS Programming Tutorial

Chapter 1 – Connecting to the DTS Server

Chapter 2 – The EchoConcept Method

Chapter 3 – Attribute Set Descriptors

Chapter 4 – The NavQuery Class

Chapter 5 – The SearchQuery Class

Chapter 6 – The MatchQuery Class

Chapter 7 – The OntylogClassQuery Class

Chapter 8 – Creating a Concept with Attributes

Chapter 9 – Updating a Concept and Its Attributes

Chapter 10 – Subset

Chapter 11 – log4j

Chapter 1 – Connecting to the DTS Server

This tutorial describes how the DTS APIs, in association with the DTS server and an Apelon Knowledgebase, can be used to query and display terminology content. These chapters assume that the DTS software and Knowledgebase have been installed. Further information on DTS installation and setup can be found in the Apelon DTS Installation Guide. You should also have read the Overview of the Apelon DTS section of these Javadocs.

During this tutorial, we will develop and extend an application called EchoConcept. This application will display (actually print out) the various attributes and relationships associated with a DTS Concept. By the end of the tutorial, EchoConcept and its supporting methods will have demonstrated most of the programming techniques that are used in developing a DTS application.

The sample code is named Chapter1.java, Chapter2.java, etc., and can be found in the …/samples/src/com/apelon/dts/samples/tutorial/ folder of your DTS installation. To run any of the tutorial chapters (Apelon Server must be running for these chapters to work), enter, for example:

runapp_cw 128 com.apelon.dts.samples.tutorial.Chapter1

at the command line in the bin directory of the DTS installation.

Note: The Apelon DTS API relies on the use of a SAX parser. When utilizing the DTS API for any program, the following argument must be specified when starting the JVM:

-Dorg.xml.sax.driver=org.apache.xerces.parsers.SAXParser

Connecting to a DTS Server

The first action that a DTS client application must take is connecting to the DTS server. This connection is defined by the ServerConnection class. DTS provides three separate implementations of ServerConnection to accommodate different operating requirements:

· ServerConnectionSocket establishes a connection to a running socket based server such as the Apelon DTS Server. The remote socket server must be started independently.

Use the following code to establish a socket connection:

ServerConnectionSocket sc = new ServerConnectionSocket(host, port);

where host is the name of the server computer and port is the port number.

· ServerConnectionSecureSocket extends the functionality of the ServerConnectionSocket by creating a secure socket connection to the server. The implementation prepends the basic query with an authorization token (username+password).

The following code establishes a secure socket connection:

ServerConnectionSecureSocket scss = new ServerConnectionSecureSocket(host, port, user, pass);

 

where user is the username and pass is the password.

· ServerConnectionJDBC permits a direct JDBC connection to the database and avoids the need to create a socket based server. This is very useful during program development and for small demos. Once the JDBC server connection is created, it is necessary to associate an instance of one or more query server classes with the connection.

Use the following code to establish a JDBC connection (the query server class and message header depend on which API query client class you're using):

ServerConnectionJDBC connJDBC = new ServerConnectionJDBC(user, password, host, port, instance);
connJDBC.setQueryServer(com.apelon.dts.server.OntylogConceptServer.class,DTSHeader.ONTYLOGCONCEPTSERVER_HEADER);

Chapter1.java integrates the code fragments above into a sample class that creates DTS database connections. This class will be used to provide Knowledgebase connectivity in all the succeeding tutorial chapters.

Namespaces

DTS 3.0 introduces the idea of namespaces, which allow for different vocabulary sources to be organized separately within the same knowledgebase. It is no longer sufficient to know a concept name to uniquely identify a concept—you must also know the namespace in which the concept is defined. To retrieve a list of namespaces, you can use the query class NamespaceQuery after you get a server connection:

NamespaceQuery nameQry = NamespaceQuery.createInstance(conn);
Namespace[] nameSpaces = nameQry.getNamespaces();

This is done in the promptForNamespace(ServerConnection conn) method in Chapter1.java.

Apelon Exceptions

As can be seen in Chapter1.java, DTS introduces a number of new Exceptions. An ApelonException is an exception that originates in the com.apelon.apelonserver.client package, which is where the ServerConnection classes reside. In later chapters you will also see a DTSException , which is an exception that originates from the com.apelon.dts.client package. Refer to the DTS API Javadoc for details on the specific exceptions thrown by DTS methods, and to the ApelonException and DTSException Javadoc for information on how to extract exception information in error reporting.

Back to Top

Chapter 2 – The EchoConcept Method

EchoConcept prints the contents of a DTSConcept on the console. These contents include the DTSAttributes (properties and roles) as described in the DTS Overview. All DTSConcepts (as well as all other DTS primitive objects) are derived from the abstract class DTSObject and as such also have three defining fields: the concept name, the concept code, and the concept ID. While the values of these fields are relatively unrestricted, names (strings) are usually human-readable descriptions, IDs (ints) are numeric keys, and codes (strings) are alphanumeric values that may reflect an alternate external representation. Concept names, codes, and IDs are unique in any DTS KB Namespace and with a namespace ID can be used to unambiguously refer to and access a DTSConcept object.

Chapter2.java contains the initial EchoConcept implementation: the EchoConcept method which takes a DTSConcept object as its sole argument. We will see later how to actually get a DTSConcept object, but for now we will simply assume one has been correctly passed. The values of the defining fields can be retrieved through the standard DTSConcept accessor methods:

echoConcept(Concept con)
{
...
System.out.println("Name: " + con.getName());
System.out.println ("Code: " + con.getCode());
System.out.println ("Id: " + con.getId());

Next we determine whether or not the input OntylogConcept (which extends DTSConcept) has children (subconcepts or subs) and/or parents (superconcepts or sups). The boolean state of these conditions is retrieved via the getFetchedHasSups() and getFetchedHasSubs() methods:

System.out.println ("Has Subs: " + con.getFetchedHasSubs());
System.out.println ("Has Sups: " + con.getFetchedHasSups());

Why do we say “fetched” in “getFetchedHasSubs” and not just “getHasSubs”? The answer lies in the way DTS saves concept data. When DTS retrieves a concept from the Knowledgebase, it does not necessarily retrieve all the information associated with the concept. The application can specify what information is required. The concept data that is retrieved is said to be “fetched”. This methodology improves performance by reducing database queries and intermediate storage requirements when only some concept data is needed by the application. In Chapter 3 we will see how to specify the particular pieces of information that are fetched, but for now, we will just assume we will get everything we want. (Note that concept names, IDs, and codes are always fetched.) You should refer to the Javadoc to see what data is retrieved for each DTS method.

When you look at Chapter2.java you will see that the above statements are modified by conditionals such as:

if (con.isFetchedHasSubs()) …

to determine if the subs and sups data has actually been fetched. We will see how to get an OntylogConcept’s subs and sups in Chapter 4.

With this background, the rest of EchoConcept should be clear. A DTSProperty is a DTSAttribute. getFetchedProperties() returns an array of DTSProperty objects, and the associated accessor methods return the DTSProperty name and value. The code is:

DTSProperty[] props = con.getFetchedProperties();
for (conIter = 0; conIter < props.length; conIter++)
{
System.out.println (props[conIter].getName() + “: “ +
props[conIter].getValue());
}

The methods for roles are similar.

When an exception is encountered, Chapter2.java, and all other sample code, contains code to log errors using the Apache log4j package, the static method Chapter1.getDtsClientLog4jCategory().error() in particular. Refer to Chapter 10 – log4j for more information.

catch (Exception ex) {
Chapter1.getDtsClientLog4jCategory().error("Caught Exception", ex);
}

That completes the description of EchoConcept, but how do we get a DTSConcept or OntylogConcept in the first place? We will explain how to search for concepts in Chapter 5, but for now, we will simply retrieve the “top level” or “root” concepts in a DTS Knowledgebase Ontylog namespace. Root concepts are concepts that have no parents (superconcepts) in the Ontylog hierarchy. They are at the top (or bottom depending on your point of view) of the concept tree. An Ontylog namespace may have multiple roots, each root corresponding to a different terminology hierarchy or taxonomy.

To get the root concepts we need a DTS query service class, specifically a navigation query class ( NavQuery). As described in the DTS Overview, the DTS client APIs are organized into collections of service classes. After creating the ServerConnection (from Chapter1.java) you need to instantiate one (or more) of these service classes using the associated static createInstance() methods. To create a navigation server, execute:

NavQuery navQry = NavQuery.createInstance(sc);

The other query classes DTSConceptQuery, OntylogConceptQuery, OntylogClassQuery, OntylogExtConceptQuery, and SearchQuery can be created in a similar fashion. Note that BaseConceptQuery, which is used to fetch DTSConcepts from names, codes and IDs, is the base class for all the service classes. We will discuss service classes in much greater detail in later chapters.

Now that we have a connection to the DTS Knowledgebase and a service class of relevant methods, we can get the root concepts. The method call is:

OntylogConcept[] cons = navQry.getConceptChildRoots (attributeSetDescriptor, namespaceId);

An array of root concepts has been placed into cons. Now each DTSConcept in cons can be passed to EchoConcept.

If we have to retreive root concepts for an Ontylog Extension namespace (which includes root concepts in its linked subscription namespace):

NamespaceQuery nameQry = NamespaceQuery.createInstance(serverConnection);
Namespace extNamespace = nameQry.findNamespaceById (extNamespaceId);
OntylogConcept[] cons = navQry.getConceptChildRoots(attributeSetDescriptor, extNamespace);

Chapter2.java ties all these pieces together. The Main method instantiates a Chapter1 class and invokes getConnectionSocket to get a connection. Then we create an instance of Chapter2 and call fetchAndDisplayRootConcepts. This method creates a NavQuery from the connection, gets the root concepts, and calls EchoConcept for each root.

Back to Top

Chapter 3 – Attribute Set Descriptors

As described in Chapter 2, DTS concepts consist of a name, code, ID, and namespace ID, along with sets of synonyms, properties, roles, associations, superconcepts, and subconcepts. Since client code will not always be interested in all attributes of concepts retrieved from DTS, a common theme in the DTS API is a mechanism to allow client code to specify “how much” concept information to retrieve on the server side before passing DTSConcept or OntylogConcept objects back through the API.

Attribute population specification is accomplished by instantiating and populating an ConceptAttributeSetDescriptor (ASD), and then passing that ASD to DTS API calls. There are several groups of attributes to specify:

What Synonym Types to retrieve
What Property Types to retrieve
What Role Types to retrieve
What Concept Association Types to retrieve

Since DTS 3.4.0, data related to defined view for the namespace type “Ontylog Extension'” (see OntylogExtConceptQuery) can also be retrieved. They are the defined view attributes:

o Kind
o Primitive/Defined
o Defining Concepts
o Defining Roles

Also in DTS 3.4.0, the superconcepts and subconcepts of an OntylogConcept can be retrieved along with the concept. These are part of the inferred view data of an OntylogConcept.

Code snipped below is an example of how to get the defined view information:

      ConceptAttributeSetDescriptor ca = new ConceptAttributeSetDescriptor("Defined View ASD", 100);



      //Specifies whether to retrieve defined view attributes for an Ontylog concept.

      //These includes the primitive/defined attribute, kind, defining superconcepts, and defining roles.

      ca.setDefinedViewAttributes(true);
      OntylogConcept defConcept = (OntylogConcept)conExQuery.findConceptById(conceptId, conceptNamespaceId, ca);



      DTSRole [] roles = defConcept.getFetchedDefiningRoles();

      DTSConcept [] cons = defConcept.getFetchedDefiningConcepts();

      boolean isPrimitive = defConcept.getFetchedPrimitive();

To retrieve all the superconcepts and subconcepts (from inferred view) for a given OntylogConcept:

      ConceptAttributeSetDescriptor ca = new ConceptAttributeSetDescriptor("Parent_Child ASD");

      ca.setSuperconcepts(true);

      ca.setSubconcepts(true);



      OntylogConcept concept = (OntylogConcept)conExQuery.findConceptById(id, namespaceId, ca);

      OntylogConcept[] sups = concept.getFetchedSuperconcepts();

      OntylogConcept[] subs = concept.getFetchedSubconcepts();

To create a ConceptAttributeSetDescriptor that specifies the property “preferredName” and the role “HAS_SPECIMEN”, you would do the following:

ConceptAttributeSetDescriptor myAsd = new ConceptAttributeSetDescriptor("My ASD");

PropertyType prop = someConceptServerInstance.getPropertyTypeByName("preferredName");
if (prop != null)
myAsd.addProperty(prop);

RoleType role = someConceptServerInstance.getRoleTypeByName("HAS_SPECIMEN");
if (role != null)
myAsd.addRole(role);

The ConceptAttributeSetDescriptor class also includes a couple of static ASDs to address common terminology situations. These ASDs are:

ALL_ATTRIBUTES - causes all attributes (Synonyms, Properties, Roles, and Associations) of a concept to be fetched.
Note: This attribute currently does not get the Ontylog defined view attributes (kind, defining superconcepts, defining roles and primitive/defined attribute).
NO_ATTRIBUTES – causes no attributes to be fetched.

Chapter3.java updates Chapter2.java and demonstrates the result of using different AttributeSetDescriptors in the getRootConcepts() method.

Back to Top

Chapter 4 – The NavQuery Class

The NavQuery class is the client side object used to navigate concepts in a DTS Knowledgebase. NavQuery creates another object, the NavChildContext, that contains the actual child concepts. Providing the data model for a tree control is a common NavQuery task.

Instantiating a NavQuery follows the overall pattern of creating any client service provider:

ServerConnection conn = new ServerConnectionSocket(“localhost”,6666);
NavQuery navQry = NavQuery.createInstance(conn);

Once you have a NavQuery, you can navigate the Ontylog concept hierarchy like so:

1. Call getConceptChildRoots, to get a ConceptChild array of the root concepts in a hierarchy. ConceptChild is a simple extension of OntylogConcept with the addition of the getHasChildren() method.

ConceptChild[] roots = navQry.getConceptChildRoots(asdToUse, namespaceId);

2. For each concept, call getNavContext, to get a populated NavChildContext object corresponding to the base concept, and containing the base concept’s immediate children.

for (int i=0; i < cons.length; i++) {
NavChildContext childCtx = navQry.getNavChildContext(cons[i], asdToUse); }

3. From the NavChildContext object, get children, and optionally go back to step 2.

ConceptChild[] children = childCtx.getChildren();

Note: Only the NavQuery’s methods can take a ConceptAttributeSetDescriptor argument, not the NavChildContext methods. The NavChildContext focus concept will then get the attributes specified on NavQuery.getNavChildContext, but the NavChildContext children get only the information about whether they in turn have children.

If you need to navigate upward instead of downward in a hierarchy, for example in the upper pane of a concept walker, NavQuery has a corresponding method getNavParentContext(OntylogConcept concept, ConceptAttributeSetDescriptor dtsAsd) that returns a NavParentContext object. The NavParentContext has ConceptParents, which, like ConceptChild objects, extend OntylogConcept.

In DTS 3.4.0, the NavQuery class has been extended to retrieve parent and child concepts for an Ontylog Extension concept. Since the Ontylog Extension namespace has a link to a Subscription Ontylog namespace, the new API method getNavChildContext (DTSConcept concept, ConceptAttributeSetDescriptor dtsAsd, Namespace namespace) gets children both in the concept's Ontylog Extension namespace and its linked subscription Ontylog namespaces. This is done by providing the Ontylog Namespace as the Namespace argument for the Ontylog Extension concept. Similarly, the newly-added API getNavParentContext (DTSConcept concept, ConceptAttributeSetDescriptor dtsAsd, Namespace namespace) retrieves parents from both extension and subscription namespaces for a given Ontylog Extension concept.

Chapter4.java introduces a new method, walkConceptTree(), that navigates and displays a variable number of levels (generations) of a DTS concept hierarchy.

Back to Top

Chapter 5 – The SearchQuery Class

SearchQuery provides fetching of concepts based on matching of concept names, property values, role values or association values. It supports exact matching, wild-card matching, and normalized word matching. Search indices can also be built on the knowledgebase that limit the search to certain portions of the terminology. Search indices are covered further in Chapter 9.

Attribute Types

In order to improve performance many DTS methods (including those in the SearchQuery) use attribute types rather than attribute names in their signatures. An attribute type is an object (again derived from DTSObject) that represents a synonym, property, role, or association. For example, the property whose name is “preferredName” is a DTSPropertyType object. This object can be accessed by the code:

DTSPropertyType prefNameType = searchQuery.findPropertyTypeByName(“preferredName”, namespaceId);

You can get DTSPropertyTypes, DTSRoleTypes, and AssociationTypes by their names, codes, or IDs.

Searching by exact match and wild card

To find the concept whose name is “ACUTE RHEUMATIC FEVER” you would call:

DTSConcept con = searchQry.findConceptByName("ACUTE RHEUMATIC FEVER", namespaceId, asd);

To find all concepts whose name starts with “ACUTE” you would call:

DTSSearchOptions searchOptions = new DTSSearchOptions(searchLimit, namespaceId, asd);
Concept[] cons = searchQry.findConceptsWithNameMatching("ACUTE*" , searchOptions);

To find all concepts with a “preferredName” property value starting with “ACUTE”, you would call:

Concept[] cons = searchQry.findConceptsWithPropertyMatching(prefNameType, "ACUTE*" , searchOptions);

To find all concepts with a “HAS_PROCEDURE” role value starting with “Ventri”, you would call:

DTSRoleType hasprocType = searchQry.findRoleTypeByName("HAS_PROCEDURE”);
Concept[] cons = searchQry.findConceptsWithRoleMatching(hasprocType, "Ventri*", searchOptions);

where hasProcType is the DTSRoleType whose name is “HAS_PROCEDURE”. This last example shows how you can find the “inverse” of roles. If one concept has a role “pointing” to another, e.g. “Aspirin” “TREATS” “Arthritis”, how do you find its inverses—what is “Arthritis” treated by? This is simply findConceptsWithInverseRoleMatching(treatsType, “Arthritis”).

Searching by normalized word values

DTS also provides a more flexible search capability that enables searching on values in a word order-insensitive fashion.What this means is that each word in a concept property value is “normalized” or broken down into its base form: a value of “breaking bones” is converted to the base words “break” and “bones”. A “Property Containing” search then matches each search word with the corresponding normalized property value words. In order to use Property Containing searches, the properties must have been specified as a “Contains Index” property at DTS installation. Refer to the DTS KB Administrator Guide for information on how to specify properties that should have this feature.

For example, a concept with a “preferredName” property value of “DISEASES OF OTHER ENDOCRINE GLANDS” would be found by issuing the following call:

Concept[] cons = searchQry.findConceptsWithPropertyContaining(prefNameType,"GLAND DISEASE");

Chapter5.java shows a number of SearchQuery code examples.

Back to Top

Chapter 6 – The MatchQuery Class

MatchQuery provides more advanced matching and search capabilities than those in SearchQuery. MatchQuery is an interface to Apelon’s MatchPack component which features lexical matching transformations (such as spelling correction and phrase completion) to match input terms to the contents of named match contexts.

Match contexts, or “silos”, are created by selecting concepts from the DTS Knowledgebase (via hierarchical roots or attributes), then extracting the concepts’ names and/or synonyms to populate the silo. This selection and extraction process is defined by an XML configuration file. The silo’s terms are matched to input terms, optionally using a variety of lexical transformations, by MatchQuery. Examples of silos are names of diseases, radiology procedures having CPT codes, drugs that are on formulary, or combinations of these. See Apelon DTS Matching – Quick Overview for further information on how to create match contexts.

To use MatchQuery, you first create a MatchOptions object. Then set desired match options via MatchOptions methods. For example, to select the desired match context:

MatchOptions so = new MatchOptions();
so.setSilo(“icd-9 diseases”);

Matching is defined as comparing the content of the pattern (input string) to a target (strings in the match context). There are a number of options related to the tolerance of the match algorithm:

MatchOptions.COMPLETE_MATCH is the least tolerant; all words in the pattern must match those in the target.
MatchOptions.UNDERMATCH matchs if the words in the pattern are a subset of the words in the target.
MatchOptions.PARTIAL_UNDERMATCH is the most tolerant. PARTIAL_UNDERMATCH is successful if a particular subset of the pattern matches a subset of the target.

Set the tolerance with the setMatch() method:

so.setMatch(MatchOptions.PARTIAL_UNDERMATCH);

See the SearchOption Javadoc for a more complete description of the match options.

Normally MatchQuery returns all the matches that satisfy the match criteria. To return only the best (least tolerant) matches even though an undermatch option was specified, choose “best match only”:

so.setBestMatchOnly(true);

To turn on spelling correction for the pattern string:

so.setSpellCorrection(true);

Once MatchOptions has been populated, create a MatchQuery and get your matches with findConceptsWithSiloMatching():

matchServer = MatchQuery.createInstance(conn);
Concepts[] cons = matchServer.findConceptsWithSiloMatching(so, “heart disease”);

Chapter6.java gives a complete example of the use of MatchQuery.

Back to Top

Chapter 7 – The OntylogClassQuery Class

OntylogClassQuery implements subsumption, or “class”, queries. It can determine if one concept is a descendant (a subconcept at any level) of another concept, and can retrieve all the subconcepts for a particular concept. This differs from NavChildContext, which only retrieves the direct children of a particular concept.
Note: Presently APIs in this class do not retrieve data for Ontylog Extension type namespaces. This will be handled in a later DTS release.

To get all the descendants of OntylogConcept con:

OntylogClassQuery classQry = OntylogClassQuery.createInstance(conn);
OntylogConcept[] cons = classQry.getSubConcepts(con, asd);

getSubConcepts() also takes a ConceptAttributeSetDescriptor specification to control the attributes to be fetched with the returned concepts.

To determine if con1 is a descendant, or arbitrary level subconcept, of con2,

boolean bIsChild = classQry.isSubConcept(con1, con2);

If you have an array of existing concepts, called cons, and you want to determine whether or not OntylogConcept con1 is a descendant of any of the concepts in the array, you can also use isSubConcept():

boolean bIsChild[] = classQry.isSubConcept(con1, cons);

bIsChild[i] tells whether con1 is a descendant of cons[i]. Other variations of isConcept() are available using Concept Names and ConceptCodes.

An important application of isSubConcept() is in decision support and guideline triggers. A prospective decision may be based on whether a patient’s diagnosis is an instance of “Heart Disease”. The physician’s diagnosis, on the other hand, would be specified as “Myocardial infarction” or “Mitral Value Insufficiency”. A single call to isSubConcept() can determine if the more general diagnosis condition has been met.

Method searchAndCheckSubsumption() in Chapter7.java fetches two sets of concepts from the knowledge base and classifies them as descendants of each other.

Back to Top

Chapter 8 – Creating a Concept with Attributes

For the first time in DTS it is possible to create concepts. DTS has three types of namespaces that can contain concepts: Ontylog namespaces, Ontylog Extension namespaces and Thesaurus namespaces. The content of an Ontylog namespace is loaded from a TDE database. TDE allows for the definition of a concept in terms of its superconcepts and roles, which are defining relationships with other concepts. DTS can provide for a more loosely-related collection of concepts in a thesaurus namespace. It is possible to create concepts in a thesaurus namespace, but not in an Ontylog namespace. In DTS 3.4.0, a new type of namespace called Ontylog Extension has been added, which has a link to a subscription Ontylog namespace. This type was primarily added to aid classification of local namespaces with defined view data (roles, kinds, and defining superconcepts). The concepts in Ontylog Extension namespaces can be created just like thesaurus concepts but can have more information in the form of these attributes.

ThesaurusConceptQuery provides the methods for creating, modifying, and deleting concepts in a thesaurus namespace. BaseConceptQuery provides the methods needed to add, modify, or delete attributes of a concept, such as synonyms, properties, or qualifiers on properties between concepts. Qualifiers on concept associations can be included when creating the association using the AssociationQuery method addConceptAssociation.

The methods here in the DTS API are "lightweight" compared to the TDE API. Rather than creating a concept object and decorating it with attributes before finally committing all the edits in a single method, the ThesaurusConceptQuery.addConcept() method only adds the core concept with its name, code, ID, and namespace ID. To add synonyms or properties to the concept use the BaseConceptQuery methods addSynonym() and addProperty(). Any qualifiers can be added to the DTSProperty object before issuing the addProperty() method.

OntylogExtConceptQuery provides the API for creating, updating, and deleting concepts in the Ontylog Extension namespace. It also provides methods to add and delete defining concepts or roles, and update primitive/defined status for the concepts (as shown below). Just like thesaurus concepts, Ontylog Extension concepts can also have associations to any other concepts and properties with or without qualifiers.

The OntylogExtConceptQuery and OntylogConceptQuery can be initialized with the server connection as:

   ServerConnectionSocket sc = new ServerConnectionSocket( host, port );      

 
 
   sc.setQueryServer(Class.forName( "com.apelon.dts.server.OntylogExtConceptServer" ), 

                                      DTSHeader.ONTYLOGEXTCONCEPTSERVER_HEADER);
   sc.setQueryServer(Class.forName( "com.apelon.dts.server.OntylogConceptServer" ),

                                      DTSHeader.ONTYLOGCONCEPTSERVER_HEADER );

   OntylogExtConceptQuery conExQuery = OntylogExtConceptQuery.createInstance( sc );

   OntylogConceptQuery conQuery      = OntylogConceptQuery.createInstance( sc );

To create a concept in the Ontylog Extension namespace:

   DTSConcept myExtConcept = new DTSConcept( "CALCIUM_TEST", conNamespaceId );

To search for a appropiate concept, which will be the defining superconcept of the above concept:

   OntylogConcept superconcept = (OntylogConcept)conQuery.findConceptByName( "CHEMISTRY_TEST",

                                                      subNamespaceId, ConceptAttributeSetDescriptor.NO_ATTRIBUTES );
 
   conExQuery.addDefiningConcept( (OntylogConcept)myExtConcept, superconcept );

To search for a suitable concept, which will be the defining role of the new concept:

   OntylogConcept valueconcept = (OntylogConcept)conQuery.findConceptByName( "CALCIUM",

         subNamespaceId, ConceptAttributeSetDescriptor.NO_ATTRIBUTES );



 
 
Create a DTSRoleType object for the role and create a defining role for the above extension concept :
   DTSRoleType roleType = new DTSRoleType( "HAS_SUBSTANCE_MEASURED", 10, "R10", 50 );

   DTSRole role = new DTSRole( roleType, valueconcept, RoleModifier.ALL, 0 ); //0 indicates that role is ungrouped

   conExQuery.addDefiningRole( (OntylogConcept)myExtConcept, role );

Finally, modify the primitive status of the new concept to be defined:

   conExQuery.updatePrimitive( (OntylogConcept)myExtConcept, false );    //false for concept to be defined

Concept associations and properties (with or without qualifiers) can be added to concepts using the AssociationQuery and ThesaurusConceptQuery.

To initialize the AssociationQuery and ThesaurusQuery objects:

   ServerConnectionSocket sc = new ServerConnectionSocket( host, port );    

 
 
   sc.setQueryServer(Class.forName( "com.apelon.dts.server.AssociationServer" ), 

                                      DTSHeader.ASSOCIATIONSERVER_HEADER);

   sc.setQueryServer(Class.forName( "com.apelon.dts.server.ThesaurusConceptServer" ),

                                        DTSHeader.THESAURUSCONCEPTSERVER_HEADER);

   AssociationQuery assocQuery = AsscociationQuery.createInstance( sc );

   ThesaurusConceptQuery thesQuery = ThesaurusConceptQuery.createInstance( sc );

To get local association type and use it to build concept associations from an extension concept to a subscription concept:

   AssociationType assocType = assocQuery.findAssociationTypeByName( "Mapping Category", localNamespaceId );      

   OntylogConcept toConcept = (OntylogConcept)conQuery.findConceptByName( "PROCEDURE",

                                 subNamespaceId, ConceptAttributeSetDescriptor.NO_ATTRIBUTES );
   ConceptAssociation newAssoc = new ConceptAssociation( myExtConcept, assocType, toConcept );

   boolean assocAdded = assocQuery.addConceptAssociation( newAssoc );

To build local a concept property, first fetch an appropiate local property type and use it construct a property on the extension concept:

   PropertyType propType = thesQuery.findPropertyTypeByName( "MY LOCAL PROPERTY", localNamespaceId );
   DTSProperty newProperty = new DTSProperty(propType, "Custom test to detect calcium");

   DTSConcept updatedConcept = thesQuery.addProperty( myExtConcept, newProperty );


Back to Top

Chapter 9 – Updating a Concept and Its Attributes

It is also possible to update an existing concept and many of its attributes. If the concept is in a writable namespace, you can update its synonyms, properties, associations defined in the namespace, and property or association qualifiers.

Updating Thesaurus Concepts

There is a method in ThesaurusConceptQuery called updateConcept, but this is only for updating the name or possibly the code for a concept in a thesaurus namespace. The concept ID cannot be changed. To update other attributes of either Ontylog or thesaurus concepts, the methods in BaseConceptQuery are required, such as updateSynonym and updateProperty, or in AssociationQuery, namely, updateConceptAssociation. Concepts in Ontylog Extension namespaces, similar to thesaurus concepts, can be updated by either modifying the concept name, changing the primitive/defined status, adding or deleting defining concepts, adding or deleting defining roles, or by regrouping or ungrouping defining roles.

For example, local properties and synonyms can be updated using methods in ThesaurusConceptQuery:

  updateProperty( myConcept, property, newProperty );

  updateSynonym( oldSynonym, newSynonym );

For updating defined view information of an Ontylog Extension concept, OntylogExtConceptQuery methods can be used :

  updateDefiningConcept( myExtConcept, deleteDefiningConcept, addDefiningConcept );

  updateDefiningRole( myExtConcept, deleteRole, addRole );

  updatePrimitive( myExtConcept,  primitive );

  ungroupRoleGroup( myExtConcept,  roleGroupNum );

  createRoleGroup( myExtConcept,  roles );

Back to Top

 

Chapter 10 – Subsets

A subset is any collection of concepts that share a specific set of associated attributes, and may include only a few concepts or many concepts.  The concepts in a subset may only be derived from a single namespace.  The namespace may be a local or subscription.  If the namespace selected for the subset is of the type Subscription Ontylog or Ontylog Extension, the subset can also have a default hierarchy based on the collection.  If the namespace is of type Ontylog Extension the subset can include concepts from both the Ontylog Extension (i.e., local) namespace and the linked Subscription Ontylog namespace. 

The first step in creating a subset is to define a subset expression.  The subset expression defines the criteria for concept selection.  This expression can be created using the SubsetExpressionBuilder class.  This example will create a simple subset expression to retrieve a concept and all its descendants from an Ontylog namespace.  To create a subset expression start by choosing a namespace to build the subset from and then finding a concept:

    // Find an Ontylog Namespace for the subset

    Namespace ns = NamespaceQuery.findNamespaceByName("Ontylog Namespace Name"); 
 
    SearchQuery searchQry = SearchQuery.createInstance(srvrConn);
    DTSConcept myConcept = searchQry.findConceptByName("Some Concept*", ns.getId,
                                                       ConceptAttributeSetDescriptor.NO_ATTRIBUTES);

Begin by constructing and building the subset expression:

    SubsetExpressionBuilder expr = new SubsetExpressionBuilder();

    // From this namespace     
    DefaultMutableTreeNode nsNode = expr.initNamespace(ns);

     
    // Select this concept and all its descendants
    DefaultMutableTreeNode conNode = expr.addConceptFilter(nsNode, myConcept, false);

    // Build the expression
    String xml = expr.buildExpression();

Now define the subset using the Subset class:

    // Name the Subset
    String name = "Tutorial Test Subset";


    Subset subset = new Subset();

    subset.setName( name );

    subset.setId( 10 );

    subset.setExpression( xml );

    subset.setDescription( "This subset was created from the subset chapter of the DTS tutorial");

Once the subset definition is built, add the subset to the knowledgebase using the Subset Query class:

    SubsetQuery subsetQry = SubsetQuery.createInstance(srvrConn);

    Subset addedSubset = subsetQry.add( subset );

The Subset object returned from the query has an updated time field, a created user field if the subset was created using a secure socket connection, and a new subset ID assigned by the server.

To search for a particular subset, the following calls can be made using a DataTypeFilter class which can filter by namespace or use a name pattern as its filter criteria:

    DataTypeFilter filter = new DataTypeFilter();

    filter.setFilterBy( DataTypeFilter.FILTER_BY_NAME_MATCHING );

    filter.setNamePattern( "Tutorial Test*" );

    Subset [] subs = subsetQry.find( filter, new SubsetDescriptor(true, true) );

    for (int i=0; i<subs.length; i++ ){

      System.out.println("\tSubset [" + subs[i].getName() + "] retrieved." );

    }

A subset definition added to the knowledgebase does not have any concepts or a hierarchy. To compute concepts and all descendants of the subset definition created above:

    boolean build = subsetQry.build( addedSubset.getId() );

Once the subset is computed (and built), queries can be made on that subset to view all its concepts (with or without attributes):

    DTSConcept [] cons = subsetQry.fetchConcepts( addedSubset.getId(), ConceptAttributeSetDescriptor.NO_ATTRIBUTES );
    

    for( int i=0; i<cons.length; i++ ){

      System.out.println("\t" + cons[i].getName() );

    }

Or search for concepts in the subset with a name pattern using the call in SearchQuery class:

    boolean searchSynonyms = false; //To not search synonymous terms

    OntylogConcept[] matchedConcepts = SearchQuery.findConceptsWithNameMatching("Concept Name*", addedSubset.getId(), 

                                                                                 searchSynonyms, 10 );

All the concepts in the subset can be exported to a thesaurus namespace with associations to the original concepts in the following manner:

    long requestId = 10000000;

    Namespace thesNsp = NamespaceQuery.findNamespaceByName("My Thesaurus Namespace Name");

    boolean exported = subsetQry.export( addedSubset.getId(), thesNsp.getId(), requestId );

Note: The call to export subset concepts is internally preceded by first removing all concepts and associations in the given thesaurus namespace.

Once all subset concepts are successfully exported to a namespace, any calls can be made to query concepts and their associations using the SearchQuery API methods:

    DTSConcept [] cons = searchQry.findConceptsWithNameMatching( "Heart*", new DTSSearchOptions(10000, thesNsp.getId()) );

Navigational searches can also be performed on the hierarchy computed for the above subset (for Ontylog and Extension concepts only). To get all children for a concept in the subset with respect to the hierarchy of its linked Ontylog namespace:

    DTSConcept myConcept = searchQry.findConceptByName("View This Concept Hierarchy*", nameSpaceId, 

                                                       ConceptAttributeSetDescriptor.NO_ATTRIBUTES);

    Namespace ns = NamespaceQuery.findNamespaceByName("OntylogNamespaceName");

    NavChildContext cxt = navquery.getSubsetNavChildContext( (OntylogConcept)myConcept, addedSubset.getId(), ns, 

                                              ConceptAttributeSetDescriptor.NO_ATTRIBUTES );

    ConceptChild [] children = cxt.getChildren();

    for( int i=0; i < children.length; i++ ){

        System.out.println("\tConcept [" + children[i].getName() + "] is a children ");

    }

To get all the parents for a given subset concept:

    NavParentContext cxt = navquery.getSubsetNavParentContext( (OntylogConcept)myConcept, addedSubset.getId(), ns, 

                               ConceptAttributeSetDescriptor.NO_ATTRIBUTES );

    
    ConceptParent [] parents = cxt.getParents();

    for( int i=0; i < parents.length; i++ ){

       System.out.println("\tConcept [" + parents[i].getName() + "] is a parent ");

    }


Back to Top

Chapter 11 – log4j

All Apelon products leverage an open source logging project called log4j, rather than writing messages to the console (though messages can be directed to the console through log4j). At a minimum, DTS client applications must initialize log4j logging in the main() method where app startup takes place. This is easily accomplished with the following code, located in Chapter1.java.

In the import section:

import com.apelon.common.log4j.LogConfigLoader;

Later in a static method called by all example code main() methods:

LogConfigLoader logConfig = new LogConfigLoader("DtsIntroLogConfig.xml", Chapter1.class);

// This call starts log4j using the log config file DtsIntroLogConfig.xml
logConfig.loadDefault();

The above code tells log4j to initialize itself based on the settings specified in the file DtsIntroLogConfig.xml. Refer to the links at the end of this chapter for complete descriptions of log4j configuration.

If you would like your DTS client application to also log messages via log4j instead of the console, you simply create a log4j “Category” class.

In the import section:

import org.apache.log4j.Category;

Later in code, instantiate a logging category:

Category myLogCategory = Category.getInstance("mycompany.mycategory");

You can then log simple messages to the category you created:

myLogCategory.info(“Hello World”);

Or you can pass a message and a caught exception to the category, so that the message and stack trace go to the log file:

...
}
catch (Exception ex)
{
myLogCategory.error("Caught Exception",ex);
}

For a thorough explanation of lo4j, refer to the following links:

- Homepage:  http://jakarta.apache.org/log4j/docs/index.html
- Manual:  http://jakarta.apache.org/log4j/docs/manual.html
- FAQ:  http://jakarta.apache.org/log4j/docs/FAQ.html
- JavaDoc:  http://jakarta.apache.org/log4j/docs/api/index.html
- Javaworld article:  http://www.javaworld.com/javaworld/jw-11-2000/jw-1122-log4j_p.html
- Newsgroups:  http://www.geocrawler.com/lists/3/Web/8359/0/
- Troubleshooting:  http://jakarta.apache.org/log4j/docs/TROUBLESHOOT.html


Back to Top

Back to Top