Why graphs: property graphs, RDF and Neo4j

After this lesson you will explain why following a relationship in a graph database is a step while a join is a search, use the slides' graph vocabulary, write a Neo4j model in the notation the exam expects, and tell a property graph from an RDF graph of the same facts.

A social network of a million people, each with about fifty friends. Question: who can be reached from one person through at most four friendships? In a test reported in the book Graph Databases by Robinson, Webber and Eifrem (background, not on the slides), MySQL answered in 1,543 seconds — almost 26 minutes — and Neo4j in 1.4 seconds. At five friendships MySQL never finished; Neo4j took 2.1.

Same data, same question. The slide that opens part 2 of the deck gives the verdict without numbers: "Relational Databases (incredibly!) are not good in managing relationships!" (p. 251). By the end of the lesson you will say where a factor of a thousand comes from — and for which kind of question it vanishes.

A graph, in the slides' words

Three kinds of question are graph questions. Neighbourhoods: who are A's friends, and how many? Paths: can a message travel from A to O, and by which shortest route? Patterns: where does a shape occur — two bank accounts sharing one phone number, a sailor who reserved a red boat? Part 1 of the deck (pp. 218–249) supplies the vocabulary; what the text below does not explain, a table lists in the slides' wording.

Informally a graph is "a set of nodes joined by a set of lines or arrows" (p. 224). Formally (p. 225) it is an ordered triple \(G = (V, E, f)\): \(V\) is the set of vertices (nodes), \(E\) the set of edges, and \(f\) a function that "maps each element of E to an unordered pair of vertices in V" — the two endpoints of the edge. Lines are undirected edges. Arrows give a directed graph, in which "An edge is an ordered pair of nodes" (p. 234). Numbers on the edges — distances, costs — make a weighted graph, with a weight function \(w: E \to \R\) (p. 233).

1 2 3 4 5 6 cycle

Walking: the path [1, 2, 4, 5] and the cycle 4 → 5 → 4 (p. 229)

1 2 4 5 in in + out out out

Counting: node 2 has in-degree 2 and out-degree 3 (p. 241)

Fig. 1. The deck's two small digraphs. Left, walking: a path follows the arrows; this one is simple because no vertex repeats, and 5 is reachable from 1. Right, counting: the self-loop on 2 enters and leaves, so it counts once in the in-degree and once in the out-degree; the degree of 2 is 5.

Walk on the left graph of Fig. 1. A path is "a sequence of vertices such that there is an edge from each vertex to its successor", and it is simple "if each vertex is distinct" (p. 229). The slide's example is [1, 2, 4, 5]: 5 is reachable from 1. A path that comes back to its start is a cycle (p. 230): the two arrows between 4 and 5 form one, and so does 1 → 2 → 4 → 1. On the right graph, the degree of a node is the "Number of edges incident on a node" (p. 240); with arrows it splits into in-degree and out-degree, and "Degree = indegree + outdegree" (p. 241): "The in degree of 2 is 2 and the out degree of 2 is 3."

Two definitions are easy to blur. A graph is connected if "any two nodes are connected by a path"; a directed graph is strongly connected if "there is a directed path from any node to any other node" (p. 231) — a path, not a direct edge. And a graph is sparse if \(|E| \approx |V|\), dense if \(|E| \approx |V|^2\) (p. 232). A social network is sparse: fifty friends each, among a million people.

"Strongly connected means every node is directly connected to every other one." So say the course notes, but that is a complete graph. Strongly connected asks only for a directed path: the cycle 1 → 2 → 4 → 1 of Fig. 1 makes its three nodes strongly connected with three arrows, where a complete digraph needs six.

The deck's history starts with "Leonhard Euler's paper on "Seven Bridges of Königsberg", published in 1736" (p. 221), drawn as a town map turned into four points and seven lines. Euler's argument, which the slide does not spell out: a walk uses the bridges of every land mass it passes in pairs, in and out, so only its two ends may touch an odd number of bridges. Königsberg's counts were 5, 3, 3, 3 — no walk crosses every bridge once, and no route had to be tried.

One family deserves a closer look, because its slide has a slip. A complete graph \(K_n\) is one in which "Every pair of vertices are adjacent" (p. 236).

The rest of the vocabulary of part 1, as the slides print it — a closed question can quote any line.
TermAs the slide puts itPage
Famous problems"The traveling salesman problem"; Francis Guthrie's "four color problem" of 1852, "SOLVED ONLY 120 YEARS LATER!" (in 1976 — background); other examples: wiring, shortest routes, matching, scheduling, visibility222–223
Vertex, edgevertex: "Basic Element", "Drawn as a node or a dot"; edge: "A set of two elements", drawn as a line between its "end vertices, or endpoints"226
Example"V:={1,2,3,4,5,6}", "E:={{1,2},{1,5},{2,3},{2,5},{3,4},{4,5},{4,6}}" — six vertices, seven edges227
Simple graph"graphs without multiple edges or self-loops"228
Cyclic, acyclic"A graph is called cyclic if it contains a cycle; otherwise it is called acyclic"230
BipartiteV "can be partitioned into 2 sets V1 and V2" so that every edge has one end in each235
Planar"Can be drawn on a plane such that no two edges intersect"; "K4 is the largest complete graph that is planar" — although the slide draws K4 with crossing diagonals: one crossing-free drawing is enough (put a corner inside the triangle of the other three)237
Tree"Connected Acyclic Graph"; "Two nodes have exactly one path between them"238
Hypergraph"edges can connect any number of vertices": a pair (X, E), E "a set of subsets of X, called hyperedges"239
Subgraph, supergraph"Vertex and edge sets are subsets of those of G"; a supergraph "contains G as a subgraph"242
Spanning subgraph"Subgraph H has the same vertex set as G", "Possibly not all the edges"243
Graph ADT"a graph is an abstract data type (ADT)": nodes and edges that "establish relationships (connections) between the nodes"244
Algorithmsshortest path (single source, all pairs), network flow, matching, topological ordering, strongly connected components; biconnected components and articulation points, bridges, colouring, Euler and Hamiltonian tours, clique, isomorphism, edge and vertex covers, visibility248–249

