Sensemaking AI · Module 1 cheat sheet

RDF, Turtle, SPARQL

A working reference for the foundations module — the back-pocket page for when you forget how to write a property path or which prefix `schema:` resolves to.

The standard prefix block

Paste this at the top of every Turtle file and SPARQL query. The first eight are the curriculum default; PROV-O and OWL-Time get added when reification or temporal modeling shows up.

@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#> .
@prefix dcterms: <http://purl.org/dc/terms/> .
@prefix foaf:    <http://xmlns.com/foaf/0.1/> .
@prefix schema:  <https://schema.org/> .
@prefix skos:    <http://www.w3.org/2004/02/skos/core#> .
# Add when needed:
@prefix prov:    <http://www.w3.org/ns/prov#> .
@prefix time:    <http://www.w3.org/2006/time#> .
Note

schema: resolves to https://schema.org/ with the trailing slash. Older tutorials use http://; both work but the spec is now https.

SPARQL equivalent (no @, no period)

PREFIX rdf:    <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX schema: <https://schema.org/>
PREFIX skos:   <http://www.w3.org/2004/02/skos/core#>

Turtle syntax, the essentials

Every Turtle file is a set of triples (subject, predicate, object) ending in a period. The shorthand is what makes it readable at scale.

:Naruto schema:memberOf :Konoha .
The basic triple. Subject, predicate, object, period. Every statement ends in a period.
:Naruto a schema:Person ;
        schema:memberOf :Konoha ;
        schema:name "Naruto Uzumaki" .
Semicolon reuses the same subject for the next predicate-object pair. The final period closes the whole block.
:Naruto naruto:hasJutsu :ShadowClone ,
                        :Rasengan ,
                        :SageMode .
Comma reuses the same subject AND predicate for multiple objects.
:Naruto a schema:Person .
# is the same as:
:Naruto rdf:type schema:Person .
The a keyword is shorthand for rdf:type. Use it.

Literals

"Naruto Uzumaki"                # plain string
"Naruto Uzumaki"@en             # language-tagged
"うずまきナルト"@ja               # Japanese
"1999-09-21"^^xsd:date          # typed
42                              # integer (sugar for "42"^^xsd:integer)
3.14                            # decimal
true                            # boolean
Five forms. Plain strings are valid but lose information; prefer language tags for human-readable text and typed literals for everything else.

Long strings, blank nodes, lists

:Itachi schema:description """
Member of the Uchiha clan
who joined Akatsuki after
the Uchiha massacre.
""" .
Triple-quoted strings preserve line breaks. Useful for descriptions and prose.
:Naruto schema:address [
  schema:streetAddress "Konoha Village" ;
  schema:postalCode    "00001"
] .
Anonymous blank node in square brackets. Useful for grouped properties you'll never need to reference elsewhere.
:ChuninExams naruto:teams
  ( :Team7 :Team8 :Team10 ) .
Ordered list in parentheses. Use sparingly — RDF lists are awkward to query.
Pitfall

Blank nodes are local. Two blank nodes from different files cannot be assumed identical even if they have identical properties. If you'll need to reference it from outside, use a real URI.


XSD datatypes you'll actually use

There are dozens. These are the ones that come up in real ontology work.

DatatypeWhat forExample
xsd:stringtext, when language doesn't matter"Konoha"^^xsd:string
xsd:integerwhole numbers"42"^^xsd:integer or just 42
xsd:decimalexact decimals (money, ratios)"3.14"^^xsd:decimal
xsd:doublefloating point (scientific)"6.022e23"^^xsd:double
xsd:booleantrue/falsetrue
xsd:datecalendar date (no time)"2026-05-26"^^xsd:date
xsd:dateTimedate plus time, ISO 8601"2026-05-26T14:30:00Z"^^xsd:dateTime
xsd:gYearyear only"1999"^^xsd:gYear
xsd:anyURIURI as a literal (not a node)"https://barbhs.com"^^xsd:anyURI

SPARQL essentials

