Modelling in OWL & TypeDB

A high-level mapping of features for users familiar with OWL, designed to help re-imagine the same data in TypeDB.

Krishnan Govindraj


OWL and TypeDB share a similar goal of enabling machines to better “understand” the data they store, by adding semantics to the data – either in the form of an OWL ontology or a TypeDB schema. In a previous post, we outlined how the difference in settings (the open-web or a database) resulted in very different choices of semantics.

This post aims to be a high-level mapping of features for users familiar with OWL, designed to help re-imagine the same data in TypeDB. We start with the basics – discussing modelling primitives, the flexibility to model complex data and approaches to reasoning. Then enumerate how to model some of the more common, less obvious features of OWL in TypeDB.

Primitives

OWL

OWL ontologies are defined in terms of classes and properties.An individual can be a member of one or more classes. Properties relate one individual to another (or to a value). Their domain and range can be specified.

:Person rdf:type owl:Class .
:Organisation rdf:type owl:Class .
:EmployedBy rdf:type owl:ObjectProperty ;
rdfs:domain :Person ;
rdfs:range :Organisation .
:FullName rdf:type owl:DatatypeProperty ;
rdfs:domain :Person ;
rdfs:range xsd:string .
# A person must have exactly one FullName property
:Person rdfs:subClassOf [
rdf:type owl:Restriction ;
owl:onProperty :FullName ;
owl:cardinality "1"^^xsd:nonNegativeInteger
] .

Classes can be defined as subclasses of (any number of) other classes – Every individual of a class is automatically in the superclass.

:Student rdf:type owl:Class;
rdfs:subclassOf :Person .
:ForProfit rdf:type owl:Class ;
rdfs:subclassOf :Organisation .
:InternsAt rdf:type owl:ObjectProperty ;
rdfs:subPropertyOf :employedBy ;
rdfs:domain :Student ;
rdfs:range :Organisation .
TypeDB

TypeDB takes a very different approach in its type-system. A type is considered intrinsic to an instance. An instance has a single type which is specified when the instance is inserted. There are 3 kinds of instantiable types – entities, relations and attributes.

  • Entities are standalone
  • Relations define role types, which connect instances who “play” these roles.

Attributes hold values, and are “owned” by instances.

define
attribute full-name, value string;
entity person,
owns full-name @card(1),
plays employed-by:employee;
entity organisation,
plays employed-by:employer;
relation employed-by,
relates employer,
relates employee;

TypeDB follows a single-inheritance model, allowing types to have at most one supertype. To model a single type “being” different things in different contexts, TypeDB uses role type-based composition instead of multiple subtypes – as we’ll see later!

Role types can also “specialize” (essentially subtype) one super role.

entity student sub person;
entity for-profit sub organisation;
relation interns-at,
relates intern as employee;
student plays interns-at:intern;

Note that a person does not play the specialised intern role. However, a student (being a person) can play the employee role in an employment relation.

Flexibility: Composition v/s Polymorphism

Suppose we want to allow a person to be employed by another person, rather than just by organisations. What are our options here? 

OWL
:jane rdf:type :Person .
:john rdf:type :Student ;
:EmployedBy :jane .

In OWL, we have multiple options. 

The first is to leave things as is and let :jane be inferred to be in :Organisation. (In certain countries this may be a legal requirement and what you want)

The second is to edit the range of :EmployedBy to be the union of the two types:

:EmployedBy rdf:type owl:ObjectProperty ;
rdfs:domain :Person ;
rdfs:range owl:unionOf(:Organisation, :Person) .
TypeDB
insert
$john isa person;
$jane isa person;
$_ isa employed-by, links (employer: $jane, employee: $john);

In TypeDB, $jane cannot  be both an organisation and a person. Unless person is a subtype of organisation we can’t use it in the employer role. TypeDB’s interface polymorphism allows multiple types to implement an interface. Hence, we simply add the following to the schema:

person plays employed-by:employer;

Now, we can see that a person can be perceived as either an employer or an employee, depending on the context.

Reasoning: axioms & functions

OWL

OWL allows us to define a class as equivalent or subclass of other classes – using operators such as intersection, union, complement.

:Father owl:equivalentClass owl:intersectionOf(:Parent, :Male).
:Child owl:equivalentClass owl:unionOf(:Son, :Daughter).

 It also allows us to define new properties using property chains

:GrandfatherOf rdf:type owl:ObjectProperty ;
owl:propertyChainAxiom ( :FatherOf :ParentOf ) .
TypeDB