Last, the slides store the graph as a data structure, four ways (pp. 245–247): an incidence matrix (edges × vertices), an adjacency matrix (\(|V| \times |V|\), \(a_{ij} = 1\) "if \((i, j) \in E\) and 0 otherwise"), an edge list ("pairs (ordered if directed) of vertices", "Optionally weight and other data") and an adjacency list, every node with the list of its neighbours.

the graph adjacency matrix adjacency list 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 01001 10111 01010 01101 11010 1 → 2, 5 2 → 1, 3, 4, 5 3 → 2, 4 4 → 2, 3, 5 5 → 1, 2, 4 the list skips the zeros
Fig. 2. One graph, three ways (p. 247). To list the neighbours of node 2, the matrix makes you read its whole row — five cells here, a million for a million people; the adjacency list hands you exactly the four neighbours.

Hold on to that difference. An edge list is a relational table Edge(from, to): finding a node's neighbours means searching it. An adjacency list on disk, every node carrying its own connections, is what the next slides call a graph database.

Following a relationship: a search or a step

After the provocation of the hook (p. 251), part 2 defines its subject (p. 252): a graph database is a "Database that uses graph structures with nodes, edges and properties to store data"; it "Provides index-free adjacency"; its "Edges hold most of the important information".

Index-free adjacency is the technical core. The slide glosses it as "Every node is a pointer to its adjacent element"; read it as: every node stores direct references to its own relationships, and through them to its neighbours. Moving to a neighbour follows a reference — no search, so the cost does not depend on how many other nodes the database holds. In a relational database the same move is a join: the row's key is looked up in an index over the whole table, a search whose cost grows with the table (about \(\log_2\) of its rows), repeated for every row at every hop.

The slide on indexes (p. 272) draws the contrast with two captions. Relational: "Use index scans to look up rows in tables and join them with rows from other tables". Graph: "Use indexes to find the starting points for a query". The widget turns them into numbers.

Friends of friends: a join plan against a traversal

Fifteen people, A to O, sorted by how many friendships separate them from A; the table Friendship(person, friend) stores each of the 17 friendships twice, once from each side. Choose how far from A to look. The join plan is a chain of self-joins: every row it has produced is extended by a search in the index of the whole table, and a ×n badge counts the rows that end at each person. The traversal searches the index once, to find A, and then expands each person once, following the relationships stored with them. The numbers are computed from this model (a search compares about \(\log_2\) of the number of rows); they are not measurements and not on the slides.

  1. Set the depth to 1 hop. Before reading the bars: which plan does less work?
    What you should see

    Neither. Both search the index once to find A and then read A's 3 friendships: 9 against 9 with the circle alone, 29 against 29 with a million people. One join costs what one traversal step costs. The gap opens with the second hop — and the measured times of the hook start close too: at two friendships, 0.016 s for MySQL against 0.010 s for Neo4j.

  2. With only this circle in the table, move the depth from 2 to 4. Predict how the join plan's rows grow, and how the traversal's relationships grow.
    What you should see

    The rows go 13 → 41 → 124: friends share friends, so the chain of joins reaches the same people again and again — at depth 4 it reaches A herself 21 times and B 18 times. The traversal follows 13 → 25 → 32 relationships, and it can never exceed 34, the two ends of the circle's 17 friendships. The work goes from ×1.9 to ×9.9.

  3. Keep the depth at 3 and switch the table to a million people. Which readouts change, and which do not?
    What you should see

    The people reached (12), the rows of the join (41) and the relationships the traversal follows (25) stay as they were: A's circle did not change. Only the price of one index search grows, from 6 to 26 comparisons, and the join pays it 14 times while the traversal pays it once: 405 against 51, ×7.9. That is index-free adjacency: a hop costs the same however big the rest of the database is.

The model isolates the two effects behind the hook's gap: the join plan carries every repeat of its rows and pays a whole-table search for each; the traversal pays only for the neighbourhood it explores. The measured times have the same shape: close at two hops, far apart from three on.

Friends of friends on 1,000,000 people with about 50 friends each, seconds per query — measured, background, not on the slides (Robinson, Webber and Eifrem, Graph Databases, ch. 2, from Vukotic and Watt, Neo4j in Action).
Depth (friendships)MySQLNeo4j
20.0160.010
330.2670.168
41,543.5051.359
5did not finish2.132

