How things get globally unique names, why every value carries a type, how triples are written to files, and where they're kept.
Carolyn Inworth has spent exactly $502,529.42 with us; Redshift says so, to the cent. Now write that number into a graph twice. Label it once as a decimal and once as a float, and read both back. The decimal returns $502,529.42. The float returns $502,529.40625. Same number, same database; the only difference is one word saying what type of value it is, and that one word puts one customer's total two cents off. This chapter is about the unglamorous things that decide whether a knowledge graph is right or almost right: how things are named, how values carry their types, how triples are written in files, and which folder they're kept in.
3.1Names that mean the same thing everywhere
In Aurora, 225 means Carolyn only because it sits in the cid column of c360.customer. In Redshift, product.id = 225 is a product. A key means something only together with its table.
A graph has no tables to supply that context, and triples from different sources mix freely (Figure 2.2). So every thing needs a name that means the same everywhere and can't collide. That's what IRIs are for:
Term
What it is
Why it exists
Tiny example
IRI (Internationalized Resource Identifier)
A globally unique name in URL/URN form. It needn't point anywhere; Stardog never fetches it.
So a name means the same in any system.
urn:stardog:demos:c360:customer:225
URN
An IRI scheme (urn:) for names that aren't web locations.
Naming things without pretending they're web pages.
Our data IRIs.
tag: IRI
An IRI scheme (RFC 4151) for minting unique names without registering anything.
A cheap, collision-free vocabulary namespace.
tag:stardog:api:ecomm:firstName
Namespace / local name
The shared beginning of a family of IRIs / the part after it.
Lets a prefix abbreviate the common part (Section 3.4).
tag:stardog:api:ecomm: + firstName
One property of IRIs catches everyone: they're compared as exact strings. …customer:225, …Customer:225 and …customer:0225 are three different things. One stray character and you have a stranger.
Figure 3.1: Anatomy of our two kinds of IRI. Data IRIs are built from row keys; vocabulary IRIs share a namespace that a prefix abbreviates.
3.2Names built from keys: a preview of the IRI trick
Nobody types urn:stardog:demos:c360:customer:225 by hand. It's computed from the row's key by an IRI template: a fixed string with a slot for a column, like urn:stardog:demos:c360:customer:{cid}, so the same row gets the same name every time, from any system (Chapter 8 writes them). Every c360 thing follows one pattern, urn:stardog:demos:c360:<kind>:<key>:
Kind
Template (after urn:stardog:demos:c360:)
Key column(s)
Customer
customer:{cid}
Aurora customer.cid, and Redshift purchase.cid
Address
location:{id}
Aurora address.id (and customer.location)
Credit card · rewards account
cards:{id} · rewards:{id}
Aurora credit_card.id · rewards_account.id
Order
purchase:us:pos{id}
Redshift purchase.id
Product · category · unit price
product:{id} · category:{id} · unitprice:{id}
Redshift product.id, category.id
Vendor · industry
vendor:{vendor_name} · industry:{industry}
Redshift vendor columns
The customer template appears in both databases. Here's the actual line from the Aurora mapping (Chapter 8) and the Redshift mapping (Chapter 9):
# Aurora mapping, c360.customer
BIND(TEMPLATE("urn:stardog:demos:c360:customer:{cid}") AS ?iri)
# Redshift mapping, c360.purchase
BIND(TEMPLATE("urn:stardog:demos:c360:customer:{cid}") AS ?cust_iri)
Both produce the identical IRI. Same name, same node: this is where the two databases meet.
Figure 3.2: The IRI trick. Two rows in two engines pass their cid through the same template, so they produce the same name, and the graph joins them for free.
Both sides produce exactly the same string for Carolyn, and triples that use the same name are about the same node (Section 2.4). The blog calls this the IRI template; this book calls it the IRI trick, and Chapter 9 shows the machinery. For now, notice that it's nothing more than an agreement about spelling.
An IRI is a globally unique primary key, for things and for properties alike.
Example: customer:225 and product:225 never collide, while Aurora's and Redshift's cid 225 become the same node.
3.3Values carry their types
Term
What it is
Why it exists
Tiny example
Datatype
An IRI saying how to read a literal's characters. RDF borrows XML Schema's, prefixed xsd:.
So 22 can be compared and summed as a number, not text.
xsd:integer
Typed literal
A value written as characters, ^^, and its datatype.
The type travels with every single value.
"2011-01-21"^^xsd:date
Language tag
A marker on a string saying which language it's in.
Multilingual labels. Not used in c360.
"Milwaukee"@en
The types in c360, with SQL cousins and shorthand:
Datatype
Full form
Shorthand
SQL cousin
c360 use
xsd:string
"Carolyn"
quoted, no ^^
varchar
names, email, state
xsd:integer
"22"^^xsd:integer
22
integer
order quantity
xsd:decimal
"439.78"^^xsd:decimal
439.78
numeric
order price
xsd:float / xsd:double
"24.0"^^xsd:float
4.3978E2 (a double)
real / double precision
product list price, in the kit's model
xsd:date
"2011-01-21"^^xsd:date
none
date
purchase date
The type decides how values sort, compare and add up. As strings, "9" sorts after "10", so "top orders by quantity" with a string quantity is wrong in a way that looks almost right. FILTER(?price > 250), the heart of the "large order" rule (Chapter 10), only works if ?price is a number; YEAR(?date) only works on a real date. And then there's arithmetic.
Money is xsd:decimal, never xsd:float.
Example: 502,529.42 stored as a float comes back as 502,529.40625.
When the data lives in Aurora and Redshift, a value's type comes from the mapping. The ontology can say what type a property is meant to have, but that documents rather than converts (Chapter 6). So the working mappings cast explicitly: xsd:decimal for money, xsd:integer for quantities, xsd:date for dates. Without casts, values arrive with whatever type the database driver reports.
3.4Prefixes: nicknames for namespaces
Typing <tag:stardog:api:ecomm:firstName> every time, as you did in Chapter 2, gets old fast.
Term
What it is
Why it exists
Tiny example
Prefix
A declared nickname for a namespace.
Readability; fewer typos in long IRIs.
PREFIX : <tag:stardog:api:ecomm:>
Prefixed name
Nickname + local name, expanded by plain text concatenation.
Short to write, identical in meaning to the full IRI.
:firstName = <tag:stardog:api:ecomm:firstName>
Ours is : (also written ecomm:) = tag:stardog:api:ecomm:. The standard ones: rdf: (core terms, chiefly rdf:type), rdfs: (labels, classes; Chapter 5), owl: (richer ontology terms; Chapter 6), xsd: (datatypes) and, in the kit, so: for https://schema.org/. Full namespaces are in the Turtle file in Section 3.7.
Because expansion is pure text, one IRI can have several spellings. The kit's rules file declares stardog: as tag:stardog:api: and writes stardog:ecomm:Customer, which expands to exactly :Customer. Stardog only compares the expanded form.
3.5Turtle: triples in a file
Triples need a file format, the way tables have CSV and SQL dumps. The one people read and write is Turtle (.ttl), a compact text format, because one triple per line with full IRIs is correct but exhausting. The kit's ontology files are Turtle, and so is what Stardog Designer exports. Watch three facts shrink.
Full form, one triple per line, each ending in a full stop. Valid Turtle already:
With prefixes, a semicolon, and a. In a file, prefixes are declared with @prefix and a full stop. A semicolon means "same subject, next predicate". And a is short for rdf:type, the predicate that says what kind of thing something is:
Turtle punctuation: ; means "same subject, next predicate", . means "done with this subject", , means "same subject and predicate, another object". Indentation means nothing.
Example: customer:1 a :Customer ; :name "Jolynn Bithany" . is two triples, written out in full as customer:1 a :Customer . and customer:1 :name "Jolynn Bithany" . In SQL terms, ; is the next column of the same row and . ends the row.
A comma means "same subject and predicate, another object". This line from the kit's schema.ttl is four triples:
That's enough to read the kit's ontology, which you'll load in Chapter 6. Its opening lines say "Industry is an OWL class labeled 'Industry'", and so on:
:Industry a owl:Class ;
rdfs:label "Industry" .
:Customer a owl:Class ;
rdfs:label "Customer" .
# starts a comment (like SQL's --), and whitespace doesn't matter. One trap: SPARQL declares prefixes as PREFIX, with no @ and no full stop, a frequent typo.
3.6Named graphs: folders inside the database
Term
What it is
Why it exists
Tiny example
Named graph
A set of triples labeled with its own IRI.
To organize, secure, query and delete groups of triples as a unit.
<urn:book:ch03>
Default graph
The one unnamed graph every database has.
Where data goes when no graph is named.
Empty in our design.
Quad
A triple plus the graph it lives in.
What a store with named graphs actually records.
customer:225firstName"Carolyn" in <urn:book:ch03>
Figure 3.3: Named graphs as folders. Solid folders hold triples stored in Stardog; dashed ones are virtual graphs whose triples are produced from SQL on demand. The default graph stays empty in our design.
The build uses these folders four ways. Organization: the ontology and the rules each get a graph, so each can be reloaded alone (Chapter 6). Cleanup:DROP GRAPH removes a whole folder. Provenance: each virtual graph has a name like <virtual://redshift_c360>, so a query can say which source it means. Security: access can be granted per graph (Chapter 12).
What does a query see? By default, a Stardog query that doesn't name a graph reads only the default graph. The database option query.all.graphs widens that to all local graphs, and special names such as <tag:stardog:api:context:all> mean "every local and virtual graph" (see Querying Stardog in the docs). This book's habit is simpler: always say which graph you mean, with GRAPH <…> { … }.
3.7Hands-on: a Turtle file in its own folder
You'll write a Turtle file about Carolyn, her address and one of her orders, load it into <urn:book:ch03>, inspect the types, and delete it. Load it from a file with Studio's Load Data dialog (Path A) or by pasting an INSERT DATA (Path B). Do one, not both.
Check yourself
Why can't RDF just use 225 as Carolyn's identifier, the way the customer table does?
A graph has no table to supply context, and 225 is also a product id in Redshift. Triples from many sources mix freely, so each name must be globally unique, as urn:stardog:demos:c360:customer:225 is.
What makes Carolyn's Aurora triples and her Redshift orders connect?
Both mappings build her IRI from cid with the identical template urn:stardog:demos:c360:customer:{cid}: same name, same node. Differ by one character and the join silently returns nothing.
What is the datatype of 439.78 unquoted in Turtle, and of "439.78" in quotes?
Unquoted, it's shorthand for xsd:decimal. Quoted with no ^^, it's an xsd:string and will sort and compare as text.
Where did the two cents go, and what brought them back?
The price was mapped as xsd:float, a 32-bit binary type that can't hold most decimal fractions exactly; summing many prices accumulated the error. Casting to xsd:decimal in the mapping made the total match SQL: $502,529.42.
Write in compact Turtle: customer 225 is a Customer, first name "Carolyn", last name "Inworth".
<urn:stardog:demos:c360:customer:225> a :Customer ; :firstName "Carolyn" ; :lastName "Inworth" . with @prefix : <tag:stardog:api:ecomm:> . declared. a means rdf:type; ; repeats the subject.
You loaded a file and GRAPH <urn:book:ch03> finds nothing. Likely cause?
It was loaded without a named graph, so its triples went into the default graph.
Chapter recap
IRIs are globally unique names, compared as exact strings: urn:stardog:demos:c360:… for data, tag:stardog:api:ecomm:… for vocabulary.
Data IRIs are built from keys by templates; Aurora and Redshift both use …customer:{cid}: the IRI trick.
Every literal carries its datatype, which decides sorting, comparison and arithmetic. Money is xsd:decimal; float cost us two cents.
Prefixes are text abbreviations; : is tag:stardog:api:ecomm:.
Turtle is the readable file format: @prefix, ; for same subject, , for same predicate, a for rdf:type.
A database has one default graph and many named graphs. Always name the graph you mean; unnamed loads land in the default graph.