A knowledge graph stores facts as three part sentences. An ontology is the rulebook that says which nouns and which verbs are allowed, what they mean, and what a machine is permitted to conclude from them. Everything below is built around a single worked example from grade 11 and 12 science.
That is one triple. One edge in the graph. A knowledge graph with a billion facts is just a billion of these, and nothing else.
Almost every formal term in this field has a plain English counterpart.
| Everyday word | Formal term | What it means | From our example |
|---|---|---|---|
| Noun, a kind of thing | owl:Class | A set of things | Process, Function, Quantity |
| Proper noun | Individual, instance | One member of a class | RadioactiveDecay, Carbon14 |
| Verb | Property, predicate | The named edge between two things | isModelledBy, hasHalfLife |
| Verb joining two things | Object property | Individual to individual | RadioactiveDecay isModelledBy ExponentialODE |
| Adjective, attribute | Datatype property | Individual to a raw value | Carbon14 hasHalfLife 5730 |
| Raw value | Literal | A string, number or date with a type | "5730"^^xsd:decimal |
| One sentence | Triple, statement | Subject plus predicate plus object | one edge in the graph |
| A rule about sentences | Axiom | A constraint or a definition | every process sits in exactly one subject |
| The name of anything | IRI | A globally unique web identifier | http://example.org/sci#Carbon14 |
ex:Process is not any particular process. It is the set of all of them.
Membership can be listed by hand, or computed by a reasoner from a condition.
ex:RadioactiveDecay is a single thing in the world that the graph talks about.
It gets an IRI so that anyone else on the web can point at the same thing.
ex:isModelledBy connects a process to a mathematical object. Give it a
domain and a range and the graph starts catching its own mistakes.
ex:hasHalfLife connects a thing to a plain number. The value is a literal,
so it is the end of the line. Nothing hangs off it.
Not data. A statement about the shape of the world, such as: a process is never a mathematical object. Reasoners work by chasing the consequences of these.
Two graphs built on different continents can agree they mean the same carbon 14 because they use the same identifier. This is the entire point of the web part of the semantic web.
The schema. Classes, properties and axioms. Small, hand crafted, changes rarely. This is the ontology.
ex:Process is a classex:isModelledBy runs from a process to a mathematical objectThe data. Individuals and the triples about them. Large, often machine generated, changes constantly. This is the bulk of the knowledge graph.
ex:RadioactiveDecay is a physical processex:RadioactiveDecay isModelledBy ex:ExponentialODEex:Carbon14 hasHalfLife 5730So what is an ontology derived knowledge graph? It simply means the A-Box was built to conform to a T-Box that already existed, rather than a schema being reverse engineered out of messy data afterwards. The rulebook came first.
RDF, RDFS, OWL, SPARQL and SHACL are not competitors. They stack. Read from the bottom up and every layer answers a question the one below it could not.
The data model. Everything is a triple. There is no table, no document, no nesting. Serialised as Turtle, N-Triples, JSON-LD or RDF/XML, all the same graph underneath.
Adds hierarchy: subClassOf, subPropertyOf, domain, range, label. Enough for a taxonomy, not enough for a theory.
Adds description logic. You can define a class by a condition instead of listing members, and a reasoner will then find facts nobody typed in.
The query language. If SQL matches rows against a schema, SPARQL matches subgraphs against a pattern.
Quality control. OWL says what may be inferred, SHACL says what counts as a valid record. Different jobs, often confused.
Names that work across organisations. Without them, merging two graphs is string matching and guesswork.
rdfs:subClassOf. The dashed red line is an axiom you must write by hand: a process can never also be a mathematical object.Why the dashed line matters. Nothing in RDF assumes two classes are separate.
If you do not state disjointness, a reasoner is perfectly happy to believe that
ex:BacterialGrowth and ex:ExponentialFunction might be the same thing.
An ontology with no disjointness axioms infers almost nothing useful.
The most useful cross subject fact available at grade 11 and 12 level is that a single differential equation quietly governs radioactive decay, first order chemical kinetics, capacitor discharge and bacterial population growth. In a syllabus these live in four separate books and nobody ever says they are the same thing. A knowledge graph exists precisely to make that connection explicit and queryable.
Where biology leaves the pattern. Exponential growth is only true while
resources are unlimited. Biology then moves to the logistic model,
\(\dfrac{dN}{dt} = rN\left(1 - \dfrac{N}{K}\right)\), which the graph records as
ex:BacterialGrowth ex:refinedBy ex:LogisticGrowth. Recording the limit of a model
is as valuable as recording the model.
Turtle is the readable serialisation of RDF. A semicolon means "same subject again", a comma means "same subject and predicate again", and a full stop ends the statement. That is nearly the whole syntax.
# prefixes: short names for long IRIs
@prefix ex: <http://example.org/sci#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
# ---------- CLASSES, the nouns ----------
ex:Entity a owl:Class .
ex:Process a owl:Class ; rdfs:subClassOf ex:Entity .
ex:PhysicalProcess a owl:Class ; rdfs:subClassOf ex:Process .
ex:ChemicalProcess a owl:Class ; rdfs:subClassOf ex:Process .
ex:BiologicalProcess a owl:Class ; rdfs:subClassOf ex:Process .
ex:MathematicalObject a owl:Class ; rdfs:subClassOf ex:Entity .
ex:Function a owl:Class ; rdfs:subClassOf ex:MathematicalObject .
ex:DifferentialEquation a owl:Class ; rdfs:subClassOf ex:MathematicalObject .
ex:Quantity a owl:Class ; rdfs:subClassOf ex:Entity .
ex:Unit a owl:Class ; rdfs:subClassOf ex:Entity .
ex:Subject a owl:Class ; rdfs:subClassOf ex:Entity .
# A process is never a mathematical object. State it, do not assume it.
ex:Process owl:disjointWith ex:MathematicalObject .
# The three science branches are mutually exclusive
[] a owl:AllDisjointClasses ;
owl:members ( ex:PhysicalProcess ex:ChemicalProcess ex:BiologicalProcess ) .
# The subjects themselves are a closed list
ex:Subject owl:equivalentClass [
a owl:Class ;
owl:oneOf ( ex:Mathematics ex:Physics ex:Chemistry ex:Biology )
] .
# ---------- OBJECT PROPERTIES, verbs joining two things ----------
ex:isModelledBy a owl:ObjectProperty ;
rdfs:label "is modelled by" ;
rdfs:domain ex:Process ;
rdfs:range ex:MathematicalObject ;
owl:inverseOf ex:models .
ex:hasSolution a owl:ObjectProperty ;
rdfs:domain ex:DifferentialEquation ;
rdfs:range ex:Function .
ex:belongsToSubject a owl:ObjectProperty , owl:FunctionalProperty ;
rdfs:comment "functional: at most one subject per process" ;
rdfs:domain ex:Process ;
rdfs:range ex:Subject .
ex:hasRateConstant a owl:ObjectProperty ;
rdfs:domain ex:Process ;
rdfs:range ex:Quantity .
ex:hasPrerequisite a owl:ObjectProperty , owl:TransitiveProperty ;
rdfs:comment "if A needs B and B needs C, then A needs C" ;
rdfs:domain ex:Entity ;
rdfs:range ex:Entity .
ex:isAnalogousTo a owl:ObjectProperty , owl:SymmetricProperty ;
rdfs:domain ex:Process ;
rdfs:range ex:Process .
# ---------- DATATYPE PROPERTIES, the adjectives ----------
ex:hasHalfLife a owl:DatatypeProperty , owl:FunctionalProperty ;
rdfs:domain ex:Process ;
rdfs:range xsd:decimal .
ex:hasFormula a owl:DatatypeProperty ;
rdfs:range xsd:string .
# ---------- PROPERTY CHAIN ----------
# studies then isModelledBy, composed into one new verb
ex:needsMathTopic a owl:ObjectProperty ;
owl:propertyChainAxiom ( ex:studies ex:isModelledBy ) .
# This is the payoff of OWL over RDFS.
# Do not list the members. State the condition and let the
# reasoner work out for itself who qualifies.
ex:ExponentiallyGovernedProcess a owl:Class ;
rdfs:label "exponentially governed process" ;
owl:equivalentClass [
a owl:Class ;
owl:intersectionOf (
ex:Process
[ a owl:Restriction ;
owl:onProperty ex:isModelledBy ;
owl:hasValue ex:ExponentialODE ]
)
] .
# Every process must be modelled by something.
# someValuesFrom means "at least one, of this kind".
ex:Process rdfs:subClassOf [
a owl:Restriction ;
owl:onProperty ex:isModelledBy ;
owl:someValuesFrom ex:MathematicalObject
] .
# Nobody will ever type the line
# ex:RadioactiveDecay a ex:ExponentiallyGovernedProcess .
# The reasoner derives it, along with the chemistry and the biology.
# ---------- the maths hub ----------
ex:ExponentialODE a ex:DifferentialEquation ;
rdfs:label "first order linear ODE" ;
ex:hasFormula "dN/dt = k*N" ;
ex:hasSolution ex:ExponentialFunction ;
ex:hasPrerequisite ex:NaturalLogarithm , ex:SeparationOfVariables ;
ex:belongsToSubject ex:Mathematics .
ex:ExponentialFunction a ex:Function ;
ex:hasFormula "N(t) = N0 * exp(k*t)" .
# ---------- physics ----------
ex:RadioactiveDecay a ex:PhysicalProcess ;
rdfs:label "radioactive decay" ;
ex:belongsToSubject ex:Physics ;
ex:isModelledBy ex:ExponentialODE ;
ex:hasRateConstant ex:DecayConstant ;
ex:hasFormula "N(t) = N0 * exp(-lambda*t)" .
ex:Carbon14 a ex:Nuclide ;
ex:undergoes ex:RadioactiveDecay ;
ex:hasHalfLife "5730"^^xsd:decimal .
ex:CapacitorDischarge a ex:PhysicalProcess ;
ex:belongsToSubject ex:Physics ;
ex:isModelledBy ex:ExponentialODE ;
ex:hasFormula "q(t) = q0 * exp(-t/(R*C))" ;
ex:isAnalogousTo ex:RadioactiveDecay .
# ---------- chemistry ----------
ex:FirstOrderReaction a ex:ChemicalProcess ;
rdfs:label "first order reaction kinetics" ;
ex:belongsToSubject ex:Chemistry ;
ex:isModelledBy ex:ExponentialODE ;
ex:hasRateConstant ex:RateConstantK ;
ex:hasFormula "[A](t) = [A]0 * exp(-k*t)" ;
ex:exampleReaction ex:DecompositionOfN2O5 .
# ---------- biology ----------
ex:BacterialGrowth a ex:BiologicalProcess ;
rdfs:label "exponential population growth" ;
ex:belongsToSubject ex:Biology ;
ex:isModelledBy ex:ExponentialODE ;
ex:hasRateConstant ex:MalthusianParameter ;
ex:hasFormula "N(t) = N0 * exp(r*t)" ;
ex:refinedBy ex:LogisticGrowth .
ex:LogisticGrowth a ex:BiologicalProcess ;
ex:belongsToSubject ex:Biology ;
ex:isModelledBy ex:LogisticODE ;
ex:hasFormula "dN/dt = r*N*(1 - N/K)" ;
ex:hasPrerequisite ex:ExponentialODE .
# ---------- the cross subject application ----------
ex:RadiocarbonDating a ex:PhysicalProcess ;
ex:belongsToSubject ex:Physics ;
ex:isModelledBy ex:ExponentialODE ;
ex:hasPrerequisite ex:RadioactiveDecay , ex:CarbonCycle , ex:Photosynthesis .
Repeat the subject. Start a new predicate.
Repeat the subject and the predicate. Add another object.
A blank node. An anonymous thing that needs no name of its own, used for restrictions.
This is the reason ontologies exist. Load the two Turtle files into a reasoner such as HermiT, Pellet or ELK, and it will assert triples that appear nowhere in the source. Press the button and watch what the graph knows that its authors never wrote.
hasPrerequisite transitive was enough.Why a curriculum planner would pay for this. The property chain
studies then isModelledBy produces needsMathTopic.
A school can now ask which maths must be taught before a given chemistry chapter, without
anyone ever having written a rule that mentions chemistry and maths in the same sentence.
| Construct | What it does | Where it appears above |
|---|---|---|
rdfs:subClassOf | Taxonomy. Every member of the child is a member of the parent | PhysicalProcess under Process |
owl:equivalentClass | A defined class. Membership is computed, not listed | ExponentiallyGovernedProcess |
owl:disjointWith | Membership in one forbids membership in the other | Process against MathematicalObject |
owl:inverseOf | Generates the reverse edge automatically | isModelledBy and models |
owl:TransitiveProperty | A to B and B to C gives A to C | hasPrerequisite |
owl:SymmetricProperty | A to B gives B to A | isAnalogousTo |
owl:FunctionalProperty | At most one value allowed | belongsToSubject, hasHalfLife |
owl:InverseFunctionalProperty | Acts as a primary key | an ISBN, a student roll number |
owl:Restriction with someValuesFrom | Must relate to at least one thing of that kind | every process has some model |
owl:Restriction with allValuesFrom | If it relates at all, only to that kind | tighten a range locally |
owl:Restriction with hasValue | Must relate to this exact individual | the ExponentiallyGovernedProcess definition |
owl:propertyChainAxiom | Composes two verbs into a third | studies plus isModelledBy gives needsMathTopic |
owl:oneOf | An enumerated class, a closed list | the four school subjects |
owl:sameAs, owl:differentFrom | Identity management across graphs | linking your IRIs to Wikidata |
Polynomial time. This is what SNOMED CT and the biomedical ontologies run on.
Designed so queries can be answered by an existing relational database underneath.
Scales to very large graphs by giving up some expressivity.
The description logic known as SROIQ(D). The default choice for a hand built ontology.
No reasoner can guarantee an answer. Avoid unless you enjoy suffering.
Start in OWL DL. Drop to EL or RL only when the reasoner gets too slow on real data.
In plain English: find every pair of topics from different subjects that are secretly the same mathematics. Nobody wrote that pairing anywhere. It falls out of the shape of the graph.
PREFIX ex: <http://example.org/sci#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
SELECT ?modelLabel ?topicA ?subjectA ?topicB ?subjectB
WHERE {
?topicA ex:isModelledBy ?model ;
ex:belongsToSubject ?subjectA .
?topicB ex:isModelledBy ?model ;
ex:belongsToSubject ?subjectB .
?model rdfs:label ?modelLabel .
FILTER ( ?subjectA != ?subjectB )
FILTER ( STR(?topicA) < STR(?topicB) )
}
ORDER BY ?modelLabel ?subjectA
| Shared model | Topic A | Topic B |
|---|---|---|
| first order linear ODE | physics RadioactiveDecay | chemistry FirstOrderReaction |
| first order linear ODE | physics RadioactiveDecay | biology BacterialGrowth |
| first order linear ODE | chemistry FirstOrderReaction | biology BacterialGrowth |
| first order linear ODE | physics CapacitorDischarge | chemistry FirstOrderReaction |
| first order linear ODE | physics CapacitorDischarge | biology BacterialGrowth |
You have just auto generated an interdisciplinary syllabus. The second FILTER stops each pair appearing twice in mirror image. The first stops a topic pairing with itself inside its own subject.
Returns a table of bindings, like SQL. There is also CONSTRUCT for building a new graph, ASK for a yes or no, and DESCRIBE for everything known about a resource.
It is written in Turtle with variables in place of some terms. The engine finds every subgraph that matches the shape.
A SERVICE clause can send part of one query to Wikidata and join the answer with your local data, live.
This single distinction causes more confusion than anything else in the field. Beginners write an OWL cardinality axiom expecting an error message, and are baffled when the reasoner quietly infers something instead.
You say a process must have at least one model. You then load a process with no model stated. OWL concludes: it must have one, we just do not know which. No error. A new anonymous fact instead.
Use OWL when you want the machine to know more.
You say a process node must carry at least one isModelledBy triple. You then
load a process with none. SHACL reports: violation, on this node, on this path, with
your message attached.
Use SHACL when you want the pipeline to refuse bad data.
@prefix sh: <http://www.w3.org/ns/shacl#> .
ex:ProcessShape a sh:NodeShape ;
sh:targetClass ex:Process ;
sh:property [
sh:path ex:belongsToSubject ;
sh:minCount 1 ;
sh:maxCount 1 ;
sh:message "Every process must sit in exactly one subject."
] ;
sh:property [
sh:path ex:isModelledBy ;
sh:minCount 1 ;
sh:class ex:MathematicalObject ;
sh:message "Every process needs at least one mathematical model."
] ;
sh:property [
sh:path ex:hasHalfLife ;
sh:datatype xsd:decimal ;
sh:minExclusive 0 ;
sh:message "Half life must be a positive number."
] .
ex:FirstOrderReaction a ex:ChemicalProcess ;
ex:belongsToSubject ex:Chemistry ;
ex:isModelledBy ex:ExponentialODE .
One subject, one model of the right class. Conforms.
ex:MysteryProcess a ex:ChemicalProcess ;
ex:belongsToSubject ex:Chemistry , ex:Physics ;
ex:hasHalfLife "-40"^^xsd:decimal .
isModelledBy triple at allThe rule of thumb. If the answer to a broken record should be a helpful error message for a human, that is SHACL. If the answer should be a new triple in the graph, that is OWL. Most real systems run both, on the same data, for different reasons.
RDF and OWL were designed for the open web, where no single system ever holds all the facts. That design choice has consequences you will meet on day one.
The graph is assumed incomplete. If it does not say that logistic growth is modelled by the exponential ODE, that does not make it false. It makes it unknown. A relational database works the opposite way: not in the table means false.
To make a reasoner conclude that a list is complete,
you must say so out loud with owl:disjointWith, owl:AllDisjointClasses
or owl:oneOf. That is exactly why the axioms earlier were written by hand.
Two different IRIs may denote the same thing unless you say
otherwise. ex:DecayConstant and ex:RateConstantK could turn out to be
one entity. Nothing forbids it.
Control it with owl:sameAs to merge and
owl:differentFrom or owl:AllDifferent to keep apart.
owl:sameAs is how a local graph gets stitched to Wikidata, and it is the technical
heart of linked open data.
Is ex:LogisticGrowth ex:isModelledBy ex:ExponentialODE true?
A closed world database has only two of these. Under a closed world, the third column silently collapses into the second, and every gap in your data becomes a confident lie. Under an open world, a gap stays a gap. That is safer for integration and more annoying for validation, which is precisely why SHACL exists as a separate closed world layer on top.
The phrase covers two families of technology with genuinely different strengths. Knowing which one someone means saves a lot of confusion in a job interview.
| RDF and OWL | Labelled property graph, such as Neo4j | |
|---|---|---|
| Unit of storage | a triple: subject, predicate, object | nodes and edges, each with a property map |
| The verb | a predicate IRI | a relationship type string |
| Attributes on an edge | awkward, needs reification or RDF-star | native and easy |
| Query language | SPARQL | Cypher, Gremlin, GQL |
| Global identifiers | built in, IRIs work across organisations | local identifiers only |
| Formal semantics and inference | strong and standardised | none by default |
| Best at | data integration, standards, sharing across institutions | fast traversal, analytics, recommendations, fraud rings |
This is how RDF closed the edge attribute gap. It matters scientifically here, because the biology claim is only conditionally true.
<< ex:BacterialGrowth ex:isModelledBy ex:ExponentialODE >>
ex:assertedBy ex:BiologyTextbookClass12 ;
ex:confidence "0.98"^^xsd:decimal ;
ex:validOnlyWhen "resources are unlimited" .
The last line is the reason biology moves on to the logistic equation. A graph that cannot record the boundary of a model will eventually mislead someone.
Four clumsy triples per statement, describing the statement as a resource. It works, and nobody enjoys it.
Add a fourth element to every triple saying which sub graph it lives in. This is what most triple stores actually do in production.
The order below is not decorative. Doing step four before step one is the single most common cause of an abandoned ontology project.
Not classes. Questions. Which physics topics use maths that has not been taught yet? Which chemistry and biology chapters share an equation? If a proposed class does not help answer one of these, delete it.
Check Linked Open Vocabularies, the OBO Foundry and schema.org. Someone has already modelled units, people, provenance and time, and modelled them better than a first attempt will.
BFO, DOLCE or SUMO, if the graph will ever be merged with someone else's. Our ex:Process class is a BFO occurrent, and saying so makes future alignment cheap.
Resist inventing a class called Modelling. Promote a verb to a class only when you genuinely need to hang attributes off the relationship itself, which is the n-ary relation design pattern.
Under the open world assumption, an ontology without disjointness infers almost nothing. This is the step everybody skips and everybody regrets.
If it infers nonsense, or if every class collapses into every other class, your axioms are wrong. Treat an unsatisfiable class the way you treat a failing test.
Keep validation out of the ontology. The ontology describes the world. The shapes describe what your particular data pipeline will accept.
Never reuse an IRI for a different meaning. Deprecate instead. Somebody somewhere has already linked to it.
| Mistake | What to do instead |
|---|---|
| Confusing a class with an individual | Is Carbon14 a class of atoms, or one nuclide type? Decide deliberately. OWL punning allows both, but only on purpose |
Using subClassOf to mean part of | Subclass means every member is also a member. A nucleus is not a subclass of an atom, it is partOf one |
| Expecting OWL to reject bad data | It will not. It infers. Put the rejection logic in SHACL |
| Forgetting the open world assumption | Missing data is unknown, never false. Say what is closed, explicitly |
| Over modelling | A hundred classes nobody queries is worse than ten that answer real questions |
| Bare numbers with no units | 5730 is meaningless on its own. Attach a unit IRI from QUDT or UCUM, or someone will compare years with seconds |
| Building the graph before writing the questions | Go back to step one. Everything else is cheaper afterwards |
SKOS for loose thesauri, Dublin Core for metadata, schema.org for search engines, FOAF for people, PROV-O for provenance, QUDT and UCUM for units.
Gene Ontology, ChEBI for chemical entities, Protein Ontology, UBERON for anatomy, all curated under the OBO Foundry.
Protege to edit, HermiT or ELK or RDFox to reason, Jena Fuseki or GraphDB or Stardog to store, rdflib and owlready2 in Python.
Graph embeddings such as TransE and ComplEx predict missing links statistically. GraphRAG lets a language model traverse explicit relationships instead of guessing them.
A note on how this page was scoped. Most projects that call themselves ontologies actually need SKOS: a thesaurus with broader and narrower terms, and nothing more. Reach for OWL when you specifically want a machine to draw conclusions, as in the reasoning section above. If you never plan to run a reasoner, you are paying for logic you will not use.