The slide's list of advantages (p. 253) follows from the same mechanism. "When there are relationships that you want to analyze, Graph databases become a very nice fit because of the data structure"; they "are very fast for associative data sets — Like social networks"; and they "Map more directly to object oriented applications — Object classification and Parent->Child relationships": objects that reference each other already form a graph, with nothing to flatten into junction tables.

Index-free adjacency: each node stores its own relationships, so a hop is a step and not a search; the index is used once, to find where to start.

"Foreign Keys? No thanks": from tables to a property graph

The deck makes the argument on one example, the Sailors database (p. 254): Sailor(sid, sname, rating, age), Boat(bid, bname, color) and Reserve(sid, bid, day), with the keys underlined — all three columns for Reserve. Reserve is a junction table, the relational way to store a many-to-many relationship: two foreign keys plus the relationship's own attribute, day.

Sailor Boat sid sname 22dustin 31lubber 58rusty bid color 101red 102green 103red Reserve sid bid day 2210110/10/96 5810311/12/96 foreign keys

Before: three tables and two foreign keys (p. 254)

:Sailor :Boat 223158 101102103 dustinlubberrusty redgreenred :RESERVES {day: 10/10/96} :RESERVES {day: 11/12/96} no Reserve table

After: the reservations are relationships with a day (pp. 256–257)

Fig. 3. The many-to-many relationship before and after. Each row of the junction table became one RESERVES relationship from a sailor to a boat; the relationship keeps its own attribute, day, and the copied keys sid and bid are gone. Sailor 31 and boat 102 simply have no relationship.

Page 255 redraws the tables as a generic graph, every table, row, attribute and value an oval: possible, and unreadable. Page 256 gives the "Actual Graph Model" in one line, (:Sailor) –[:reserves]-> (:Boat), and page 257 the reason, "Foreign Keys? No thanks": the reservation links directly to its sailor and its boat, so its copies of sid = 22 and bid = 101 are crossed out and only day stays. In the property graph the reservation is a relationship, and the day its property.

That is the property graph, Neo4j's model. The slide gives it in three bullets — "Nodes – with labels (type) and attributes", "Edges", "Indexes" (p. 266) — and Neo4j's documentation (background) completes it:

At the exam the model is written in the notation of p. 256. The paper of 30 Jan 2026 asks for "a written textual representation (no graphical sketches) of the graph model, using the typical Neo4J notation for nodes ( ), edges -[ ]->, and attributes { }. Include data types for attributes." For the Sailors:

// the Sailors of p. 254 as a graph model
(:Sailor {sid: Integer, sname: String,
          rating: Integer, age: Float})
(:Boat {bid: Integer, bname: String, color: String})
(:Sailor)-[:RESERVES {day: Date}]->(:Boat)

Labels in CamelCase, types in capitals, no keys copied onto the edge: "N:N relationship do not need any special treatment" (p. 1385). The full recipe — ISA hierarchies, the past papers — is lesson 12.

"In CREATE (Neo:Crew { name:'Neo' }) (p. 268), Neo is the label." The course notes say so, but Neo is a variable — the name the statement uses for the node while it runs. Crew is the label, name a property. Nothing called Neo is stored except the value of that property.

A query on such a graph is a shape. "Query: Graph matching approach" (p. 258) draws a small shape found three times inside a large blob of "Facts": you describe a pattern — a sailor, a RESERVES arrow, a red boat — and the database returns every place where it occurs; in Fig. 3, twice. And the graph needs no schema migration: a book that gains a second author gets one more edge ("Easy to Extend", p. 259), and a stored value can be replaced by a node computed from other measurements ("Easy to Change", p. 260).

The same facts as RDF triples

The exam guideline lists, under "Features of Graph DB", two models: "Property graphs (neo4j)" and "Direct labelled graphs (RDF)" — "direct" meaning directed. The slides never name RDF, but they draw it: pages 255 and 257, with every attribute an oval at the end of an arrow, are in the RDF style. What follows on RDF itself is background from the W3C's RDF 1.1 Primer.

In RDF (Resource Description Framework) everything is a triple: "A statement always has the following structure: <subject> <predicate> <object>". Subjects and predicates are IRIs — web identifiers such as http://example.org/sailing#s22, abbreviated :s22 — and an object is an IRI or a literal value. An RDF graph is a set of triples; drawn as arrows from subject to object, labelled with their predicates, they form a directed labelled graph. An attribute is just an arrow that ends in a literal, :s22 :sname "dustin", and a label becomes the triple :s22 rdf:type :Sailor.

:Sailor :Reservation :Boat :s22 :res1 :b101 "dustin" "1996-10-10" "red" rdf:type rdf:type rdf:type :sailor :boat :sname :day :color ID4 on p. 257
Fig. 4. The reservation of Fig. 3 as RDF triples, one arrow per triple (classes on top, resources in the middle, literals at the bottom). The day cannot sit on an arrow, so the reservation became a resource of its own, :res1 — the node ID4 that the slide draws on p. 257 with its sailor, its boat and its day.

The difference shows on the relationship. In the property graph the reservation is one relationship with a property. A triple has three places and no fourth, so to record the day RDF turns the reservation into a resource — reification — with a triple to the sailor, one to the boat and one for the day.

One fact, two models: property graph and triples

