n a previous article I showed you how to leverage Semantic Web technology to create a relatively sophisticated sommelier application that recommended wines based on an understanding of wine styles. The application’s understanding of wine was based on a model?a pre-existing ontology of wines and their relationships. As you may remember from my previous article, an ontology is a set of concepts and relationships that can be used by people and computers to reason about a domain. The application extended the ontology through inferencing into a much richer and more complete model.
The sommelier application made a good example, but may have left you wondering how you could accomplish something similar in your own applications, for which there are most likely no pre-built ontologies available. In this article I will show you how to create a basic ontology using the Web Ontology Language (OWL). If you aren’t already familiar with ontologies and haven’t read my previous article then I recommend you do so before reading this one, as I will build on the concepts outlined in it.
Building an ontology can be a slippery slope because the world that we inhabit and attempt to model is complex, dynamic, and perceived in different ways by different people. It is all too easy when trying to model something to end up modeling far more than was truly necessary to solve your particular business problem. The business context for developing an ontology is very important in building one, so here’s a quick description of the application that this article uses to illustrate the process of constructing an ontology.
Introducing the Clothing Store Application
![]() | |
Figure 1. Clothing Storefront: Users arriving at the storefront web page see a list of clothing items available for purchase and a set of categories to navigate in order to further refine their search. |
In this article you will see how to build a basic clothing storefront application (see Figure 1) that lets users browse and purchase clothing. This clothing store organizes its inventory by type and by gender. The type hierarchy is fairly straightforward; for example, Levi’s are jeans, which are pants, which are a type of clothing.
The gender hierarchy is a little more interesting because some types of clothing items are specific to a particular gender, whereas others are equally appropriate for both men and women. For example, skirts are worn exclusively by women and ties exclusively by men, so these entire categories of clothing are gender specific. In contrast, jeans belong to a category that includes items worn by either gender; both men and women wear jeans. But even though both men and women wear jeans, they often wear different styles, so that a particular style of jeans may look fine on a woman but wouldn’t be stylish for a man to wear. Yet another case consists of items such as some types of sandals, which can be worn by anybody.
Web clothing stores often present several paths by which users can navigate to the items they intend to purchase. Most common are paths oriented by gender, for example, men’s and women’s clothing. But stores often create top-level links for other common items such as shoes and jewelry. Thus a user interested in purchasing a new pair of shoes could either navigate to the shoes through the men’s clothing path or through the shoes path. To accommodate this flexibility, the store example will need to merge the type and gender hierarchies discussed above, which is where ontologies come into play.
Developing a Clothing Catalog Ontology
I could choose to model this multi-dimensional hierarchy of products by explicitly creating relationships between each item and its corresponding type and gender categories. But this approach would necessitate more upfront work and ongoing maintenance, not to mention exponentially increase in complexity as other dimensions are added. An easier approach is to model the store’s inventory in a single inheritance ontology, as illustrated in Figure 2.
|
|
This simple hierarchy expresses a type-oriented view of the store’s clothing catalog, but doesn’t provide a path to navigate items in a gender-oriented way. By choosing to model the store’s catalog as an OWL ontology (that prescribes semantic meaning for particular types of relationships) you can infer a richer model, as illustrated in Figure 3.
As you can see, new relationships have been added to Figure 3 that provide a navigation path between gender-oriented categories. With this goal in mind, the first step is to develop the type-oriented perspective into the catalog.
Developing the Type-Oriented Hierarchy
I’ll present a step-by-step guide to developing a small ontology such as the clothing hierarchy described here. Because the focus of this article is on developing the ontology itself I won’t delve deeply into the Java code used to query it; you can see more such code in my previous article, or you can download the accompanying source code for this article.
In the spirit of limiting the modeling work to tasks that will provide value from the user’s perspective, it’s useful to start by developing some unit tests upfront. For this example I have added eight clothing items for purchase into the store’s catalog as shown in Table 1.
Name | Type |
RelaxedFitStonewashedJeans | Jeans |
StretchGauchoJeans | Jeans |
BeachTrekkerShoes | Shoes |
MonkStrapLoafers | Shoes |
PlainToeHighHeelPumps | Shoes |
ModernKnitSkirt | Skirt |
CottonTartanNecktie | Tie |
SilkMiniBoxesNecktie | Tie |
With this inventory I would expect to be able to execute the following unit tests against the application:
src/test/java/com/devx/clothing/catalog/CategoryTest.java ... @Test public void testListItems_Clothing() { // ensure eight items Category category = catalogManager.findCategoryByName("Clothing"); assertEquals(8, category.listItems().size()); } @Test public void testListItems_Jeans() { // ensure two items of type "Jeans" Category category = catalogManager. findCategoryByName("Jeans"); assertEquals(2, category.listItems().size()); } @Test public void testListItems_Shoes() { // ensure three items of type "Shoes" Category category = catalogManager. findCategoryByName("Shoes"); assertEquals(3, category.listItems().size()); } @Test public void testListItems_Skirt() { // ensure one item of type "Skirt" Category category = catalogManager. findCategoryByName("Skirt"); assertEquals(1, category.listItems().size()); } @Test public void testListItems_Ties() { // ensure two items of type "Tie" Category category = catalogManager. findCategoryByName("Tie"); assertEquals(2, category.listItems().size()); } ...
Note that the tests assert that the number of instances defined in the various categories all roll up to the root clothing category. Now you need to build enough of the clothing catalog ontology to make these tests pass.
An ontology consists of a collection of statements about the domain in the form of “
OWL-based ontologies can be stored in many forms including XML files, databases, and in-memory representations. I’ve used XML here because it is the simplest way to begin developing ontologies quickly. OWL extends the W3C Resource Description Framework (RDF), so OWL documents begin with an RDF element and a namespace declaration.
This header declares contained elements to be valid RDF elements and also declares namespaces that will be referenced later in the document. The RDF and RDFS (RDF Schema) namespaces correspond to their respective W3C standards for defining metamodels, or ontologies. The XSD namespace refers to the standard XML schema specification and will be used to define relationships to primitive elements. And the OWL namespace of course refers to the OWL specification. The base element declares a namespace for the unique elements declared in this document.
After the namespace declaration OWL documents may declare an ontology header. This header can include information useful in describing the ontology, such as imported ontologies, versioning information, and comments. Because this clothing catalog ontology is simple and doesn’t build on other ontologies, I simply added an rdf:comment element to provide a simple description for the ontology.
A simple ontology to model a clothing catalog
![]() | |
Figure 4. Jeans Class: The Jeans class contains all instances of Jeans defined in the ontology. |
Ontologies in OWL are composed of classes, instances, and properties. Classes are sets that contain like individuals and are related to each other via subclassing. In the example clothing catalog ontology the individual items that people can purchase from the store are instances, while classes in this case are the types that relate sets of instances. For example, the Jeans class contains all the Jeans instances as shown in Figure 4.
You declare the Jeans class in OWL using the owl:Class construct. The example below declares that there a class exists in the ontology with the name “Jeans.”
Now you can declare the two jeans instances in the table to be of this type:
This definition of the Jeans class and instances will pass the testListItems_Jeans unit test shown earlier, but the testListItems_Clothing test will continue to fail because the two jeans instances defined are not yet types of clothing. As illustrated in Figure 2, what we really want to express is that jeans are a type of pants and that pants are a type of clothing. In OWL you express this “type-of” relationship through subclassing as shown below:
![]() | |
Figure 5. Clothing Subclasses: Because the jeans class is a subclass of pants, which is a subclass of clothing, all jeans instances are also instances of pants and clothing. |
This declares that all instances of jeans are also instances of the pants and clothing classes as shown in Figure 5. The remaining classes and instances can similarly be defined to pass the unit tests defined above. You can do this yourself or download the accompanying source code for the completed definitions.
Adding Properties
For this ontology to be useful, you have to capture more information about the clothes in the catalog than just type-of relationships. For example, it would be useful to capture the price of a product or a unique ID for each product. Using OWL you declare properties similarly to classes and then use them to restrict class membership and relate instances to other instances and datatypes. As an example, here’s how you declare product price and ID properties in OWL:
These definitions declare the existence of two properties: pricedBy and identifiedBy. Both are datatype properties because they relate instances to primitive datatypes (float and int, respectively). In contrast, as you will see when developing the gender-oriented hierarchy a little later, object properties relate instances to other instances. You can see from the preceding example that properties can have both a domain and a range. The domain of a property is the class of instances to which the property can be applied. The range of a property is the range of allowed values to which it can be assigned. In the above example the priceBy and identifiedBy attributes can both be applied to any clothing instance and can accept any value of type float and int respectively. You can apply these properties to object instances as shown below:
6 40.0 45.0 8
You can see these relationships in Figure 6.
![]() | |
Figure 6. Datatype Properties. Datatype properties relate instances to primitive datatypes. |
As useful as it is to be able to relate individual instances to datatypes and objects it is often important to be able to reason about entire classes of instances in terms of their properties. One case in point is property restrictions. Property restrictions restrict the set of allowable properties that members of a class can have. For example, in the clothing catalog it would be nice to require that all instances of clothing available in the catalog have a price (the pricedBy property) and a product ID (the identifiedBy property). You can implement this in OWL via subclassing as shown in the following code:
1 1
The preceding example restricts the Clothing class by subclassing anonymous classes that represent the restriction. The first anonymous class includes the set of all instances with a single pricedBy property and the other is the set of all instances with a single identifiedBy property. With these restrictions in place you can reason that all instances of the clothing class have a single instance of the pricedBy and identifiedBy properties.
You can also subclass properties, and define characteristics for them such as symmetric, inverse, etc. For a more comprehensive treatment of properties consult the links in this article, which provide more information.
Developing the Gender-Oriented Ontology
So far, you’ve seen how to develop a simple type hierarchy of clothing items. But the clothing store also needs to support browsing the inventory in a gender-oriented manner. You could repeat the process of defining new classes?but how can you assign an instance to be a member of multiple classes? The answer is to create gender-oriented classes that can infer the membership of instances from a logical analysis of their properties. There are several steps required to accomplish this, which I will illustrate by showing how you can infer membership in the WomensJeans class by adding a wornBy property to clothing items.
For example, consider the list of clothing items extended with the wornBy property shown in Table 2:
Name | Type | Worn By |
RelaxedFitStonewashedJeans | Jeans | Men |
StretchGauchoJeans | Jeans | Women |
BeachTrekkerShoes | Shoes | Men and Women |
MonkStrapLoafers | Shoes | Men |
PlainToeHighHeelPumps | Shoes | Women |
ModernKnitSkirt | Skirt | Women |
CottonTartanNecktie | Tie | Men |
SilkMiniBoxesNecktie | Tie | Men |
By introducing classes for men’s and women’s clothing (MensJeans, WomensShoes, etc.) that infer their membership from the wornBy property of individual instances, the unit tests shown in Listing 1 should pass.
To implement this behavior you must create a new class named Gender, which will have two instances, male and female. This is shown below and should be familiar to you after the last section.
Next, you will need to define the wornBy property to relate clothing items to male and female gender instances.
Notice that because this property relates instances to one another it is an object property instead of a datatype property. Now the gender-oriented classes that infer membership from the wornBy property need to be created. The restrictions that clothing instances have one and only one each of the pricedBy and identifiedBy properties are referred to as necessary conditions for clothing items. That is, you are guaranteed that all clothing instances will have these properties, but not that all instances that meet these conditions must be clothing instances.
But in order to be able to infer the class of an instance from its properties, the class must define conditions that are both necessary and sufficient to infer class membership. For example, it is sufficient to infer that an instance is a member of the WomensClothing class if that instance is an instance of the Clothing class and meets the condition that its wornBy property points to the female gender instance. The following code defines just this set of conditions for women’s clothing.
The equivalentClass section is used to indicate that two classes have precisely the same membership. In this case the WomensClothing class contains all the instances of the anonymous set of instances having the wornBy property with a female gender instance as a target that are also instances of clothing.
You can easily follow a similar pattern to create the other gender-clothing classes and pass the remainder of the test cases.
Ontology Editors
By now I hope you have seen how an ontological representation of the clothing catalog simplifies the expression of a multi-parented hierarchy. There are other advantages to this approach including extensibility and consistency checking, but these are beyond the scope of this article and I refer you to the related resources section for more information. But although you might appreciate the expressiveness of OWL you, like many, might also balk at its verbosity.
![]() | |
Figure 7. Editing the Sample Ontology in Protégé: Here’s how the sample clothing ontology looks when viewed in the Protégé ontology editor. |
Fortunately, you don’t have to hand-roll these documents, because there are several ontology editors available?see this comprehensive survey of ontology editor tools. The most popular ontology editor is Protégé from Stanford University. Protégé is a comprehensive ontology editor framework with many available plug-ins that help with a wide variety of tasks, including visualization, querying, and integration with inferencing engines. In fact, Figure 2 and Figure 3 were produced using the OWLViz plugin. Figure 7 shows a screenshot of the clothing catalog ontology opened in Protégé.
Operational Datasources
Lastly I would like to point out that you can build ontologies that reference your operational data sources and vice versa. Earlier in this article you created an identifiedBy property that assigned a unique product ID to each article of clothing sold in the store. You could use an identifier such as this to relate an instance in an ontology to an entity in an operational data store. Because many things change constantly, such as the number of in-stock clothing items, it would not be a good idea to store that type of information in an ontology. An ontology is well suited for capturing the relationships between concepts in a domain, but relational databases excel at dealing with large amounts of constantly changing data.
The sample storefront application does just this with an in-memory representation of available inventory. If you run the application and click the “Buy Now” link five times, the application subsequently displays that item as sold. That’s because each time you click the link the application decrements the available inventory count in its memory-based operational data source.
But you can also relate your operational data sources back to your ontology through Uniform Resource Indicators (URIs). Because every resource in an OWL ontology is identified by a unique URI you can store the URIs in your operational data sources in such a way that applications consuming its data can easily reference corresponding ontologies to retrieve additional information and perform additional queries.