Skip to content

Your First Ontology

This tutorial walks through building a real ontology, loading it, inserting data, and querying with subsumption.

We’ll model a bookstore with books, authors, genres, and publishers. The ontology will let us:

  • Query all “Fiction” and get both “ScienceFiction” and “Fantasy” results
  • Insert a book with an author and have inverse relationships created automatically
  • Use domain/range inference to auto-type entities
@prefix : <http://example.org/bookstore#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
# Top-level classes
:CreativeWork a owl:Class .
:Person a owl:Class .
:Organization a owl:Class .
:Genre a owl:Class .
# CreativeWork hierarchy
:Book a owl:Class ; rdfs:subClassOf :CreativeWork .
:Article a owl:Class ; rdfs:subClassOf :CreativeWork .
# Person specializations
:Author a owl:Class ; rdfs:subClassOf :Person .
:Editor a owl:Class ; rdfs:subClassOf :Person .
# Organization specializations
:Publisher a owl:Class ; rdfs:subClassOf :Organization .
# Genre hierarchy
:Fiction a owl:Class ; rdfs:subClassOf :Genre .
:NonFiction a owl:Class ; rdfs:subClassOf :Genre .
:ScienceFiction a owl:Class ; rdfs:subClassOf :Fiction .
:Fantasy a owl:Class ; rdfs:subClassOf :Fiction .
:Biography a owl:Class ; rdfs:subClassOf :NonFiction .
:History a owl:Class ; rdfs:subClassOf :NonFiction .
# Disjointness
:Fiction owl:disjointWith :NonFiction .
# Object properties (link entities)
:writtenBy a owl:ObjectProperty ;
rdfs:domain :CreativeWork ;
rdfs:range :Author .
:authorOf a owl:ObjectProperty ;
owl:inverseOf :writtenBy .
:publishedBy a owl:ObjectProperty ;
rdfs:domain :Book ;
rdfs:range :Publisher .
:hasGenre a owl:ObjectProperty ;
rdfs:domain :CreativeWork ;
rdfs:range :Genre .
# Data properties (link entity to literal)
:title a owl:DatatypeProperty ;
rdfs:domain :CreativeWork ;
rdfs:range xsd:string ;
a owl:FunctionalProperty .
:name a owl:DatatypeProperty ;
rdfs:range xsd:string ;
a owl:FunctionalProperty .
:isbn a owl:DatatypeProperty ;
rdfs:domain :Book ;
rdfs:range xsd:string .
:publicationYear a owl:DatatypeProperty ;
rdfs:domain :CreativeWork ;
rdfs:range xsd:integer .

Save the complete file as bookstore.ttl and load it:

Terminal window
curl -X POST http://YOUR-INSTANCE/ontology \
-H "Content-Type: text/turtle" \
--data-binary @bookstore.ttl

Response:

{
"classes": 14,
"objectProperties": 3,
"dataProperties": 4,
"status": "success",
"message": "Ontology loaded and materializer activated"
}
Terminal window
curl -X POST http://YOUR-INSTANCE/mutate?commitNow=true \
-H "Content-Type: application/json" \
-d '{
"set": [
{
"uid": "_:dune",
"dgraph.type": "ScienceFiction",
"title": "Dune",
"isbn": "978-0441172719",
"publicationYear": 1965,
"writtenBy": { "uid": "_:herbert" }
},
{
"uid": "_:herbert",
"dgraph.type": "Author",
"name": "Frank Herbert"
}
]
}'

What OWLGraph inferred automatically:

For the book node (_:dune):

  • Added types: Fiction, Genre, Book, CreativeWork (from ScienceFiction hierarchy and domain inference)

For the author node (_:herbert):

  • Added types: Person (from Author hierarchy)
  • Created edge: authorOf → _:dune (from inverseOf on writtenBy)
Terminal window
# All fiction (returns Dune even though it was typed as ScienceFiction)
curl -X POST http://YOUR-INSTANCE/query \
-H "Content-Type: application/dql" \
-d '{ fiction(func: type(Fiction)) { title dgraph.type } }'
# All creative works
curl -X POST http://YOUR-INSTANCE/query \
-H "Content-Type: application/dql" \
-d '{ works(func: type(CreativeWork)) { title writtenBy { name } } }'
# What did Herbert author? (inverse property)
curl -X POST http://YOUR-INSTANCE/query \
-H "Content-Type: application/dql" \
-d '{ herbert(func: eq(name, "Frank Herbert")) { name authorOf { title } } }'
Terminal window
# View the Fiction class hierarchy
curl "http://YOUR-INSTANCE/ontology/introspect?class=Fiction"
{
"iri": "Fiction",
"superClasses": ["Genre"],
"subClasses": ["ScienceFiction", "Fantasy"],
"disjointWith": ["NonFiction"],
"properties": [...]
}
  • Class hierarchies drive subsumption queries — query a parent, get all descendants
  • Inverse properties create bidirectional relationships automatically
  • Domain/range inference auto-types entities based on the properties they use
  • Disjointness prevents invalid data at write time
  • Introspection lets you explore the ontology at runtime