Semantic Web for the Working Ontologist
Allemang, Hendler, and Gandon give the conceptual foundation. The useful part is the book's unusual honesty about where RDF hurts.
A pragmatic comparison of RDF and labeled property graphs through one resume graph, three queries, and the moment where ESCO/SKOS makes the interoperability story concrete.
Anyone arriving at RDF from Neo4j, or arriving at Neo4j from RDF, asks some version of the same question: what is the actual difference, and which one should I choose?
Most public answers are not neutral. Vendor pieces tend to make property graphs feel practical and RDF feel ceremonial. Semantic-web defenders tend to make RDF feel principled and property graphs feel local. Both framings contain truth, and both hide the part where real system design happens.
The honest answer is less slogan-shaped. RDF and labeled property graphs model many of the same facts, but they start from different primitives. Once you choose the primitives, the consequences cascade: syntax, query shape, metadata, reasoning, reuse, integration, tooling, and team skill. Its important to understand when to use both. The Resume Graph Explorer runs on Neo4j; its ESCO skill links are RDF, and the combination is what this comparison is really about. This page is not trying to crown a winner. It's trying to make the choice hard in the right way.
RDF starts with the triple: subject, predicate, object. Subjects and predicates are IRIs. Objects can be IRIs, literals, blank nodes, and in RDF 1.2, triple terms. Classes, vocabularies, entailment, and ontology design sit on top of that basic move.
A labeled property graph starts with nodes and relationships. Nodes carry labels and properties. Relationships carry types and properties. The graph feels closer to how many developers already think about objects and links between objects: this person worked at this company, and the relationship itself has dates and a title.
The first divergence is not whether Alice can know Bob. Both models can say that easily. The divergence starts when you want to say something about the relationship itself.
:Alice a foaf:Person .
:Bob a foaf:Person .
:Alice foaf:knows :Bob .
# But where does "since 2020" go?
# Event? RDF-star? classical reification?
CREATE (alice:Person {name: 'Alice'}),
(bob:Person {name: 'Bob'}),
(alice)-[:KNOWS {since: 2020}]->(bob);
That little “since 2020” is not a footnote. It is the first hint of the whole tradeoff. LPG gives the relationship a property and moves on. RDF asks you to decide whether that relationship is itself a thing worth naming. Annoying? Sometimes. Clarifying? Also sometimes.
The resume example is where the comparison stops being philosophical. Imagine one person with two jobs, one degree, and three skills linked to ESCO-style skill concepts. The names are generic; the structure mirrors the Resume Graph Explorer slice that anchors Module 1.
CREATE
(p:Person {
id: 'person-001',
name: 'Alex Morgan',
email: 'alex@example.org'
}),
(a:Company {id: 'acme', name: 'Acme Analytics'}),
(b:Company {id: 'northstar', name: 'Northstar Labs'}),
(mit:School {id: 'mit', name: 'MIT'}),
(kg:Skill {
id: 'skill-kg', label: 'knowledge graphs',
escoIri: 'http://data.europa.eu/esco/skill/illustrative-kg'
}),
(ml:Skill {
id: 'skill-ml', label: 'machine learning',
escoIri: 'http://data.europa.eu/esco/skill/illustrative-ml'
}),
(gov:Skill {
id: 'skill-gov', label: 'data governance',
escoIri: 'http://data.europa.eu/esco/skill/illustrative-gov'
}),
(p)-[:WORKED_AT {role: 'Data Scientist',
startDate: date('2021-03-01'), endDate: date('2023-02-28')}]->(a),
(p)-[:WORKED_AT {role: 'AI Consultant',
startDate: date('2023-03-01'), endDate: date('2025-12-31')}]->(b),
(p)-[:STUDIED_AT {degree: 'PhD Cognitive Science',
endDate: date('2016-05-20')}]->(mit),
(p)-[:HAS_SKILL {level: 'advanced'}]->(kg),
(p)-[:HAS_SKILL {level: 'advanced'}]->(ml),
(p)-[:HAS_SKILL {level: 'working'}]->(gov);
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
@prefix schema: <https://schema.org/> .
@prefix skos: <http://www.w3.org/2004/02/skos/core#> .
@prefix sensemaking: <https://sensemaking-ai.com/ns/resume#> .
@prefix : <https://sensemaking-ai.com/id/resume/example/> .
:alex a foaf:Person ;
foaf:name "Alex Morgan" ;
schema:email "alex@example.org" ;
sensemaking:held :employment-acme, :employment-northstar ;
sensemaking:completed :education-mit ;
sensemaking:hasSkill :skill-kg, :skill-ml, :skill-gov .
:acme a schema:Organization ; schema:name "Acme Analytics" .
:northstar a schema:Organization ; schema:name "Northstar Labs" .
:mit a schema:CollegeOrUniversity ; schema:name "MIT" .
:employment-acme a sensemaking:Employment ;
sensemaking:employer :acme ; sensemaking:role "Data Scientist" ;
sensemaking:startDate "2021-03-01"^^xsd:date ;
sensemaking:endDate "2023-02-28"^^xsd:date .
:employment-northstar a sensemaking:Employment ;
sensemaking:employer :northstar ; sensemaking:role "AI Consultant" ;
sensemaking:startDate "2023-03-01"^^xsd:date ;
sensemaking:endDate "2025-12-31"^^xsd:date .
:education-mit a sensemaking:Education ;
sensemaking:school :mit ; sensemaking:degree "PhD Cognitive Science" ;
sensemaking:endDate "2016-05-20"^^xsd:date .
:skill-kg a skos:Concept ; skos:prefLabel "knowledge graphs" ;
skos:exactMatch <http://data.europa.eu/esco/skill/illustrative-kg> .
:skill-ml a skos:Concept ; skos:prefLabel "machine learning" ;
skos:exactMatch <http://data.europa.eu/esco/skill/illustrative-ml> .
:skill-gov a skos:Concept ; skos:prefLabel "data governance" ;
skos:exactMatch <http://data.europa.eu/esco/skill/illustrative-gov> .
The ESCO IRIs in this example are illustrative placeholders, not claimed real ESCO concept identifiers.
Cypher keeps the employment dates directly on the relationship. That is the ergonomic win. Turtle turns employment into a named event-like node. That is the modeling cost, and also the modeling opening. Once employment is a thing, it can have sources, confidence, salary ranges, locations, supervisors, inferred competencies, and later provenance. RDF makes you pay earlier so later reuse has somewhere to attach.
The Turtle is longer. That is not a failure of concision. It is the URI tax arriving on the page. The next section is where that tax starts buying something.
The same data now gets three questions. No toy MATCH (n). The point is to notice where each stack feels native, where it feels heavy, and where the difference is more about habit than capability.
MATCH (p:Person {id: 'person-001'})-[r:WORKED_AT]->(c:Company)
RETURN r.role AS role, c.name AS employer,
r.startDate AS start, r.endDate AS end
ORDER BY r.startDate;
PREFIX sensemaking: <https://sensemaking-ai.com/ns/resume#>
PREFIX schema: <https://schema.org/>
PREFIX : <https://sensemaking-ai.com/id/resume/example/>
SELECT ?role ?employerName ?start ?end WHERE {
:alex sensemaking:held ?employment .
?employment sensemaking:role ?role ;
sensemaking:employer ?employer ;
sensemaking:startDate ?start ;
sensemaking:endDate ?end .
?employer schema:name ?employerName .
}
ORDER BY ?start
Cypher wins this one cleanly. Neo4j was built for path traversal through local graph structure. The relationship properties are right where the query wants them.
This is the punchline. With RDF, the ESCO concepts can sit in the same graph-shaped universe as the resume data. With LPG, you usually load ESCO into Neo4j or treat it as an external lookup.
// Works if ESCO has been materialized into Neo4j.
MATCH (p:Person {id: 'person-001'})-[r:HAS_SKILL]->(s:Skill)
OPTIONAL MATCH (s)-[:EXACT_MATCH]->(esco:ESCOConcept)
OPTIONAL MATCH (esco)-[:BROADER]->(broader:ESCOConcept)
RETURN s.label AS skill, r.level AS level,
esco.iri AS escoIri, broader.label AS broaderCategory
ORDER BY skill;
PREFIX sensemaking: <https://sensemaking-ai.com/ns/resume#>
PREFIX skos: <http://www.w3.org/2004/02/skos/core#>
PREFIX : <https://sensemaking-ai.com/id/resume/example/>
SELECT ?skillLabel ?esco ?broaderLabel ?relatedLabel WHERE {
:alex sensemaking:hasSkill ?skill .
?skill skos:prefLabel ?skillLabel ;
skos:exactMatch ?esco .
OPTIONAL { ?esco skos:broader/skos:prefLabel ?broaderLabel . }
OPTIONAL { ?esco skos:related/skos:prefLabel ?relatedLabel . }
}
ORDER BY ?skillLabel
The first time I followed `skos:broader` from a resume skill into ESCO's hierarchy without writing one line of integration code, the comparison stopped being theoretical. This is where RDF earns its keep. SKOS is already RDF. ESCO is published using RDF, OWL, and SKOS. The SPARQL query isn't "integrating" in the glue-code sense; it's following predicates the external vocabulary already understands.
MATCH (p:Person {id: 'person-001'})-[r1:WORKED_AT]->(c1:Company)
MATCH (p)-[r2:WORKED_AT]->(c2:Company)
WHERE r1.endDate <= r2.startDate AND r1 <> r2
WITH r1, c1, r2, c2
ORDER BY r1.startDate, r2.startDate
RETURN r1.role AS fromRole, c1.name AS fromEmployer,
r2.role AS toRole, c2.name AS toEmployer
LIMIT 1;
PREFIX sensemaking: <https://sensemaking-ai.com/ns/resume#>
PREFIX schema: <https://schema.org/>
PREFIX : <https://sensemaking-ai.com/id/resume/example/>
SELECT ?fromRole ?fromEmployer ?toRole ?toEmployer WHERE {
:alex sensemaking:held ?from, ?to .
?from sensemaking:role ?fromRole ;
sensemaking:employer/schema:name ?fromEmployer ;
sensemaking:endDate ?fromEnd .
?to sensemaking:role ?toRole ;
sensemaking:employer/schema:name ?toEmployer ;
sensemaking:startDate ?toStart .
FILTER (?fromEnd <= ?toStart && ?from != ?to)
}
ORDER BY ?fromEnd ?toStart
LIMIT 1
The transition query is a wash. Cypher feels familiar if your mental model is path-first. SPARQL is perfectly fine once employment has become a node. That is useful to notice: not every comparison has a dramatic winner.
RDF is more verbose because every important term can be globally identifiable. The same fact takes more characters in Turtle than in Cypher. The file has more prefixes. The query has more declarations. Diffs get longer. Beginners feel as if the syntax is asking them to write the whole internet before breakfast.
The question is not whether the verbosity is a cost. It is. The question is what the cost buys.
For an isolated application, the URI tax can be pure overhead. If one team owns the data, one database serves the application, and the main job is fast traversal through local relationships, LPG is usually the humane choice. You get productive syntax, relationship properties, graph algorithms, and a developer experience that feels direct.
For a graph that needs to participate in a broader ecosystem, the tax becomes infrastructure. ESCO, FIBO, schema.org, Wikidata, Dublin Core, SKOS, PROV-O: these are not just labels. They are shared commitments about what terms mean and how other systems can meet your data halfway. The cost arrives early. The value compounds later.
In the work I do — building hybrid LLM and knowledge-graph systems like the Digital Twin — the future integration surface isn't theoretical. It's where the next twelve months of any serious project lives. Vector similarity gets you retrieval; graph traversal gets you reasoning. Choosing the wrong primitives early is expensive in a way that becomes obvious only after the integration would have helped.
I have applications where both stacks make sense. This section is also the answer to Module 1's 30-second test: explain to a working engineer, without slogans, why someone would choose RDF over a labeled property graph or vice versa. The heuristics below are how I actually answer that question when it comes up.
| Pressure | Usually favors | Why |
|---|---|---|
| Path traversal inside one product | LPG | Traversal ergonomics and edge properties earn their keep. |
| Vocabulary reuse across systems | RDF | Global IRIs and shared vocabularies are the point. |
| Formal reasoning | RDF | RDF/RDFS/OWL have formal semantics; LPG semantics are application-defined. |
| Provenance-heavy assertions | Depends | LPG edge properties are easy; RDF reification patterns are more portable but more expensive. |
| Fast team ramp-up | The stack the team already understands | A correct LPG model beats a confused RDF model, and the reverse is also true. |
That last row is not a caveat. It is part of the model. Technical fit and team fit are not separate decisions. A graph nobody can maintain is not a good graph.
For curriculum readers: continue the Module 1 reading, especially Allemang chapters 1–3 if they are not done yet. Then do Exercise 1.3, the Resume Graph Explorer RDF slice. Write the same three queries in Cypher and SPARQL before reading more takes over. The feeling of the comparison is the learning.
For drive-by readers: Sensemaking Semantic Web is a 12-week self-directed curriculum through RDF, OWL, SPARQL, and modern knowledge graphs. The broader arc lives in the Module 1 README, and the Module 1 cheat sheet is the back-pocket syntax reference. A glossary page is planned at the curriculum glossary.
These are not equal-purpose links. Read them for different kinds of evidence.
Allemang, Hendler, and Gandon give the conceptual foundation. The useful part is the book's unusual honesty about where RDF hurts.
Hogan et al. place RDF and property graphs in the same landscape. Dense, but excellent for shared vocabulary across camps.
Warren and Mulholland compare user modeling behavior instead of arguing from taste. Skim the findings when debates get too confident.
Author of Learning SPARQL. Ongoing posts on SPARQL patterns and the LLM + KG intersection. The companion to anyone working through this material in practice.
Use this to see where the side-by-side page fits in the broader foundations module, especially Exercise 1.3.
The largest public SPARQL endpoint. The fastest way to feel SPARQL on real data — modify example queries before writing from scratch.