Above, a tiny property graph: sailor 22 and boat 101 of p. 254 and their reservation. Below, the same facts as RDF triples in the Turtle notation, where : abbreviates the IRI of this dataset and rdf:, xsd: the W3C vocabularies; lines starting with # are comments. Switch facts on and off and count what each model needs.

  1. Start from the drawing as it loads. Predict how many triples the 7 properties and 2 labels need, and how many the reservation needs. Then switch the node properties off and on.
    What you should see

    Ten triples: one rdf:type per label, one per property (their objects are literals), and a single triple for the reservation. Without the properties only 3 remain. So far the two models say the same things in the same number of statements.

  2. Switch on "a property on the relationship: day". How many triples does the reservation cost now, and where did :s22 :reserves :b101 go?
    What you should see

    Four instead of one: :res1 gets a type, an arrow to the sailor, an arrow to the boat and the day. The direct triple is gone, because the reservation is now a resource of its own. In the property graph the same change was one key-value pair on the existing relationship. This is the one thing the two models cannot say equally briefly.

  3. Switch the day off again and let dustin book Interlake a second time. Predict the number of RDF triples, then check. Now switch the day on.
    What you should see

    Still 10: the second booking is the same triple :s22 :reserves :b101, and an RDF graph is a set, so it holds that triple once — the second booking has vanished, while the property graph shows two relationships. With the day on, each booking is its own resource, :res1 and :res2: 8 triples for the two reservations, 17 in all. A relationship in RDF has no identity unless you make it a node.

RDF is queried with SPARQL, the W3C's language, whose patterns are triples with variables. One question in three models — the names of the sailors who reserved a red boat. SQL needs two joins:

-- SQL over the tables of p. 254: two joins
SELECT DISTINCT S.sname
FROM Sailor S
JOIN Reserve R ON R.sid = S.sid
JOIN Boat B ON B.bid = R.bid
WHERE B.color = 'red';

In Cypher, Neo4j's query language, the two joins become one arrow — the lesson's only query, a taste of lesson 11:

// returns dustin and rusty (Fig. 3)
MATCH (s:Sailor)-[:RESERVES]->(b:Boat {color: 'red'})
RETURN DISTINCT s.sname

In SPARQL the same pattern is written as triples, one per arrow (here over the triples without the day):

# one triple pattern per arrow
PREFIX : <http://example.org/sailing#>
SELECT DISTINCT ?name
WHERE {
  ?s a :Sailor ;
     :sname ?name ;
     :reserves ?b .
  ?b :color "red" .
}

Nodes with labels and properties; relationships with one type, a direction and properties. Two relationships between the same nodes are two things. Queried with Cypher; built as an application database for fast traversals.

A set of subject–predicate–object triples on IRIs. Attributes are arrows to literals; a relationship has no properties and no identity unless reified. Queried with SPARQL; built to publish and link data through shared identifiers.

Neo4j, as the slides present it

Part 3 introduces the product (p. 262): "Developed by Neo Technologies • Most Popular Graph Database • Implemented in Java • Open Source". Its "Salient features" (p. 263): "Neo4j is schema free – Data does not have to adhere to any convention"; "ACID – atomic, consistent, isolated and durable for logical units of work"; easy to use, well documented, and "Support for wide variety of languages — Java, Python, Perl, Scala, Cypher, etc" (Cypher is the query language, the others client languages).

JVM Neo4j Other APIs Core API Object Cache Transaction Management Operating System File System Cache HDD Record Files Nodes Relationships Relationship Types Properties Transaction Log
Fig. 5. "Neo4j Software Architecture" (p. 264), redrawn: APIs and caches inside the JVM, the operating system's file cache, and on disk separate record files for nodes, relationships, relationship types and properties, next to a transaction log. The slide has no text besides its title. The picture shows an early Neo4j: later versions replaced the object cache with a page cache over the record files (background).

Read the bottom layer with index-free adjacency in mind. Nodes and relationships live in separate record files; in the book Graph Databases (ch. 6, background) a node record points to its first relationship, and each relationship record to the previous and next relationships of both its nodes — the references a traversal follows. The transaction log lets committed work survive a crash: the D of ACID.

"Purpose" (p. 265) is the most quotable slide of part 3: "Meant to be an operational DB, not specifically for analytics • ACID • Efficient on nodes • Not so efficient in whole-graph analysis". Neo4j serves an application with many small transactions that start at a node — who are my friends, add this reservation — cheap thanks to index-free adjacency; ranking every page of the web gains nothing from local pointers. ACID matters in a graph because creating a relationship touches two nodes: only a transaction keeps an edge from pointing at a node that is gone. The course notes add a "traditional CA architecture" (lesson 5); the slides do not.

Cypher, the slide says (p. 267), is a "Declarative language", "Easy to formulate queries based on relationships", whose features "stem from improving on pain points with SQL such as join tables": you state the pattern, Neo4j chooses the traversal. Then data comes in. CREATE makes nodes, with several labels if needed — Create (n:Actor:Director {name:'Clint Eastwood'}) (p. 269); LOAD CSV WITH HEADERS ... AS row runs the statement once per line of a file, reading columns as row.Column. Run a CREATE import twice and every node exists twice — hence MERGE, "ID-based" (p. 270):