SPARQL is graph-pattern matching. You describe a shape; the engine finds every binding of variables that fits the shape. The mental shift from SQL is real — but the syntax is small.

The four query forms

SELECTReturn a table of variable bindings. The default.
ASKReturn true/false — does at least one match exist?
CONSTRUCTReturn a new RDF graph built from the matches. Used to materialize inferences.
DESCRIBEReturn everything the endpoint knows about a resource.

SELECT skeleton

PREFIX schema: <https://schema.org/>

SELECT ?person ?name
WHERE {
  ?person a schema:Person ;
          schema:name ?name .
}
ORDER BY ?name
LIMIT 25

Triple patterns

Variables start with ?. Each triple pattern ends in a period (or chains with ; / , like Turtle).

?s rdf:type ?type .              # what types is ?s?
?s ?p ?o .                       # everything about ?s
?ninja schema:memberOf :Konoha . # everyone from Konoha

FILTER — narrow existing bindings

?character schema:birthDate ?date .
FILTER (?date < "1990-01-01"^^xsd:date)

?character schema:name ?name .
FILTER (REGEX(?name, "Uchiha", "i"))   # case-insensitive

FILTER EXISTS { ?character naruto:hasJutsu :Sharingan }
FILTER NOT EXISTS { ?character naruto:rivalOf :Naruto }

OPTIONAL — left-outer join

?person schema:name ?name .
OPTIONAL { ?person schema:email ?email }
OPTIONAL { ?person schema:birthDate ?dob }

If the optional pattern matches, the variable is bound. If not, the row still returns with that variable unbound.

UNION — either pattern

{ ?person schema:name ?name }
UNION
{ ?person foaf:name ?name }

Aggregation

SELECT ?village (COUNT(?ninja) AS ?count)
WHERE {
  ?ninja schema:memberOf ?village .
}
GROUP BY ?village
HAVING (?count > 5)
ORDER BY DESC(?count)

Other aggregates: SUM, AVG, MIN, MAX, GROUP_CONCAT, SAMPLE.

Property paths — SPARQL 1.1

?p foaf:knows+ :Naruto .          # 1 or more hops
?p foaf:knows* :Naruto .          # 0 or more (includes self)
?p foaf:knows? :Naruto .          # 0 or 1 (optional)
?p (foaf:knows|schema:colleague) :Naruto .       # alternation
?p schema:parent/schema:name ?gpName .           # sequence: grandparent's name
?p ^schema:parent ?child .                       # inverse: ?p is ?child's parent

CONSTRUCT — materialize a new graph

CONSTRUCT {
  ?person naruto:hasCompetency ?skill .
}
WHERE {
  ?person naruto:heldRole ?role .
  ?role schema:duration ?d .
  ?role naruto:relatedSkill ?skill .
  FILTER (?d > "P2Y"^^xsd:duration)
}

Returns RDF, not a table. The workhorse of inference materialization in Module 3.

Mental shift

A query that returns zero results doesn't mean your data is wrong. The open-world model treats absence of evidence as just absence, not falsehood. Check the pattern first — variable names that don't match, wrong predicate, missing prefix declaration.


Wikidata query patterns

Wikidata uses Q-numbers for entities and P-numbers for properties. The query service at query.wikidata.org has a forgiving UI — find Q/P numbers via the search box, then modify example queries before writing from scratch.

The minimum useful query

PREFIX wd:  <http://www.wikidata.org/entity/>
PREFIX wdt: <http://www.wikidata.org/prop/direct/>

# Books by Adrian Tchaikovsky (Q14010003)
SELECT ?book ?title
WHERE {
  ?book wdt:P50  wd:Q14010003 ;   # P50 = author
        wdt:P31  wd:Q571 ;        # P31 = instance of, Q571 = book
        rdfs:label ?title .
  FILTER(LANG(?title) = "en")
}

The label service — always add this

SELECT ?person ?personLabel
WHERE {
  ?person wdt:P69 wd:Q49108 .   # educated at MIT
  SERVICE wikibase:label {
    bd:serviceParam wikibase:language "en" .
  }
}

