Everything Stardog knows is a three-part sentence. By the end of this chapter you'll read, write and query them.
A riddle to start. How many columns does a table need to hold every fact your company knows: every customer's email, every order's price, which address belongs to whom, which product is in which category? Your Aurora and Redshift schemas use about fifty columns across eight tables; a real company has thousands. The answer is three: subject, predicate, object. That's the entire data model of this book's world, and once you read those three-part sentences fluently, the rest is mostly vocabulary. We'll start with Carolyn Inworth of Milwaukee, customer 225.
2.1One shape for everything
Stardog stores data using RDF, the Resource Description Framework, a W3C standard. RDF is to Stardog what the relational model is to Postgres. Its central idea fits on one line: every fact is a triple.
subject → predicate → object
Read one aloud and it's a plain English sentence. Customer 225has first name"Carolyn". Order 0e7e…was purchased bycustomer 225. Here are the terms, each with what it is, why it exists, and a tiny example:
Term
What it is
Why it exists
Tiny example
RDF
A standard data model in which all information is triples.
One shape for all data, so data from anywhere combines without reconciling schemas.
The six facts you'll write in Section 2.6.
Triple
One fact: subject, predicate, object.
The smallest unit you can add, delete, query or secure.
customer:225email"cinworth68@g.co"
Subject
The thing the fact is about.
Every fact needs an anchor.
customer:225
Predicate
The property or relationship being stated.
Names what kind of fact this is, like a column name, but global.
firstName, purchasedBy
Object
The value, or the other thing at the end of the relationship.
The fact's content.
"Carolyn", or customer:225
Graph
A set of triples.
It's what a database holds; drawn out, it's literally a network (Section 2.4).
Everything in kit-c360.
No tables, no columns, no rows, no NULLs. Just a set of sentences.
Every fact is one sentence: subject → predicate → object.
Example: customer:225 :firstName "Carolyn"
2.2A SQL row becomes triples
Here's a real row from your Aurora database. Find it in DBeaver with SELECT * FROM c360.customer WHERE cid = 225, and its address with SELECT * FROM c360.address WHERE id = 'b80ea2ce-2361-408a-b443-721594cbbb35'. Press Play to watch them turn into triples.
Done: two rows, six triples. One thing per primary key, one arrow per column.
Figure 2.1: Two SQL rows become six triples. Rounded nodes are things; square boxes are plain values. Each arrow is one triple.
Three rules did all the work, the same rules the mappings in Chapter 8 follow:
The primary key becomes the subject's name. Every triple from the row is about customer 225. The key isn't a triple of its own; it's baked into the name, customer:225 (Chapter 3 gives the full-length names).
Each ordinary column becomes one triple.first_name = 'Carolyn' becomes customer:225firstName"Carolyn".
A foreign key becomes an arrow to another thing.location holds a reference, not a value you'd show anyone. So the object is the address itself, location:b80e…, which then has its own triples.
Two more things startle SQL people. NULL disappears. Carolyn's picture column is empty, so there's simply no picture triple: the fact is absent. (That's the seed of the open-world assumption, Chapter 5.) And multiple values are free. Two emails are two email triples: no email2 column, no child table, no schema change.
2.3Things and literals
Figure 2.1 draws its objects in two shapes on purpose, and the distinction matters early:
Term
What it is
Why it exists
Tiny example
IRI
A globally unique name, written like a URL or URN, in angle brackets.
So a thing or property means the same everywhere, even when data from different systems is mixed (Chapter 3).
<urn:stardog:demos:c360:customer:225>
Resource (a "thing")
Anything named by an IRI: a customer, an address, a property.
Things can have their own facts: arrows can start from them.
The address node, which has a city and a state.
Literal
A plain value: text, number, date. Always an object, never a subject.
Facts eventually bottom out in values you display, filter and add up.
"Milwaukee", 22, 439.78
Why is the address a thing but the city a literal? Because the address has facts of its own and, in our model, the city doesn't. You decide it the way you decide whether something deserves its own table: if it has attributes, or other things point to it, make it a thing. To record Milwaukee's population, you'd promote the city to a thing (city:milwaukee-wi) and hang facts off it.
Things have IRIs and can have facts of their own; literals are plain values and are always dead ends.
Example: customer:225 :address location:b80e… points to a thing; "Milwaukee" is a literal.
2.4The graph as a picture, and why merging is free
Figure 2.1's bottom half isn't an illustration of the data. It is the data. Subjects and objects are nodes; each triple is an edge labeled with its predicate. That's why the whole field is called knowledge graphs.
This matters enormously in Chapter 9. Because every triple has the same shape, two sets of triples combine by simply pouring them together, with no schemas to reconcile. Where both sets use the same name for the same thing, the pictures join up at that node by themselves.
Here's a real order from Redshift (SELECT * FROM c360.purchase WHERE id = '0e7edc35-b578-44a0-a58c-e6388bc83337'): on 2011-01-21, customer 225 bought 22 of product 258 ("Flip Flops" by Havaianas, in c360.product) for $439.78. Pour its triples next to Carolyn's:
Figure 2.2: Triples from two databases, poured together. Both sides use the same name for customer 225, so the order connects to Carolyn with no join logic at all. This is federation in miniature; Chapter 9 does it for real.
Nobody wrote a JOIN. The order's purchasedBy arrow points at customer:225, and the Aurora triples are about a node with exactly the same name. That's the whole secret of the federated join, and why Chapter 3 spends so long on how names are built.
2.5Follow the arrow instead of writing a JOIN
"Which city does customer 225 live in?" You could write the SQL in your sleep:
SELECT a.city
FROM c360.customer c
JOIN c360.address a ON a.id = c.location
WHERE c.cid = 225;
In a graph you describe the path through the picture, with blanks where you want answers. The language is SPARQL (Chapter 4); for now just read the shape (the runnable version is in the Try it box):
SELECT ?city
WHERE {
customer:225 address ?home .
?home city ?city .
}
Term
What it is
Why it exists
Tiny example
SPARQL
The W3C query language for RDF (say "sparkle").
SQL assumes tables; triples need a language built around patterns and paths.
The query above.
Triple pattern
A triple where some positions are variables (placeholders) instead of fixed values.
You describe the shape of the facts you want; Stardog finds every triple that fits.
A placeholder name, starting with ?, for a value you don't know yet. Stardog fills it in from the matching triples, and each variable becomes a column in your results.
Marks what you want back, and links lines together: the same name on two lines must be the same value (like a JOIN condition).
A shared variable does the job of the join key, and you only needed the names of two arrows.
Figure 2.3: The same question two ways. SQL matches key columns between tables; the graph query walks the arrows, and a shared variable does the job of the join key.
To be honest about what's underneath: Stardog is still joining, on ?home, much like a self-join on the imaginary three-column table. What changes is what you need to know to write the query: not table names or foreign keys, just the names of the relationships, address then city. The "how do these connect" knowledge has moved out of every query and into the data.
Two more gifts. Arrows can be followed backwards, as the Java box shows: just move the variable. And paths across sources look like paths within one: in Figure 2.2, going from an order to its customer's state is purchasedBy, address, state, even though the first arrow comes from Redshift and the rest from Aurora. The query doesn't care where triples live.
2.6Hands-on: six triples about Carolyn
Your turn. You'll put the six triples from Figure 2.1 into a scratch folder, query them three ways, and delete the folder. Three new terms first:
Term
What it is
Why it exists
Tiny example
Named graph
A set of triples with its own IRI as a label; a folder inside the database (Chapter 3).
To keep groups of triples separate: to query, secure or delete them as a unit.
<urn:book:ch02>
INSERT DATA
A SPARQL command that adds exactly the triples you list.
The simplest way to write facts by hand.
Step 1 below.
DROP GRAPH
A SPARQL command that deletes a named graph and every triple in it.
One-line cleanup, like DROP TABLE.
DROP GRAPH <urn:book:ch02>
The names are long: <urn:stardog:demos:c360:customer:225> for Carolyn, <tag:stardog:api:ecomm:firstName> for "first name". Chapter 3 explains how to build and shorten them. For today, read <…customer:225> as "customer 225" and <…ecomm:firstName> as "first name".
Check yourself
A SQL row has six non-null columns, including the primary key. About how many triples does it become?
About five, one per non-key column. The primary key becomes part of the subject's name (customer:225) rather than a triple. NULL columns produce nothing.
Is "Milwaukee" a thing or a literal in our model? Why?
A literal: a plain text value in quotes, with no arrows leaving it. To state facts about the city itself, you'd make it a thing with its own IRI.
Why is the address a thing and not a literal?
It has facts of its own (city, state, zip), and only things can be subjects. It also started as a foreign-key target, which is the usual sign.
You run the same INSERT DATA twice. How many triples are there?
The same as after the first run. A graph is a set; restating a triple changes nothing.
In the "which city" query, what plays the role of the JOIN condition?
The shared variable ?home: it's the object of the first pattern and the subject of the second, so both must match the same node. You express the join by naming relationships, not matching key columns.
Why do the Aurora and Redshift triples in Figure 2.2 connect without any join logic?
Both use exactly the same name for customer 225, and triples naming the same thing are about the same node. Building those names consistently from cid is the IRI trick of Chapters 3 and 9.
Chapter recap
RDF stores every fact as a triple: subjectpredicateobject. A set of triples is a graph.
A SQL row becomes triples: key → subject name, column → one triple, foreign key → arrow to another thing, NULL → nothing.
Objects are either things (named by IRIs, can have their own arrows) or literals (plain values, dead ends).
Triples from different sources combine by simple union and connect wherever they use the same name.
A SPARQL query is a pattern of triples with variables; a shared variable does the job of a join key, in either direction.
You wrote, queried and deleted six triples in <urn:book:ch02>: your first A-Box, with no T-Box yet.