// p. 270: one Player node per id
LOAD CSV WITH HEADERS FROM "file:///transfers.csv" AS row
MERGE (player:Player {id: row.playerUri})
ON CREATE SET player.name = row.playerName,
              player.position = row.playerPosition

MERGE is match-or-create on the whole pattern, label and properties: a :Player with that id is used if it exists, created otherwise, and ON CREATE SET fills the other properties only when it is created. A player mentioned in many rows still becomes one node. Page 271 then imports the transfers:

// p. 271: a transfer becomes a node
LOAD CSV WITH HEADERS FROM "file:///transfers.csv" AS row
MATCH (player:Player {id: row.playerUri})
MATCH (source:Club {id: row.sellerClubUri})
MATCH (destination:Club {id: row.buyerClubUri})
MERGE (t:Transfer {id: row.transferUri})
ON CREATE SET t.season = row.season, t.rank = row.transferRank,
              t.fee = row.transferFee
MERGE (t)-[:OF_PLAYER { age: row.playerAge }]->(player)
MERGE (t)-[:FROM_CLUB]->(source)
MERGE (t)-[:TO_CLUB]->(destination)

A transfer links three things — a player, a selling club, a buying club — and a relationship joins exactly two nodes, so the transfer becomes a node with three typed relationships: the hyperedge of p. 239, whose "edges can connect any number of vertices", turned into a node. The player's age at the time is a property of OF_PLAYER, the "important information" an edge holds.

"MERGE creates new nodes only if it doesn't exist another node with the same label" — so the course notes. Taken literally, the import above would create one Player in all. MERGE compares the whole pattern: one Player per id.

Two schema statements follow. An index is "For performance purpose" and "Only for access to initial node of the query" (p. 272): it finds the anchor, say customer 42; later hops follow relationships. A uniqueness constraint (p. 273) forbids two :Customer nodes with the same customerID — the graph's primary key, which keeps a MERGE by id honest. Schema free means nothing has to be declared, not that nothing can be. The slides print Neo4j 3 syntax, which Neo4j 5 rejects; today you write FOR … ON and FOR … REQUIRE, with an optional name:

CREATE INDEX ON
:Customer(customerID);
CREATE CONSTRAINT ON (c:Customer)
ASSERT c.customerID IS UNIQUE;
CREATE INDEX customer_id
FOR (c:Customer) ON (c.customerID)
CREATE CONSTRAINT customer_key
FOR (c:Customer)
REQUIRE c.customerID IS UNIQUE

"A query starts with START." The skeleton of p. 274 lists "START", "MATCH Pattern Matching", "WHERE Expressions, Predicates", "RETURN Output". START picked the starting nodes from an index in Neo4j 1.x; since 2.0 a label and a property inside MATCH do it, and current Neo4j rejects START. Begin every exam query with MATCH, as the exercise sessions do.

The next pages show the query language itself. You learn to write it in lesson 11 and lesson 12; here is what the slides print, for a closed question that quotes them.

Pages 275–281 as the slides print them, each with one line on how to read it — and, where Neo4j 5 rejects the printed form, what replaced it.
SlideWhat it prints, and how to read itPage
The Matrix queryMATCH (n:Crew)-[r:KNOWS*]-m WHERE n.name='Neo' RETURN n AS Neo,r,m, with a result of four rows.
Read: every path of one or more KNOWS relationships, in either direction, starting at Neo — one row per path, ending at Morpheus, Trinity, Cypher and Agent Smith. Today the end node needs parentheses, -(m), and the * a bound (p. 282).
275
General Query FormatMATCH (user)-[:FRIEND]-(friend) WITH user, count(friend) AS friends ORDER BY friends DESC SKIP 1 LIMIT 3 RETURN user; "WITH separates query parts explicitly, to declare the variables for the next part"; "SKIP skips results at the top and LIMIT limits the number of results".
Read: count the friends of each user (the non-aggregated user is the grouping key), sort, skip the first, keep three — the users ranked second to fourth. The template of the exam queries.
276
Patterns(n:Person:Swedish) "Node with both Person and Swedish labels"; (n)--(m) "Relationship in any direction between n and m"; (m)<-[:KNOWS]-(n) "Relationship of type KNOWS from n to m"; (n)-[:KNOWS|:LOVES]->(m) "of type KNOWS or of type LOVES"; (n)-[*1..5]->(m) "Variable length path from 1 to 5 rels."
Read: the arrowhead gives the direction, not the order of the text; $value in a property map is a parameter. Neo4j 5 writes [:KNOWS|LOVES], with one colon.
277–278
PathsshortestPath((n1:Person)-[*..6]-(n2:Person)) "Find a single shortest path"; allShortestPaths((n1:Person)-[*..6]->(n2:Person)) "Find all shortest paths"; size((n)-->()-->()) "Count the paths matching the pattern".
Read: one, or every, shortest connection of at most 6 relationships between two people. Neo4j 5 removed size() on a pattern and writes COUNT { (n)-->()-->() }.
280
Core operators and impactCypher's clauses drawn as a graph: MATCH DESCRIBES a Pattern, which is FOUND_IN the Graph and BUILDS_UP the Result; WHERE FILTERS it, Aggregation RESTRUCTURES it, SKIP/LIMIT PAGINATES it; CREATE and DELETE change the Graph.
Read: the picture also has START and RELATE, which no longer exist — MERGE took over RELATE's job.
281