Functions are the main way to do reasoning in TypeDB. The first example can’t be implemented exactly in TypeDB, since an instance can only have one type. However, we’d model parent as a parentOf relation instead (This can be done in OWL as well)

fun father() -> { person }:
match
$_ isa parentOf(parent: $f);
$f isa male; # male sub person
return { $f };

Or for the second example:

fun grandfatherOf() -> { person, person }:
match
$_ isa parentOf(child: $gc, parent: $p);
$_ isa fatherOf(child: $p, father: $gp);
return { $gp };

Notice one can define new “function instances” in terms of existing types, but can’t define existing types in terms of functions.

Union & Complement restrictions can be modelled with or and not in functions:

fun single() -> { person }:
match $p isa person; not { $_ links married(spouse: $p); };
return { $p };

We can also use this to define OWL’s transitive, symmetric & inverseOf properties.

OWL can also use complement to verify consistency (or assert the absence of) a class/property – e.g. disjoint classes, asymmetric & irreflexive properties. This cannot be done via TypeDB functions, and would need custom schema constraints (similar to @card) – TypeDB doesn’t have these constraints yet. Having said that, disjointness, asymetricity and irreflexivity can easily be expressed in a query for use in a custom validation workflow.

Multiple “classes” using relations

In OWL, it’s very natural to think of individuals as belonging to multiple classes and formulate restrictions such as “all persons who created an art object are artists“. Though TypeDB doesn’t allow an instance to have multiple types, identities which are based on it performing some role can be modelled with relations.

In this case, the following schema:

define
attribute name;
relation artist-trait,
relates artist @card(1),
relates creation @card(1..);
entity person,
owns name,
plays artist-trait:artist @card(0..1);
entity art-object @abstract, plays artist-trait:creation;
entity painting sub art-object;

and data:

insert
$picasso isa person, has name "Picasso";
$guernica isa painting;
$picasso-as-artist isa artist-trait,
links (artist: $picasso, creation: $guernica);

This approach enables modelling domains where traits are more suited than single-inheritance based polymorphism – an instance can implement multiple traits and be seen as the “composition” of these traits. The limitation is when traits overlap – Since each trait implementation is a separate relation instance, there would be duplicated interfaces for overlapping roles/attributes. 

More comparison notes

We now compare a few more useful features from the OWL specification.

Value types

TypeDB value types are boolean, integer, double, decimal, string, date, datetime, datetime-tz (datetime with timezone), duration. Attribute values can be restricted using the following schema constraints:

  • @range(start,end) for ordered value types,
  • @regex(pattern) for strings,
  • @values(value1, …) an enumeration of allowed values for discrete value types

Restricting cardinality 

Cardinality annotations @card(min[..max]) can be used on owns, relates and plays declarations. The plays cardinality would be a restriction on the inverse property  in OWL. Qualified restrictions do not suit TypeDB’s interface polymorphism.

Enumeration, Quantification & Individual uniqueness

Because OWL statements are assertions, some of these can be used both as “restrictions” for ontology consistency, and some for “inference”. 

OWLs’ DataOneOf/ObjectsOneOf
define
attribute qualifications, @values("high-school", "undergraduate", ...);
entity simpsons, owns name @key @values("homer", ...);
OWL’s DataSomeValuesFrom / ObjectsSomeValuesFrom
# via @key attribute for ObjectSomeValuesFrom
match $x isa xtype, has key $key; let $key in [...];
# note: '[]' coming soon!
OWL’s DataAllValuesFrom/ObjectAllValuesFrom
# via @key attribute for ObjectAllValuesFrom
match $x isa xtype; not { $x has key $key; not { let $key in [...]; }; };
# note: '[]' coming soon!
OWL’s DifferentIndividuals/SameIndividual

These do not apply, since TypeDB makes the Unique Name Assumption.

OWL’s NegativeObjectPropertyAssertion/NegativeDataPropertyAssertion

Not supported, due to Closed World Assumption / Horn-clause restriction.

Annotation Axioms

There is currently no way to add metadata to TypeDB types. It is being discussed in our github tracking issue #7507

Conclusion

We hope this guide serves as a quickstart for anyone familiar with OWL to think about TypeDB. For more thorough learning, we recommend following the learning journey in our documentation. As always, any questions are welcome on our discord channel.

Share this article

TypeDB Newsletter

Stay up to date with the latest TypeDB announcements and events.

Subscribe to newsletter

Further Learning

Feedback