Synthesis doc
Sub-module 1.3 synthesis
The reading companion for this workbook. Covers all four vocabularies — FOAF, Dublin Core, SKOS, schema.org — plus ESCO interop and the reuse-before-define decision table.
Five SPARQL queries on a real-shaped resume graph — FOAF for the person, Dublin Core for the document, schema.org for organizations, SKOS for skills, and the ESCO interop query that makes it all matter. Load resume-001.ttl into Fuseki before starting.
The 1.1 and 1.2 workbooks used pre-loaded narrative datasets (Naruto, mythology) to teach query patterns. This workbook uses a resume graph — the same shape of data that Exercise 1.3 asks you to build for your own career history. The vocabulary choices in the queries here are the vocabulary choices you will make in that exercise.
The workbook queries against resume-001.ttl, a sample resume for Alex Rivera — the same fictional person introduced in the 1.1 synthesis document. Load it into Fuseki before running any query.
From the starter-kit root: fuseki-server --config=fuseki/config.ttl. In the Fuseki UI, upload modules/01-foundations/artifacts/resume-graph/ttl/resume-001.ttl to the mythology dataset (or a new resume dataset — update the query links accordingly). The queries use the base IRI https://sensemaking-ai.com/resume/alex-rivera/001/ — verify the file loaded by running SELECT * WHERE { ?s ?p ?o } LIMIT 10 and checking that results appear.
This workbook uses five vocabulary prefixes: foaf:, dcterms:, schema:, skos:, and sensemaking:. Every query declares all five. You will use this same prefix block in Exercise 1.3.
If you've worked through the Sub-module 1.3 synthesis doc, skip ahead — this section condenses that material for quick reference. If you haven't, expand below before running the query lab.
A predicate in RDF is a URI — globally identifiable. Shared vocabularies make that global agreement possible across systems, organizations, and countries. The core rule: reuse before defining. Always check whether FOAF, schema.org, SKOS, or Dublin Core has an appropriate term before creating one in your custom namespace.
For people and social connections. foaf:Person is the standard class for human beings in Linked Data. Pair with schema: properties where FOAF lacks them — schema:jobTitle and schema:telephone have no close FOAF equivalents. Mixing both vocabularies on the same node is normal and encouraged.
For document metadata. Use dcterms:title, dcterms:created, dcterms:language on the document node — not the person the document describes. Always use dcterms: (the refined namespace), never the older dc: which has weaker formal semantics.
For controlled vocabularies. Skills in a resume graph are skos:Concept nodes — units in a vocabulary, not OWL classes. skos:exactMatch links to ESCO concepts without merging properties (unlike owl:sameAs). skos:broader lets SPARQL traverse the ESCO skill hierarchy.
Broad coverage, web-readable. Best for organizations, job titles, dates, URLs. Use https://schema.org/ (not http:// — they switched in 2019 and old tutorials still show the wrong prefix). Permissive ranges make it poor for formal OWL reasoning, but excellent for anything a search engine or external system needs to understand.
ESCO (European Skills, Competences, Qualifications and Occupations) is published as RDF using SKOS — ~3,000 occupation concepts and ~14,000 skill concepts in a structured hierarchy. When a skill node has skos:exactMatch pointing to an ESCO IRI, your resume graph connects directly to that taxonomy without any import or string-matching glue. Query q03 in the lab below runs skos:exactMatch/skos:broader to follow that connection. The ESCO stubs in resume-001.ttl let the query work locally; with the real ESCO dataset loaded, the same query reaches the full occupational hierarchy.
| What you're modeling | Start here |
|---|---|
| A person | foaf:Person + schema: properties FOAF lacks |
| A document or record | dcterms: metadata + foaf:primaryTopic to link to subject |
| A skill or category | skos:Concept + skos:exactMatch to ESCO |
| An organization or institution | schema:Organization or subclass |
| An event (employment, education) | Custom class (sensemaking:Employment) inheriting from schema:Event |
Full coverage with pain points (dc: vs dcterms:, skos:exactMatch vs owl:sameAs): Sub-module 1.3 synthesis doc.
Read resume-001.ttl before running queries. Three things to understand before the query lab begins.
# Dublin Core describes the document <https://sensemaking-ai.com/resume/alex-rivera/001> a sensemaking:Resume ; dcterms:title "Alex Rivera — Resume" ; dcterms:created "2024-11-01"^^xsd:date ; dcterms:language "en" ; foaf:primaryTopic <person> . # FOAF describes the person <person> a foaf:Person ; foaf:name "Alex Rivera" ; foaf:mbox <mailto:alex@example.com> ; schema:jobTitle "Data Scientist" .
dcterms: describes the document (when created, what language, what it is about). foaf:Person describes the human being. foaf:primaryTopic connects them. q04 queries the document node; q01 queries the person node. They are different subjects in the graph.
# Employment: a first-class event node <emp1> a sensemaking:Employment ; sensemaking:employer <org_riverbend> ; sensemaking:role "Junior Data Analyst" ; sensemaking:startDate "2020-01-15"^^xsd:date ; sensemaking:endDate "2022-06-30"^^xsd:date . # Organization: schema.org <org_riverbend> a schema:Organization ; schema:name "Riverbend Analytics" .
sensemaking:Employment class carries the relationship-level facts. This is the design forced by the data model — the same pattern you will use in Exercise 1.3 when you model your own career history. Query q02 traverses these nodes.
# Skills: SKOS concepts linked to ESCO <skill_python> a skos:Concept ; skos:prefLabel "Python (programming language)"@en ; skos:exactMatch <esco_python> ; sensemaking:skillLevel "advanced" . # ESCO stub shows the broader hierarchy <esco_python> a skos:Concept ; skos:prefLabel "use a scripting language: Python"@en ; skos:broader <esco_programming_languages> .
skos:exactMatch would point to real ESCO IRIs — and the broader hierarchy would be in ESCO's published dataset, not inline here. The stubs in resume-001.ttl mirror the real ESCO structure so q03 works without loading a 200MB vocabulary dataset. The query pattern is identical to what would run against live ESCO.
Each query demonstrates how one or more of the vocabularies in resume-001.ttl are accessed in SPARQL. The colored tags show which vocabulary each query primarily exercises.
PREFIX foaf: <http://xmlns.com/foaf/0.1/> PREFIX schema: <https://schema.org/> PREFIX dcterms: <http://purl.org/dc/terms/> PREFIX skos: <http://www.w3.org/2004/02/skos/core#> PREFIX sensemaking: <https://sensemaking-ai.com/ns/> SELECT ?name ?email ?jobTitle WHERE { ?person a foaf:Person ; foaf:name ?name . OPTIONAL { ?person foaf:mbox ?email . } OPTIONAL { ?person schema:jobTitle ?jobTitle . } }
PREFIX foaf: <http://xmlns.com/foaf/0.1/> PREFIX schema: <https://schema.org/> PREFIX dcterms: <http://purl.org/dc/terms/> PREFIX skos: <http://www.w3.org/2004/02/skos/core#> PREFIX sensemaking: <https://sensemaking-ai.com/ns/> SELECT ?orgName ?role ?start ?end WHERE { ?person a foaf:Person ; sensemaking:hasEmployment ?emp . ?emp sensemaking:employer ?org ; sensemaking:role ?role ; sensemaking:startDate ?start . ?org schema:name ?orgName . OPTIONAL { ?emp sensemaking:endDate ?end . } } ORDER BY ?start
PREFIX foaf: <http://xmlns.com/foaf/0.1/> PREFIX schema: <https://schema.org/> PREFIX dcterms: <http://purl.org/dc/terms/> PREFIX skos: <http://www.w3.org/2004/02/skos/core#> PREFIX sensemaking: <https://sensemaking-ai.com/ns/> SELECT ?skillLabel ?level ?domainLabel WHERE { ?person a foaf:Person ; sensemaking:hasSkill ?skill . ?skill skos:prefLabel ?skillLabel ; sensemaking:skillLevel ?level ; skos:exactMatch/skos:broader ?domain . ?domain skos:prefLabel ?domainLabel . FILTER (LANG(?skillLabel) = "en") FILTER (LANG(?domainLabel) = "en") } ORDER BY ?level ?skillLabel
PREFIX foaf: <http://xmlns.com/foaf/0.1/> PREFIX schema: <https://schema.org/> PREFIX dcterms: <http://purl.org/dc/terms/> PREFIX skos: <http://www.w3.org/2004/02/skos/core#> PREFIX sensemaking: <https://sensemaking-ai.com/ns/> SELECT ?title ?created ?modified ?language ?personName WHERE { ?doc a sensemaking:Resume ; dcterms:title ?title ; dcterms:created ?created ; dcterms:language ?language ; foaf:primaryTopic ?person . ?person foaf:name ?personName . OPTIONAL { ?doc dcterms:modified ?modified . } }
PREFIX foaf: <http://xmlns.com/foaf/0.1/> PREFIX schema: <https://schema.org/> PREFIX dcterms: <http://purl.org/dc/terms/> PREFIX skos: <http://www.w3.org/2004/02/skos/core#> PREFIX sensemaking: <https://sensemaking-ai.com/ns/> SELECT ?localLabel ?escoLabel ?level WHERE { ?person a foaf:Person ; sensemaking:hasSkill ?skill . ?skill skos:prefLabel ?localLabel ; sensemaking:skillLevel ?level ; skos:exactMatch ?esco . ?esco skos:prefLabel ?escoLabel . FILTER (?level = "advanced") FILTER (LANG(?localLabel) = "en") FILTER (LANG(?escoLabel) = "en") } ORDER BY ?localLabel
These challenges extend the query patterns toward what Exercise 1.3 will ask you to do with your own resume data. Write each query before opening the solution.
Alex's resume mentions three organizations: Riverbend Analytics (employer), Northwind Health (employer), and Midstate University (school). Write a single query that returns all three organization names using UNION — one branch for employers, one for educational institutions. Return distinct names sorted alphabetically.
Expected: 3 rows — Midstate University, Northwind Health, Riverbend Analytics.
PREFIX foaf: <http://xmlns.com/foaf/0.1/> PREFIX schema: <https://schema.org/> PREFIX sensemaking: <https://sensemaking-ai.com/ns/> SELECT DISTINCT ?orgName WHERE { ?person a foaf:Person . { ?person sensemaking:hasEmployment ?emp . ?emp sensemaking:employer ?org . } UNION { ?person sensemaking:hasEducation ?edu . ?edu sensemaking:institution ?org . } ?org schema:name ?orgName . } ORDER BY ?orgName
This UNION pattern — "find things that are either employers OR educational institutions" — is directly reusable in Exercise 1.3. When you add your own history, both branches return results from the same query without duplicating code.
The ESCO stub hierarchy in resume-001.ttl has two levels of skos:broader between each skill and the top-level "ICT skills" concept. Use skos:exactMatch/skos:broader+ to find all of Alex's skills that eventually reach the "ICT skills" concept, regardless of how many hops it takes. Return skill labels and how many hops away ICT skills is.
Expected: All three skills reach "ICT skills" via two hops. A simpler version without the hop count:
PREFIX foaf: <http://xmlns.com/foaf/0.1/> PREFIX skos: <http://www.w3.org/2004/02/skos/core#> PREFIX sensemaking: <https://sensemaking-ai.com/ns/> SELECT DISTINCT ?skillLabel WHERE { ?person a foaf:Person ; sensemaking:hasSkill ?skill . ?skill skos:prefLabel ?skillLabel ; skos:exactMatch/skos:broader+ ?top . ?top skos:prefLabel "ICT skills"@en . FILTER (LANG(?skillLabel) = "en") } ORDER BY ?skillLabel
The skos:broader+ path finds the top concept at any depth. In the full ESCO dataset, some skills are many levels deep. This pattern is how you query SKOS hierarchies without knowing the depth in advance.
Use CONSTRUCT to generate a new RDF graph containing only the person-to-ESCO-domain triples — stripping out the intermediate skill nodes. The output should be triples of the form: <person> sensemaking:hasSkillDomain <domain>. This is vocabulary transformation — deriving a simplified view from the full graph.
Expected: 3 triples — one per skill, each linking the person directly to the ESCO domain concept, bypassing the intermediate skill node.
PREFIX foaf: <http://xmlns.com/foaf/0.1/> PREFIX skos: <http://www.w3.org/2004/02/skos/core#> PREFIX sensemaking: <https://sensemaking-ai.com/ns/> CONSTRUCT { ?person sensemaking:hasSkillDomain ?domain . } WHERE { ?person a foaf:Person ; sensemaking:hasSkill ?skill . ?skill skos:exactMatch/skos:broader ?domain . }
Switch Fuseki's result format to Turtle to read the output. CONSTRUCT is how you produce derived graphs — the same mechanism that lets a reasoner materialize inferred triples. Here you are doing it explicitly rather than declaring it as a rule.
Write a query that finds pairs of jobs where one ended before the other began. Return the previous employer name, next employer name, and the gap between end and start dates. This is Exercise 1.3's third query — the consecutive-transition query that the Module 1 README describes.
Expected: 1 row — Riverbend Analytics → Northwind Health, transition from 2022-06-30 to 2022-07-01 (one day gap).
PREFIX foaf: <http://xmlns.com/foaf/0.1/> PREFIX schema: <https://schema.org/> PREFIX sensemaking: <https://sensemaking-ai.com/ns/> SELECT ?prevOrg ?nextOrg ?endDate ?startDate WHERE { ?person a foaf:Person ; sensemaking:hasEmployment ?e1 , ?e2 . FILTER (?e1 != ?e2) ?e1 sensemaking:employer ?prev ; sensemaking:endDate ?endDate . ?e2 sensemaking:employer ?next ; sensemaking:startDate ?startDate . ?prev schema:name ?prevOrg . ?next schema:name ?nextOrg . FILTER (?endDate < ?startDate) } ORDER BY ?endDate
The self-join (hasEmployment ?e1 , ?e2) binds two different employment nodes for the same person. FILTER (?e1 != ?e2) prevents the same employment pairing with itself. Then FILTER on dates selects only pairs where one ended before the other began. This is the canonical SPARQL consecutive-transition pattern.
Exercise 1.3 asks you to take a representative subset of your own career history and produce a Turtle file using the same vocabulary decisions you just queried. resume-001.ttl is the template. The five queries in this workbook are the three queries the exercise asks you to write — plus two more.
All jobs in chronological order (q02 pattern) · All skills with ESCO codes (q03 pattern) · All transitions between consecutive jobs (C4 pattern). Run all three against Alex Rivera's data first to confirm you understand the query structure, then adapt them to your own data.
The IRIs in your file will use your own namespace (e.g., <https://sensemaking-ai.com/ns/dagny#> if building Dagny's graph). The vocabulary choices remain the same. Skills are still skos:Concept with skos:exactMatch to ESCO. Employers are still schema:Organization. The document node still gets dcterms: metadata. The patterns are stable; the data changes.
The Module 1 README has the full exercise steps. The ESCO portal at esco.ec.europa.eu lets you search for skill concept IRIs by label — copy the full IRI for each skill you want to link and use it in your skos:exactMatch triple.
The reading companion for this workbook. Covers all four vocabularies — FOAF, Dublin Core, SKOS, schema.org — plus ESCO interop and the reuse-before-define decision table.
The Turtle file for this workbook. Read it alongside the queries. The inline comments explain each vocabulary choice. Use it as the structural template for Exercise 1.3.
Browse and search ESCO skill concepts by label. Find the IRI for any skill you want to link in your own resume graph. The full dataset is also downloadable as RDF.
Exercise 1.3 steps in full: which neo4j data to export, how to structure the Turtle file, which three SPARQL queries to write, and what to commit to the repo.
Section 8 (mapping properties) covers exactMatch, closeMatch, and broadMatch — the terms that distinguish "same concept" from "similar concept" when linking to external vocabularies.
If the UNION and property path patterns in C1 and C2 felt shaky, the 1.2 workbooks drill those patterns extensively on simpler data. Return there before Exercise 1.3 if needed.
FOAF / Dublin Core / SKOS / schema.org key terms on one page — what each term is for, common mistakes (dc: vs dcterms:, skos:exactMatch vs owl:sameAs), and the prefix block. Available when Module 1 materials are complete.