Hello Graph: Friend-of-Friend
Objective
Build a small social network and answer "who are my friends-of-friends?" — the question that kicked off graph-database adoption at LinkedIn and Facebook. In SQL this requires a self-join on the friendship table for every extra hop. In Cypher it is a single pattern of length two.
Step 1: Create the graph
// People
MERGE (sarah:Person {name: "Sarah Chen"})
MERGE (raj:Person {name: "Raj Patel"})
MERGE (mia:Person {name: "Mia Rossi"})
MERGE (leo:Person {name: "Leo Park"})
MERGE (eli:Person {name: "Eli Tanaka"})
MERGE (zoe:Person {name: "Zoe Williams"})
// Direct friendships (undirected, modeled as two-way)
MERGE (sarah)-[:FRIENDS_WITH]->(raj)
MERGE (sarah)-[:FRIENDS_WITH]->(mia)
MERGE (raj)-[:FRIENDS_WITH]->(leo)
MERGE (raj)-[:FRIENDS_WITH]->(eli)
MERGE (mia)-[:FRIENDS_WITH]->(zoe)
MERGE (leo)-[:FRIENDS_WITH]->(zoe)
MERGE (eli)-[:FRIENDS_WITH]->(zoe);
Step 2: Find Sarah's friends-of-friends
// Pattern: Sarah --FRIENDS_WITH--> friend --FRIENDS_WITH--> fof
// Exclude Sarah herself and anyone she's already friends with.
MATCH (sarah:Person {name: "Sarah Chen"})-[:FRIENDS_WITH]->(friend)-[:FRIENDS_WITH]->(fof)
WHERE fof <> sarah
AND NOT (sarah)-[:FRIENDS_WITH]->(fof)
RETURN fof.name AS suggestion, count(DISTINCT friend) AS mutual_friends
ORDER BY mutual_friends DESC, suggestion;
What's happening
- The two-hop pattern
(sarah)-->(friend)-->(fof)traverses the graph natively — no joins. count(DISTINCT friend)ranks suggestions by how many mutual friends connect them.- The
NOT (sarah)-->(fof)predicate excludes existing direct friendships in one anti-pattern check. - In SQL, this would require two self-joins on a
friendshipstable plus aNOT EXISTSsubquery. - Cypher's pattern syntax reads like the question itself — a key reason graphs feel intuitive.
Try this next
MATCH (p:Person {name: "Raj Patel"})-[:FRIENDS_WITH*1..2]-(other)
WHERE p <> other
RETURN DISTINCT other.name;
MATCH (a:Person)-[:FRIENDS_WITH]-(b:Person)
RETURN a.name AS person, count(b) AS degree
ORDER BY degree DESC;
MATCH path = shortestPath((a:Person {name: "Sarah Chen"})-[:FRIENDS_WITH*]-(b:Person {name: "Zoe Williams"}))
RETURN [n IN nodes(path) | n.name] AS hops, length(path) AS distance;