Classes are the nouns of your ontology — they represent the types of things in your domain. OWL provides rich constructs for defining how classes relate to each other.
rdfs:subClassOf
The most fundamental class relationship: "every instance of class A is also an instance of class B."
nwo:Employee a owl:Class ; rdfs:subClassOf nwo:Person . nwo:Customer a owl:Class ; rdfs:subClassOf nwo:Organization . nwo:Supplier a owl:Class ; rdfs:subClassOf nwo:Organization . nwo:Shipper a owl:Class ; rdfs:subClassOf nwo:Organization .
In plain English: Every Employee is a Person. Every Customer, Supplier, and Shipper is an Organization.
Given:
nwr:Nancy a nwo:Employee .
The reasoner infers:
nwr:Nancy a nwo:Person .
This means a SPARQL query SELECT ?x WHERE { ?x a nwo:Person } will return Nancy even though she was only explicitly typed as Employee.
SELECT ?x WHERE { ?x a nwo:Person }
owl:Thing ├── nwo:Organization │ ├── nwo:Customer │ ├── nwo:Supplier │ └── nwo:Shipper ├── nwo:Person │ └── nwo:Employee ├── nwo:Product ├── nwo:Category ├── nwo:Order ├── nwo:OrderDetail ├── nwo:Region └── nwo:Territory
owl:equivalentClass
An equivalent class provides necessary and sufficient conditions — a formal definition of what something IS. This is the most powerful class-level construct in OWL.
The key difference from subClassOf:
subClassOf
nwo:Order a owl:Class ; owl:equivalentClass [ a owl:Class ; owl:intersectionOf ( [ a owl:Restriction ; owl:onClass nwo:Customer ; owl:onProperty nwo:hasCustomer ; owl:qualifiedCardinality "1"^^xsd:nonNegativeInteger ] [ a owl:Restriction ; owl:minQualifiedCardinality "1"^^xsd:nonNegativeInteger ; owl:onClass nwo:OrderDetail ; owl:onProperty nwo:hasOrderDetail ] [ a owl:Restriction ; owl:onClass nwo:Shipper ; owl:onProperty nwo:hasShipper ; owl:qualifiedCardinality "1"^^xsd:nonNegativeInteger ] ) ] .
In plain English: Something IS an Order if and only if it:
Automatic classification: If the data contains an unnamed resource that has a customer, at least one order detail, and a shipper, a DL reasoner will automatically classify it as an Order — even without an explicit rdf:type nwo:Order statement.
rdf:type nwo:Order
Integrity checking: If something is declared as an Order but doesn't have all three, it's inconsistent.
nwo:OrderDetail a owl:Class ; owl:equivalentClass [ a owl:Class ; owl:intersectionOf ( [ a owl:Restriction ; owl:onClass nwo:Product ; owl:onProperty nwo:hasProduct ; owl:qualifiedCardinality "1"^^xsd:nonNegativeInteger ] [ a owl:Restriction ; owl:minCardinality "1"^^xsd:nonNegativeInteger ; owl:onProperty nwo:quantity ] ) ] .
In plain English: Something IS an OrderDetail if and only if it:
owl:disjointWith
Disjoint classes are classes that cannot share instances. Nothing can be both a Product and a Person. This is one of the most important constructs for data quality.
nwo:OrderDetail a owl:Class ; owl:disjointWith nwo:Organization, nwo:Person, nwo:Region, nwo:Territory .
In plain English: An OrderDetail cannot also be an Organization, Person, Region, or Territory.
If the data contains:
nwr:Entity123 a nwo:Product . nwr:Entity123 a nwo:Person .
And Product is disjoint with Person, the reasoner flags this as an inconsistency — something is wrong with the data.
This is invaluable for data quality checking. When integrating data from multiple sources, disjointness constraints catch classification errors immediately.
The OrderDetail, Product, Order, and Category are each disjoint with Organization, Person, Region, and Territory. Territory is disjoint with Organization, Person, and Region. Category is disjoint with Organization, Person, Region, and Territory. Region is disjoint with Organization and Person.
owl:AllDisjointClasses
A compact way to declare that every pair of classes in a group is mutually disjoint.
[] a owl:AllDisjointClasses ; owl:members ( nwo:Product nwo:Order nwo:OrderDetail nwo:Category ) . [] a owl:AllDisjointClasses ; owl:members ( nwo:Customer nwo:Employee nwo:Supplier nwo:Shipper ) .
In plain English:
Group 1: Product, Order, OrderDetail, and Category are all pairwise disjoint. This single statement expresses 6 disjoint pairs:
Group 2: Customer, Employee, Supplier, and Shipper are all pairwise disjoint. Again 6 disjoint pairs:
disjointWith
AllDisjointClasses
The Northwind ontology uses both because they serve different purposes:
This avoids an AllDisjointClasses of all 11 classes (which would be redundant, since Employee IS a Person, and Customer IS an Organization).
Beyond simple subclass relationships, OWL allows you to constrain what subclasses must have.
nwo:Territory a owl:Class ; rdfs:subClassOf [ a owl:Restriction ; owl:onClass nwo:Region ; owl:onProperty nwo:hasRegion ; owl:qualifiedCardinality "1"^^xsd:nonNegativeInteger ] .
In plain English: Every Territory must have exactly one Region. This is a necessary condition — all territories MUST satisfy it.
nwo:Category a owl:Class ; rdfs:subClassOf [ a owl:Restriction ; owl:onProperty nwo:includesProduct ; owl:someValuesFrom nwo:Product ] .
In plain English: Every Category must include at least one Product. An empty category would be inconsistent.