Nodes, relationships, properties — first-class relationships versus RDF reification and junction tables
A property graph models data as nodes plus directed, typed relationships that are first-class records carrying their own properties, which is why 'Alice KNOWS Bob since 2020' is one edge here but a junction-table-plus-attributes mess in relational and a multi-triple reification in RDF.
Scene 1's pointer walk beat the join, but we cheated — we never said how a relationship gets to be a thing you can follow. Here's the model that makes it real: relationships are first-class records with their own type and properties, not rows in a join table. Now the obvious question — what does 'first-class' buy you at the byte level that a join row doesn't?
Scene 02
Nodes, relationships, properties
- Watch
- Try it
- Predict
- Capture
One fact: 'Alice KNOWS Bob since 2020.' Watch it drawn three ways, left to right. RELATIONAL needs a junction table for the link AND a separate attributes table just to hold the date. RDF has to reify — the edge becomes its own blank node with extra triples. The PROPERTY GRAPH stores it as a single typed edge, Alice—[:KNOWS]→Bob, with {since:2020} living right on the edge. Count the moving parts: the property graph has the fewest. That right pane is the model the rest of the course is built on.
Highlighted lines are the ones running in the diagram right now.
def record_fact(g, a, rel_type, b, props):# nodes are first-class; so is the relationshipg.merge_node(a)g.merge_node(b)# the edge IS a record — properties live ON itg.add_edge(a, b, type=rel_type, props=props)# e.g. add_edge('Alice','Bob','KNOWS',{since:2020})
def record_fact(db, a, rel, b, attrs):db.upsert('USER', a); db.upsert('USER', b)# the link: just the two endpointsdb.insert(rel + '(a,b)', (a.id, b.id))# attributes have nowhere to go but a side tabledb.insert(rel + '_ATTRS', (a.id, b.id, attrs))
def record_fact(store, a, rel, b, attrs):bnode = store.fresh_blank_node()store.add(a, rel, bnode) # alice :knows _:k1store.add(bnode, ':object', b) # _:k1 :object bobfor k, v in attrs.items():store.add(bnode, k, v) # _:k1 :since 2020
Where this sits in Build a graph database (Neo4j / Dgraph-style)
Scene 02 of 16, in the Why graphs act — Friends-of-friends melts a join; edges go first-class.. The property-graph model is four things — nodes, typed relationships, labels, and properties on both — and putting key/values directly ON the edge is the move that relational join-tables and RDF triples can't make cleanly.
Up next. We've made the edge a first-class object on paper. But 'first-class' has to mean something physical: when I'm standing on Alice and I want Bob, how does the engine GET to the edge without looking it up in an index? The answer is the single most important idea in the whole course — and it's why a hop costs the same whether the graph has ten nodes or ten billion.
All 16 scenes in Build a graph database (Neo4j / Dgraph-style) · Every curriculum