Four "Hints" (p. 282) close the Cypher part, each with its reason: "Use parameters instead of literals" — with $name in place of 'dustin' the query text stays the same and Cypher re-uses its plan; "Always set an upper limit for your variable length patterns" — every hop multiplies the paths, and "It's easy to have a query touch all nodes in a graph by mistake"; "Return only the data you need"; "Use PROFILE / EXPLAIN". "Stored Procedures" (p. 279) are Java functions: "Simply move your JAR to a folder!", then call them from Cypher — which is also how libraries of whole-graph algorithms are plugged in (background).

When a graph wins — the puzzle resolved

The uses follow from "Efficient on nodes": questions that start somewhere and walk. Social networks, the slide's example (p. 253): in 2016 Facebook found its 1.59 billion users on average 3.57 intermediaries apart — four or five friendships, the depths where the joins of the hook crawled or never finished. Recommendation: people you may know are friends of friends who are not yet friends. Fraud: a ring of fake identities sharing one phone number and one address is a few hops in a graph and a chain of self-joins in SQL. Routing: "Shortest route between two cities" (p. 223) on a weighted graph — on p. 233 the direct edge from 2 to 5 costs 5, the detour through 1 costs 2 + 1 = 3. Investigation: journalists explored the Panama Papers as a Neo4j graph of about 950,000 nodes and 1.2 million relationships.

the value is in the links: many-to-many relationships, paths of unknown length, rings and shared neighbours; questions start from a node and stay local; the structure keeps changing.

records have a fixed shape, joins a small fixed depth, and questions are set-oriented — totals and reports over all rows — under the strict schema of lessons 8 and 9. Whole-graph analysis suits neither: it belongs to batch frameworks.

Now the puzzle. At four friendships the relational query is a chain of four self-joins: for every row produced so far, an index over some fifty million friendship rows is searched, and those rows include every repeat, because friends share friends. Neo4j searches an index once, to find the starting person, then follows the relationships stored with each person it reaches — work proportional to the neighbourhood, not to the database. At two friendships there is little to gain (0.016 s against 0.010 s); each further hop multiplies the relational work. The gap closes when the question stops being local: for every node at once, pointers offer no shortcut — "Not so efficient in whole-graph analysis" (p. 265). The slide's "(incredibly!)" holds for relationships followed many hops deep; joined one level at a time, they are the relational database's daily work.

Exam-style questions

Theory questions in the form of the written exam — open answers of a few sentences, and closed ones where a wrong tick may cost points — then two exercises in the form of the first part of every Neo4j exercise. The split of the 6 theory points is not published, so the theory questions carry no points.

What is index-free adjacency? Explain why it makes traversing relationships in a graph database cheaper than joins in a relational database, and say what indexes are used for in Neo4j.

Show model answer

A graph database "provides index-free adjacency": every node stores direct references to its adjacent relationships and, through them, to its neighbouring nodes. Moving from a node to its neighbours is therefore a pointer dereference, whose cost depends on that node's relationships and not on the size of the whole graph. In a relational database a relationship is a foreign key, and following it is a join: for each row an index built over the whole table is searched, at a cost that grows with the table (about log n), and this is repeated at every hop while the intermediate results grow, since friends share friends. For deep or variable-depth traversals, such as friends of friends of friends, the graph database does far less work; at one hop there is no difference. In Neo4j indexes are used only to find the starting nodes of a query; from there the traversal follows the stored relationships.

Three things earn the answer: the definition in the slide's words (index-free adjacency, p. 252), the reason (a step against a search whose cost grows with the table) and the role of indexes (starting points, p. 272). The measured times of the hook are a bonus; if you quote them, say they come from the book Graph Databases.

Compare property graphs (Neo4j) and directed labelled graphs (RDF). Where do the attributes live in each model, and how would each store the day of a reservation that links a sailor to a boat?

Show model answer

A property graph is made of nodes, relationships and properties. Nodes carry zero or more labels and key-value properties; relationships are directed, have exactly one type and can carry properties too. The reservation is one relationship with a property: (:Sailor {sid: 22})-[:RESERVES {day: date('1996-10-10')}]->(:Boat {bid: 101}). An RDF graph is a set of subject–predicate–object triples whose subjects and predicates are IRIs: every attribute is itself an edge from a resource to a literal (:s22 :sname "dustin"), and a label is an rdf:type triple. A relationship is just a triple and cannot carry an attribute, so to store the day the reservation must become a resource of its own (reification): :res1 :sailor :s22, :res1 :boat :b101, :res1 :day "1996-10-10". Property graphs are queried with Cypher and built as application databases with fast traversals; RDF is queried with SPARQL and built to publish and link data through shared identifiers.

This is the guideline's theory item word for word: "Property graphs (neo4j)", "Direct labelled graphs (RDF)". The slides draw the RDF style (pp. 255–257) without naming it, so a strong answer rests on the contrast — where attributes live, what happens to a relationship that has an attribute — rather than on RDF syntax.

