Sensemaking AI · Sensemaking Semantic Web
Submodule 1.3 · Foundations

The Resume Graph Explorer.

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.

Module 1 · Weeks 1–3 Requires Fuseki + resume-001.ttl ~90 min hands-on FOAF · dcterms · schema.org · SKOS · ESCO
Checking for Fuseki at localhost:3030…

This workbook is different.

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.

Loading resume-001.ttl into Fuseki

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.

Note on prefixes

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.

The four vocabularies in this workbook.

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.

Vocabulary overview — expand to review

Why shared vocabularies exist

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.

FOAF foaf:

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.

Dublin Core Terms dcterms:

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.

SKOS skos:

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.

schema.org schema:

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.

The ESCO interop payoff

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 modelingStart here
A personfoaf:Person + schema: properties FOAF lacks
A document or recorddcterms: metadata + foaf:primaryTopic to link to subject
A skill or categoryskos:Concept + skos:exactMatch to ESCO
An organization or institutionschema: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.

Each section of the file uses a different vocabulary.

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" .
Document ≠ person. The resume document and the person it describes are two distinct nodes with different vocabularies. 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" .
Employment as an event. Because RDF cannot put properties on edges, employment (with a role, start date, and end date) must become its own node. The custom 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> .
ESCO stubs make the interop query runnable locally. In a production graph, 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.

Five queries, five vocabularies.

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.

q01
Who is Alex Rivera and what are his basic facts?
Pattern: FOAF person facts · OPTIONAL for optional properties
foaf schema.org
Open Fuseki ↗
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 . }
}
q02
What is Alex's employment history, in chronological order?
Pattern: traversing Employment event nodes · OPTIONAL for current job · ORDER BY date
sensemaking schema.org
Open Fuseki ↗
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
q03
What are Alex's skills and their broader ESCO domains?
Pattern: SKOS interop · exactMatch/broader property path · the key payoff query
SKOS sensemaking
Open Fuseki ↗
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
q04
What does the resume document say about itself?
Pattern: Dublin Core document metadata · distinguishing document from person
dcterms foaf
Open Fuseki ↗
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 . }
}
q05
Which of Alex's skills are at "advanced" level, with their ESCO labels?
Pattern: FILTER on skill level · SKOS prefLabel · combining sensemaking and SKOS
SKOS sensemaking
Open Fuseki ↗
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

Four things to build.

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.

C1 Find all organizations mentioned anywhere in the resume

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 result and one approach

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.

C2 Find skills whose ESCO broader category is "ICT skills"

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 result and one approach

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.

C3 CONSTRUCT a simplified skills profile graph

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 result and one approach

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.

C4 Identify consecutive job transitions

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 result and one approach

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.

Now build it for your own resume.

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.

The exercise asks for three specific queries

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.

What changes when you use 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.

Five good next reads.

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.

Sample data

resume-001.ttl

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.

ESCO skill search

ESCO Classification

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.

Module exercise

Module 1 README

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.

SKOS reference

W3C SKOS Reference

Section 8 (mapping properties) covers exactMatch, closeMatch, and broadMatch — the terms that distinguish "same concept" from "similar concept" when linking to external vocabularies.

Prior workbooks

1.2 Naruto workbook

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.

Cheatsheet

Module 1 vocabulary quick-reference (coming soon)

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.