Any variable ending in Label gets auto-resolved to a readable name.

Common Q/P numbers worth memorizing

wdt:P31instance of
wdt:P279subclass of
wdt:P50author
wdt:P69educated at
wdt:P106occupation
wdt:P569date of birth
wd:Q5human
wd:Q571book
Speed

Wikidata times out on complex queries. Start narrow (constrain to one entity type first), then expand. Use LIMIT 100 while iterating.


Reusable vocabularies — when to reach for what

Reuse before defining. Every custom term is future maintenance burden and lost interoperability. The order below is the order to check.

PrefixReach for it when…Examples
rdfs: You need labels, comments, or class/subclass hierarchy rdfs:label, rdfs:comment, rdfs:subClassOf
schema: Modeling people, organizations, events, places, books, articles — almost everything mainstream schema:Person, schema:Organization, schema:Book, schema:TVEpisode
foaf: People and social relations, when schema.org doesn't fit foaf:Person, foaf:knows, foaf:homepage
skos: Concept schemes, taxonomies, controlled vocabularies (ESCO, MeSH, AAT) skos:Concept, skos:broader, skos:exactMatch
dcterms: Bibliographic metadata about resources — source, date, creator, license dcterms:source, dcterms:created, dcterms:creator
prov: Who said what, when, derived from where prov:wasAttributedTo, prov:wasGeneratedBy, prov:Activity
time: Time intervals, durations, before/after relations time:Interval, time:hasBeginning, time:before
owl: Class equivalence, property characteristics, restrictions — Module 2+ owl:Class, owl:sameAs, owl:equivalentClass

The decision flow


Common errors and where they bite

The errors you'll hit in the first month. Each one looks cryptic the first time.

Missing terminator

A Turtle statement that ends without ., or a continuation that ends with a dangling ; or ,, fails to parse. Always check the line above the parse error too — Turtle errors often surface one line late.

Wrong scheme on schema:

The canonical prefix is https://schema.org/, not http://schema.org/. Both resolve over the web, but mixing them within a single graph creates two distinct URIs that look identical to you and different to the engine.

Blank nodes across files

A blank node in file A is not the same as a blank node in file B even if every property matches. If a thing needs to be referenced from outside its file, give it a real URI.

FILTER can't bind

FILTER only constrains already-bound variables. It cannot introduce a variable. If your query says FILTER(?x = "y") but nothing else binds ?x, the filter operates on unbound and produces nothing.

owl:sameAs is a hammer

Asserting :A owl:sameAs :B tells the reasoner to merge every property of both URIs. Use skos:exactMatch when you mean "same referent, keep attributes separate."

Open-world surprise

"Person X has no listed children" does not mean "Person X has zero children." SPARQL queries that look for absence return empty, and OWL reasoners refuse to infer falsehood from missing data. Layer SHACL when you need closed-world for application logic.

Capitalization matters

URIs are case-sensitive. :Naruto and :naruto are different nodes. Pick a convention (capitalize classes and instances, lowercase properties) and enforce it.

Empty result means re-check the pattern

When a SPARQL query returns nothing, the first instinct is to blame the data. Usually it's the pattern — a typo in a prefix, a missing predicate, a triple that lives in a named graph the query didn't include. Verify with a broader pattern first.


Endpoints and sandboxes

ToolWhat it's for
Local Apache Jena Fuseki Your default development triplestore. Runs on localhost:3030 after install.
Wikidata Query Service The largest public SPARQL endpoint. Helper UI is forgiving; modify example queries.
DBpedia SPARQL Wikipedia-derived structured data. Older than Wikidata, sometimes faster.
RDF Playground In-browser sandbox. Write Turtle on the left, visualize as a graph, run SPARQL or SHACL. Best teaching tool for newcomers.
WebVOWL Drag-and-drop ontology visualization. Generate publication-ready screenshots without installing anything.
Protégé Desktop ontology editor. The de facto standard for OWL design work. Save in Turtle, not RDF/XML.