According to the lecture, which of the following describe Neo4j? Select all that apply.

  • It is schema free: data does not have to adhere to any convention. "Neo4j is schema free – Data does not have to adhere to any convention" (p. 263).
  • It is especially efficient for whole-graph analysis, such as ranking every node of a large graph. The opposite: "Not so efficient in whole-graph analysis" (p. 265). Index-free adjacency helps local traversals, not computations that visit everything.
  • It provides ACID transactions. "ACID – atomic, consistent, isolated and durable for logical units of work" (p. 263), repeated on p. 265.
  • It searches an index at every hop of a traversal. Indexes find only the starting points (p. 272); the hops follow stored relationships — index-free adjacency (p. 252).
  • It is meant to be an operational database, not specifically for analytics. The first line of "Purpose" (p. 265).
  • Each node has exactly one label. A node may have several: (n:Actor:Director {name:'Clint Eastwood'}) (p. 269).
Show model answer

Schema free, ACID, and meant to be an operational database. Neo4j is "Not so efficient in whole-graph analysis", uses indexes only to find the starting nodes of a query, and allows several labels per node.

Three right options out of six. When wrong ticks may cost points, the safe ticks are the phrases printed on pp. 263 and 265; the wrong options are the three classic confusions — analytics, indexes, labels.

How many edges does the complete undirected graph \(K_7\) have?

  • 42 That is \(n(n-1)\), the formula printed on the slide: it counts ordered pairs, the arrows of a complete directed graph.
  • 49 \(7^2\) also pairs every vertex with itself; a complete graph has no self-loops.
  • 21 One edge per unordered pair of the 7 vertices: \(7 \cdot 6 / 2 = 21\).
  • 28 \(8 \cdot 7 / 2 = 28\) is \(K_8\), one vertex too many.
Show model answer
\[ |E(K_7)| = \frac{7 \cdot 6}{2} = 21 \]

Every unordered pair of the 7 vertices is one edge.

The slide prints "Has n(n-1) edges", which gives 42: the arrows of a complete directed graph. Its own drawing of \(K_4\) has 6 edges. If a closed question offers both 21 and 42, choose by the graph — 21 undirected, 42 with both directions.

True or false: the query MATCH (user)-[:FRIEND]-(friend) WITH user, count(friend) AS friends ORDER BY friends DESC SKIP 1 LIMIT 3 RETURN user returns the three users with the most friends.

  • True SKIP 1 first drops the best-connected user; LIMIT 3 then keeps the next three.
  • False "SKIP skips results at the top and LIMIT limits the number of results": the users ranked second to fourth.
Show model answer

False. WITH counts the friends of each user, ORDER BY sorts the users by that count in descending order, SKIP 1 drops the first one and LIMIT 3 keeps the next three: the users ranked second to fourth.

The query is the "General Query Format" of p. 276, word for word. A true/false item tests one clause at a time; here the trap is SKIP. Users with the same count have no defined order unless you add a second sort key.

Consider the Sailors example of the lecture (p. 254) as an ER model. The following attributes describe the entities; the primary keys are underlined. Sailor - sid (integer), sname, rating (integer), age (real). Boat - bid (integer), bname, color. The relationship Reserve links Sailor (0,N) and Boat (0,N) and has the attribute day (a date).

Suppose you want to store the respective data instances in a graph database. Provide a written textual representation of the graph model, using the typical Neo4J notation for nodes ( ), edges -[ ]->, and attributes { }. Include data types for attributes. Then write one example instance: sailor 22 (dustin, rating 7, age 45.0) reserved boat 101 (Interlake, red) on 10/10/96.

Show model answer
(:Sailor {sid: Integer, sname: String,
          rating: Integer, age: Float})
(:Boat {bid: Integer, bname: String, color: String})
(:Sailor)-[:RESERVES {day: Date}]->(:Boat)
(:Sailor {sid: 22, sname: "dustin", rating: 7, age: 45.0})
  -[:RESERVES {day: date("1996-10-10")}]->
(:Boat {bid: 101, bname: "Interlake", color: "red"})

Reserve is not a node: it is the relationship, and its attribute day is a property of the edge. There is no sid or bid on the edge either: the edge itself links the sailor and the boat, where the relational schema of the lecture needed them as foreign keys.

The wording is that of the exam of 30 Jan 2026 (2 PT there), the first part of every Neo4j exercise; in 2024–2025 it was a 1-point "sketch". No grading scheme is published, but these go against the recipe of the exercise sessions (p. 1385) or against the question: a Reserve node, sid and bid repeated on the edge, a relationship without a type or without a direction, missing data types. Past papers write dates as dd/mm/yyyy: store them as Date and say which format you assume.

Consider an ER model of football transfers, the data imported on p. 271. The following attributes describe the entities; the primary keys are underlined. Player - Player_ID, Name, Position. Club - Club_ID, Name. Transfer - Transfer_ID, Season, Rank, Fee. Relationships: Of_Player links Transfer (1,1) and Player (0,N) and has the attribute Age, the player's age at the time of the transfer; From_Club and To_Club each link Transfer (1,1) and Club (0,N): the club that sells and the club that buys.

Provide a written textual representation of the graph model, using the typical Neo4J notation for nodes ( ), edges -[ ]->, and attributes { }, with data types. Explain why Transfer is a node and not a relationship.

Show model answer
(:Player {player_id: String, name: String, position: String})
(:Club {club_id: String, name: String})
(:Transfer {transfer_id: String, season: String,
            rank: Integer, fee: Float})
