Your First Ontology
This tutorial walks through building a real ontology, loading it, inserting data, and querying with subsumption.
The Domain: A Bookstore
Section titled “The Domain: A Bookstore”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
Step 1: Define Classes
Section titled “Step 1: Define Classes”@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 .Step 2: Define Properties
Section titled “Step 2: Define Properties”# 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 .Step 3: Load the Ontology
Section titled “Step 3: Load the Ontology”Save the complete file as bookstore.ttl and load it:
curl -X POST http://YOUR-INSTANCE/ontology \ -H "Content-Type: text/turtle" \ --data-binary @bookstore.ttlResponse:
{ "classes": 14, "objectProperties": 3, "dataProperties": 4, "status": "success", "message": "Ontology loaded and materializer activated"}Step 4: Insert Data
Section titled “Step 4: Insert Data”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)
Step 5: Query with Subsumption
Section titled “Step 5: Query with Subsumption”# 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 workscurl -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 } } }'Step 6: Introspect
Section titled “Step 6: Introspect”# View the Fiction class hierarchycurl "http://YOUR-INSTANCE/ontology/introspect?class=Fiction"{ "iri": "Fiction", "superClasses": ["Genre"], "subClasses": ["ScienceFiction", "Fantasy"], "disjointWith": ["NonFiction"], "properties": [...]}What You Learned
Section titled “What You Learned”- 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