(:Transfer)-[:OF_PLAYER {age: Integer}]->(:Player)
(:Transfer)-[:FROM_CLUB]->(:Club)
(:Transfer)-[:TO_CLUB]->(:Club)

A transfer links three nodes — a player, a selling club and a buying club — and has attributes of its own, while a Neo4j relationship joins exactly two nodes; so the transfer becomes a node with three typed relationships. The player's age belongs to the link between the transfer and the player and is a property of OF_PLAYER. The two relationships to Club are told apart by their types, FROM_CLUB and TO_CLUB.

This is the model behind the import of p. 271. A design such as (:Club)-[:TRANSFERS {player: …, fee: …}]->(:Club) loses the player as a node: you could no longer walk from a player to his transfers. The same move — an entity with several partners stays a node — returns in past papers, for example Ticket in the exam of 11 Feb 2025; ISA hierarchies and the rest of the recipe are lesson 12.

Check yourself

A's circle stays exactly as in the first widget, but the Friendship table around it grows from 34 rows to 50 million. At depth 3, which numbers change?

  • The number of people A reaches A reaches the same 12 people: the rest of the table is not connected to her circle.
  • The relationships the traversal follows The traversal reads only the relationships stored with the people it visits — index-free adjacency.
  • Only the comparisons inside the index: for the join plan's 14 searches and for the traversal's single search The circle is the same, so the rows, the people reached and the relationships followed do not move; each search now compares about 26 entries instead of 6, and the join plan does 14 of them.
  • The rows the join plan produces The rows are walks inside the circle; their number depends on the circle. What grows is the price of finding each of them.

Which statements about the property graph model of Neo4j are true? Select all that apply.

  • A relationship has exactly one type and a direction (:Sailor)-[:RESERVES]->(:Boat): one type, from a start node to an end node.
  • A property is a node reached through an edge That is the triple style of pp. 255–257 and of RDF; in Neo4j a property is a key-value pair on a node or a relationship.
  • A node can carry several labels Page 269: (n:Actor:Director {name:'Clint Eastwood'}).
  • A relationship can join three nodes, like a relationship of three entities in an ER diagram A relationship joins exactly two nodes; a link among three things becomes a node, as Transfer on p. 271.
  • A relationship can carry properties RESERVES keeps day; on p. 271 OF_PLAYER keeps the player's age.

Labels on nodes, one type on each relationship, properties on both.

In the RDF view of the second widget, dustin books Interlake a second time and the reservation has no day. How many triples does the second booking add?

  • One It would be the same triple again; a set does not hold duplicates.
  • None The triple :s22 :reserves :b101 is already in the graph, and an RDF graph is a set of triples.
  • Four Four is the price of a reified reservation — and only once the reservation becomes a resource, for example to carry its day.

In a property graph two relationships between the same nodes are two things; in RDF a statement has no identity unless you make it a node.

The slide says \(K_n\) "Has n(n-1) edges". For \(n = 4\) that is 12. Which of these quantities are 12? Select all that apply.

  • The edges of the \(K_4\) drawn on the slide Count them: four sides and two diagonals, 6.
  • The ordered pairs (u, v) of distinct vertices: the arrows of a complete directed graph on 4 nodes 4 · 3 = 12, with u → v and v → u counted separately.
  • The edges of \(K_5\) 5 · 4 / 2 = 10.
  • The sum of the degrees of the four vertices of \(K_4\) Each vertex has degree 3, and 4 · 3 = 12: every edge counted once from each end.

\(n(n-1)\) counts each edge twice; halve it for the undirected \(K_n\).

Which of these directed graphs on the nodes 1, 2, 3 is strongly connected?

  • 1 → 2, 2 → 3, 1 → 3 Nothing leaves 3, so 3 reaches no one.
  • 1 → 2, 2 → 1, 2 → 3 3 has no outgoing arrow, so it cannot reach 1 or 2.
  • 1 → 2, 2 → 3, 3 → 1 Every node reaches every other one along the cycle: three arrows are enough.

Strongly connected needs a directed path between every pair, not a direct edge — a direct edge everywhere is a complete graph, the course notes' slip.

A file of transfers mentions player p7 in three rows. The import of p. 270 runs MERGE (player:Player {id: row.playerUri}) once per row. How many Player nodes with id p7 exist afterwards?

  • Three That is what CREATE would do: the import of p. 269 makes one node per row.
  • One MERGE matches the whole pattern, label and id: the first row creates the node and ON CREATE SET fills its name and position; the next two rows find it.
  • One Player node in total, whatever the ids That is the course notes' reading ("only if it doesn't exist another node with the same label"); MERGE compares the properties of the pattern too, so there is one node per id.

A query starts from customer 42 and follows two relationships from there. There is an index on :Customer(customerID). Where does Neo4j use it?

  • At every hop, to find the next nodes That is the relational picture of the same slide: an index search for every join.
  • Nowhere: Neo4j is schema free, so it has no indexes Schema free means nothing has to be declared; indexes and uniqueness constraints exist and are declared per label and property (pp. 272–273).
  • Once, to find customer 42; the two hops follow the relationships stored with the nodes "Use indexes to find the starting points for a query" (p. 272).

Key take-aways

What to watch and read