Graph Databases - Relationship-Focused Data
Learning Objectives: By the end of this lesson, you will be able to:
- Understand graph theory concepts and their application to data modeling
- Design graph data models for relationship-heavy domains
- Write queries using Cypher (Neo4j) and understand graph traversal patterns
- Identify use cases where graph databases outperform relational databases
- Implement common graph algorithms for recommendations and fraud detection
1. Welcome and Introduction (5 minutes)
Icebreaker Activity
Open LinkedIn and look at your connections. You’re connected to 500 people, each of whom is connected to 500 others. Want to find “friends of friends who work at Google”? In a relational database, this requires multiple self-joins on potentially millions of rows—a query that could take minutes. In a graph database, this is a simple two-hop traversal that returns results in milliseconds.
Relationships aren’t just foreign keys—they’re first-class citizens. Welcome to graph databases.
Lesson Overview
- Duration: 90 minutes
- Prerequisites: Understanding of data modeling, basic programming concepts
- Materials Needed: Neo4j Desktop or Aura free tier, command line access
2. Learning Objectives and Session Outline (5 minutes)
What We’ll Cover Today:
- Graph Theory Fundamentals - Nodes, relationships, and properties
- Graph Data Modeling - Designing schemas for graph databases
- Cypher Query Language - Writing queries for Neo4j
- Graph Algorithms - Pathfinding, centrality, and community detection
- Use Cases - When graphs outperform other databases
- Popular Systems - Neo4j, Amazon Neptune, JanusGraph comparison
Success Criteria:
By the end of this session, you should be able to model relationship-heavy domains as graphs and write efficient graph traversal queries.
3. Foundation Knowledge (15-20 minutes)
Core Concepts
What is a Graph?
A graph is a data structure consisting of:
- Nodes (Vertices): Entities or objects
- Relationships (Edges): Connections between nodes
- Properties: Key-value pairs attached to nodes and relationships
Real-World Analogy: Think of a social network. People are nodes, friendships are relationships, and information like name, age, and location are properties. The power of graphs is answering questions about connections: “Who knows someone at Company X?” or “What’s the shortest path between two people?”
Why This Matters: Many real-world problems are inherently about relationships: social networks, recommendation engines, fraud detection, knowledge graphs, supply chains, and more. Graph databases model these naturally and query them efficiently.
Graph vs. Relational Model
Relational Approach to Friendships:
-- Users table
| id | name | city |
|----|-------|----------|
| 1 | Alice | New York |
| 2 | Bob | Boston |
| 3 | Carol | Chicago |
-- Friendships table
| user_id | friend_id |
|---------|-----------|
| 1 | 2 |
| 2 | 3 |
-- Find friends-of-friends
SELECT DISTINCT f2.friend_id
FROM friendships f1
JOIN friendships f2 ON f1.friend_id = f2.user_id
WHERE f1.user_id = 1 AND f2.friend_id != 1;
-- Gets more complex with each additional hop!
Graph Approach:
(Alice)-[:FRIENDS_WITH]->(Bob)-[:FRIENDS_WITH]->(Carol)
// Find friends-of-friends
MATCH (alice:Person {name: 'Alice'})-[:FRIENDS_WITH*2]->(fof:Person)
RETURN DISTINCT fof
Key Terminology
- Node: An entity in the graph (Person, Product, Company)
- Relationship: A connection between two nodes with a type and direction
- Label: Category/type for nodes (like a table name)
- Property: Key-value attribute on nodes or relationships
- Traversal: Following relationships to explore the graph
- Path: A sequence of nodes connected by relationships
- Degree: Number of relationships a node has
- Index-Free Adjacency: Each node stores direct pointers to neighbors
- Cypher: Neo4j’s declarative query language
- Pattern Matching: Finding structures that match a specified pattern
4. Detailed Explanation and Examples (25-30 minutes)
Graph Data Modeling
From Domain to Graph
Step 1: Identify Entities (Nodes)
- Person, Product, Order, Company, Location
Step 2: Identify Relationships
- Person WORKS_AT Company
- Person PURCHASED Product
- Product BELONGS_TO Category
Step 3: Add Properties
- Node properties: name, email, price
- Relationship properties: since, amount, rating
Step 4: Consider Query Patterns
- What questions will you ask?
- Design relationships to support efficient traversal
Graph Model Example: E-commerce Recommendations
(:Customer {id, name, email})
-[:PURCHASED {date, quantity}]->(:Product {id, name, price})
-[:BELONGS_TO]->(:Category {name})
(:Product)-[:SIMILAR_TO {score}]->(:Product)
(:Customer)-[:VIEWED {timestamp}]->(:Product)
(:Customer)-[:REVIEWED {rating, text, date}]->(:Product)
When to Use Graph Databases
Excellent For:
- Social networks and recommendations
- Fraud detection and anti-money laundering
- Knowledge graphs and semantic search
- Network and IT infrastructure mapping
- Supply chain and logistics
- Real-time recommendations
Not Ideal For:
- Simple CRUD applications
- Aggregation-heavy analytics
- High-volume transactional systems
- Data without significant relationships
Popular Graph Database Systems
Neo4j
- Type: Native graph database
- Query Language: Cypher
- Best For: Most graph use cases, excellent tooling
- Features: ACID transactions, built-in algorithms, visualization
Amazon Neptune
- Type: Managed graph database
- Query Languages: Gremlin, SPARQL
- Best For: AWS users, RDF knowledge graphs
- Features: Fully managed, multi-model (property graph + RDF)
JanusGraph
- Type: Distributed graph database
- Query Language: Gremlin
- Best For: Very large graphs, custom backends
- Features: Pluggable storage (Cassandra, HBase, etc.)
ArangoDB (Multi-model)
- Type: Multi-model (document + graph)
- Query Language: AQL
- Best For: Mixed workloads
- Features: Combines document and graph capabilities
Common Misconceptions
-
Misconception: “Graph databases are just for social networks” Reality: Fraud detection, recommendations, knowledge management, and many other domains benefit from graphs.
-
Misconception: “I can model relationships in relational databases just as well” Reality: While possible, multi-hop queries become exponentially slower as relationships deepen.
-
Misconception: “Graph databases can’t scale” Reality: Modern graph databases like Neo4j support clustering and can handle billions of nodes.
5. Live Coding Demonstration (20-25 minutes)
Example 1: Creating a Social Network Graph
// Create nodes
CREATE (alice:Person {name: 'Alice', age: 30, city: 'New York'})
CREATE (bob:Person {name: 'Bob', age: 28, city: 'Boston'})
CREATE (carol:Person {name: 'Carol', age: 32, city: 'New York'})
CREATE (david:Person {name: 'David', age: 35, city: 'Chicago'})
CREATE (eve:Person {name: 'Eve', age: 27, city: 'Boston'})
// Create relationships
CREATE (alice)-[:FRIENDS_WITH {since: 2015}]->(bob)
CREATE (alice)-[:FRIENDS_WITH {since: 2018}]->(carol)
CREATE (bob)-[:FRIENDS_WITH {since: 2016}]->(carol)
CREATE (bob)-[:FRIENDS_WITH {since: 2019}]->(david)
CREATE (carol)-[:FRIENDS_WITH {since: 2017}]->(david)
CREATE (david)-[:FRIENDS_WITH {since: 2020}]->(eve)
// Create companies
CREATE (techcorp:Company {name: 'TechCorp', industry: 'Technology'})
CREATE (finco:Company {name: 'FinCo', industry: 'Finance'})
CREATE (startup:Company {name: 'StartupXYZ', industry: 'Technology'})
// Create employment relationships
CREATE (alice)-[:WORKS_AT {role: 'Engineer', since: 2019}]->(techcorp)
CREATE (bob)-[:WORKS_AT {role: 'Manager', since: 2017}]->(finco)
CREATE (carol)-[:WORKS_AT {role: 'Director', since: 2015}]->(techcorp)
CREATE (david)-[:WORKS_AT {role: 'CTO', since: 2018}]->(startup)
CREATE (eve)-[:WORKS_AT {role: 'Designer', since: 2021}]->(finco)
// Basic queries
// Find all of Alice's friends
MATCH (alice:Person {name: 'Alice'})-[:FRIENDS_WITH]-(friend:Person)
RETURN friend.name, friend.city
// Find friends-of-friends
MATCH (alice:Person {name: 'Alice'})-[:FRIENDS_WITH]-()-[:FRIENDS_WITH]-(fof:Person)
WHERE NOT (alice)-[:FRIENDS_WITH]-(fof) AND alice <> fof
RETURN DISTINCT fof.name AS friend_of_friend
// Find people Alice knows who work in Technology
MATCH (alice:Person {name: 'Alice'})-[:FRIENDS_WITH*1..2]-(person:Person)-[:WORKS_AT]->(company:Company)
WHERE company.industry = 'Technology' AND person <> alice
RETURN person.name, company.name, company.industry
// Find the shortest path between Alice and Eve
MATCH path = shortestPath(
(alice:Person {name: 'Alice'})-[:FRIENDS_WITH*]-(eve:Person {name: 'Eve'})
)
RETURN path, length(path) AS hops
// All paths between Alice and Eve (up to 5 hops)
MATCH path = (alice:Person {name: 'Alice'})-[:FRIENDS_WITH*..5]-(eve:Person {name: 'Eve'})
RETURN path, length(path) AS hops
ORDER BY hops
Example 2: E-commerce Recommendation System
// Create product catalog
CREATE (laptop:Product {id: 'P001', name: 'Pro Laptop', price: 1299.99, category: 'Electronics'})
CREATE (mouse:Product {id: 'P002', name: 'Wireless Mouse', price: 49.99, category: 'Electronics'})
CREATE (keyboard:Product {id: 'P003', name: 'Mechanical Keyboard', price: 129.99, category: 'Electronics'})
CREATE (headphones:Product {id: 'P004', name: 'Noise Canceling Headphones', price: 299.99, category: 'Electronics'})
CREATE (stand:Product {id: 'P005', name: 'Laptop Stand', price: 79.99, category: 'Accessories'})
CREATE (bag:Product {id: 'P006', name: 'Laptop Bag', price: 59.99, category: 'Accessories'})
// Create customers
CREATE (john:Customer {id: 'C001', name: 'John', email: 'john@email.com'})
CREATE (sarah:Customer {id: 'C002', name: 'Sarah', email: 'sarah@email.com'})
CREATE (mike:Customer {id: 'C003', name: 'Mike', email: 'mike@email.com'})
CREATE (lisa:Customer {id: 'C004', name: 'Lisa', email: 'lisa@email.com'})
// Create purchase relationships
CREATE (john)-[:PURCHASED {date: date('2024-01-10'), quantity: 1}]->(laptop)
CREATE (john)-[:PURCHASED {date: date('2024-01-10'), quantity: 1}]->(mouse)
CREATE (john)-[:PURCHASED {date: date('2024-01-15'), quantity: 1}]->(stand)
CREATE (sarah)-[:PURCHASED {date: date('2024-01-12'), quantity: 1}]->(laptop)
CREATE (sarah)-[:PURCHASED {date: date('2024-01-12'), quantity: 1}]->(keyboard)
CREATE (sarah)-[:PURCHASED {date: date('2024-01-12'), quantity: 1}]->(headphones)
CREATE (mike)-[:PURCHASED {date: date('2024-01-14'), quantity: 1}]->(mouse)
CREATE (mike)-[:PURCHASED {date: date('2024-01-14'), quantity: 1}]->(keyboard)
CREATE (lisa)-[:PURCHASED {date: date('2024-01-16'), quantity: 1}]->(laptop)
CREATE (lisa)-[:PURCHASED {date: date('2024-01-16'), quantity: 1}]->(bag)
// Create reviews
CREATE (john)-[:REVIEWED {rating: 5, text: 'Great laptop!', date: date('2024-01-20')}]->(laptop)
CREATE (sarah)-[:REVIEWED {rating: 4, text: 'Good keyboard', date: date('2024-01-18')}]->(keyboard)
CREATE (mike)-[:REVIEWED {rating: 5, text: 'Perfect mouse', date: date('2024-01-19')}]->(mouse)
// Recommendation: "Customers who bought X also bought Y"
MATCH (c:Customer)-[:PURCHASED]->(product:Product {name: 'Pro Laptop'}),
(c)-[:PURCHASED]->(other:Product)
WHERE other <> product
RETURN other.name AS also_bought, COUNT(*) AS frequency
ORDER BY frequency DESC
// Recommendation: Similar customers (bought same products)
MATCH (john:Customer {name: 'John'})-[:PURCHASED]->(p:Product)<-[:PURCHASED]-(similar:Customer)
WHERE similar <> john
WITH similar, COUNT(p) AS shared_products
ORDER BY shared_products DESC
RETURN similar.name, shared_products
// Recommend products that similar customers bought
MATCH (john:Customer {name: 'John'})-[:PURCHASED]->(p:Product)<-[:PURCHASED]-(similar:Customer),
(similar)-[:PURCHASED]->(rec:Product)
WHERE NOT (john)-[:PURCHASED]->(rec)
RETURN rec.name AS recommended, COUNT(*) AS score
ORDER BY score DESC
LIMIT 5
// Products frequently bought together (market basket analysis)
MATCH (c:Customer)-[:PURCHASED]->(p1:Product),
(c)-[:PURCHASED]->(p2:Product)
WHERE id(p1) < id(p2) // Avoid duplicates
WITH p1, p2, COUNT(*) AS co_purchases
WHERE co_purchases >= 2
RETURN p1.name, p2.name, co_purchases
ORDER BY co_purchases DESC
// Average rating for products
MATCH (p:Product)<-[r:REVIEWED]-(:Customer)
RETURN p.name, AVG(r.rating) AS avg_rating, COUNT(r) AS review_count
ORDER BY avg_rating DESC
Example 3: Fraud Detection Pattern
// Create accounts and transactions for fraud detection
CREATE (acc1:Account {id: 'A001', holder: 'John Smith', balance: 5000})
CREATE (acc2:Account {id: 'A002', holder: 'Sarah Johnson', balance: 10000})
CREATE (acc3:Account {id: 'A003', holder: 'Shell Corp 1', balance: 50000})
CREATE (acc4:Account {id: 'A004', holder: 'Shell Corp 2', balance: 25000})
CREATE (acc5:Account {id: 'A005', holder: 'Jane Doe', balance: 8000})
CREATE (acc6:Account {id: 'A006', holder: 'Mike Brown', balance: 15000})
// Create transaction chain (potential money laundering)
CREATE (acc1)-[:TRANSFERRED {amount: 4000, date: datetime('2024-01-15T10:00:00')}]->(acc3)
CREATE (acc3)-[:TRANSFERRED {amount: 3800, date: datetime('2024-01-15T11:00:00')}]->(acc4)
CREATE (acc4)-[:TRANSFERRED {amount: 3600, date: datetime('2024-01-15T12:00:00')}]->(acc2)
// Normal transactions
CREATE (acc5)-[:TRANSFERRED {amount: 500, date: datetime('2024-01-14T09:00:00')}]->(acc6)
CREATE (acc6)-[:TRANSFERRED {amount: 200, date: datetime('2024-01-16T14:00:00')}]->(acc5)
// Shared entities for detecting fraud rings
CREATE (addr1:Address {street: '123 Main St', city: 'New York'})
CREATE (addr2:Address {street: '456 Oak Ave', city: 'Boston'})
CREATE (phone1:Phone {number: '555-0100'})
CREATE (email1:Email {address: 'shared@suspicious.com'})
// Link accounts to shared entities
CREATE (acc3)-[:HAS_ADDRESS]->(addr1)
CREATE (acc4)-[:HAS_ADDRESS]->(addr1) // Suspicious: same address
CREATE (acc3)-[:HAS_PHONE]->(phone1)
CREATE (acc4)-[:HAS_PHONE]->(phone1) // Suspicious: same phone
CREATE (acc3)-[:HAS_EMAIL]->(email1)
CREATE (acc4)-[:HAS_EMAIL]->(email1) // Suspicious: same email
// Detect circular money flow (potential laundering)
MATCH path = (a:Account)-[:TRANSFERRED*3..6]->(a)
RETURN path, length(path) AS hops
// Detect rapid fund movement (funds in and out within 24 hours)
MATCH (source:Account)-[t1:TRANSFERRED]->(middle:Account)-[t2:TRANSFERRED]->(dest:Account)
WHERE t2.date > t1.date AND t2.date < t1.date + duration('P1D')
RETURN source.holder AS from, middle.holder AS through, dest.holder AS to,
t1.amount AS amount_in, t2.amount AS amount_out,
t1.amount - t2.amount AS retained
// Find accounts sharing multiple identifiers (fraud ring)
MATCH (a1:Account)-[:HAS_ADDRESS|HAS_PHONE|HAS_EMAIL]->(shared)<-[:HAS_ADDRESS|HAS_PHONE|HAS_EMAIL]-(a2:Account)
WHERE a1 <> a2
WITH a1, a2, COUNT(DISTINCT shared) AS shared_identifiers, COLLECT(shared) AS identifiers
WHERE shared_identifiers >= 2
RETURN a1.holder, a2.holder, shared_identifiers
// Calculate total money flow between suspicious accounts
MATCH (a1:Account)-[t:TRANSFERRED*]->(a2:Account)
WHERE a1.holder CONTAINS 'Shell' OR a2.holder CONTAINS 'Shell'
WITH a1, a2, relationships(t) AS transfers
RETURN a1.holder AS from, a2.holder AS to,
REDUCE(total = 0, t IN transfers | total + t.amount) AS total_transferred
Example 4: Graph Algorithms
// Note: Some algorithms require Neo4j Graph Data Science Library
// Degree Centrality (most connected nodes)
MATCH (p:Person)-[r:FRIENDS_WITH]-()
RETURN p.name, COUNT(r) AS degree
ORDER BY degree DESC
LIMIT 10
// PageRank-style influence (simplified)
// Find people who are connected to well-connected people
MATCH (p:Person)-[:FRIENDS_WITH]-(friend:Person)-[r:FRIENDS_WITH]-()
WITH p, COUNT(DISTINCT friend) AS direct_friends, COUNT(r) AS friends_connections
RETURN p.name, direct_friends, friends_connections,
direct_friends * 0.5 + friends_connections * 0.1 AS influence_score
ORDER BY influence_score DESC
// Community Detection (finding clusters)
// Simplified approach: find densely connected groups
MATCH (p1:Person)-[:FRIENDS_WITH]-(p2:Person)-[:FRIENDS_WITH]-(p3:Person)-[:FRIENDS_WITH]-(p1)
RETURN p1.name, p2.name, p3.name AS triangle_members
// Using Neo4j GDS Library (if available)
// Create in-memory graph projection
// CALL gds.graph.project('social', 'Person', 'FRIENDS_WITH')
// Run PageRank
// CALL gds.pageRank.stream('social')
// YIELD nodeId, score
// RETURN gds.util.asNode(nodeId).name AS name, score
// ORDER BY score DESC
// Run Louvain Community Detection
// CALL gds.louvain.stream('social')
// YIELD nodeId, communityId
// RETURN communityId, COLLECT(gds.util.asNode(nodeId).name) AS members
// ORDER BY communityId
// Betweenness Centrality (who bridges different groups)
// CALL gds.betweenness.stream('social')
// YIELD nodeId, score
// RETURN gds.util.asNode(nodeId).name AS name, score
// ORDER BY score DESC
// Path finding with weighted relationships
MATCH (start:Person {name: 'Alice'}), (end:Person {name: 'Eve'})
CALL gds.shortestPath.dijkstra.stream('social', {
sourceNode: start,
targetNode: end,
relationshipWeightProperty: 'strength'
})
YIELD path
RETURN path
6. Hands-On Practice (30-40 minutes)
Exercise 1: Knowledge Graph (15 minutes)
Objective: Build a knowledge graph for a movie database
Requirements:
- Model movies, actors, directors, and genres
- Support queries like “actors who worked with Director X”
- Find collaboration networks between actors
Solution:
// Create schema
CREATE (inception:Movie {title: 'Inception', year: 2010, rating: 8.8})
CREATE (darkKnight:Movie {title: 'The Dark Knight', year: 2008, rating: 9.0})
CREATE (interstellar:Movie {title: 'Interstellar', year: 2014, rating: 8.6})
CREATE (tenet:Movie {title: 'Tenet', year: 2020, rating: 7.4})
CREATE (prestige:Movie {title: 'The Prestige', year: 2006, rating: 8.5})
CREATE (nolan:Director {name: 'Christopher Nolan', born: 1970})
CREATE (dicaprio:Actor {name: 'Leonardo DiCaprio', born: 1974})
CREATE (bale:Actor {name: 'Christian Bale', born: 1974})
CREATE (hathaway:Actor {name: 'Anne Hathaway', born: 1982})
CREATE (caine:Actor {name: 'Michael Caine', born: 1933})
CREATE (mcconaughey:Actor {name: 'Matthew McConaughey', born: 1969})
CREATE (scifi:Genre {name: 'Sci-Fi'})
CREATE (action:Genre {name: 'Action'})
CREATE (thriller:Genre {name: 'Thriller'})
// Connect movies to director
CREATE (nolan)-[:DIRECTED]->(inception)
CREATE (nolan)-[:DIRECTED]->(darkKnight)
CREATE (nolan)-[:DIRECTED]->(interstellar)
CREATE (nolan)-[:DIRECTED]->(tenet)
CREATE (nolan)-[:DIRECTED]->(prestige)
// Connect actors to movies with roles
CREATE (dicaprio)-[:ACTED_IN {role: 'Cobb'}]->(inception)
CREATE (bale)-[:ACTED_IN {role: 'Batman'}]->(darkKnight)
CREATE (bale)-[:ACTED_IN {role: 'Alfred Borden'}]->(prestige)
CREATE (hathaway)-[:ACTED_IN {role: 'Catwoman'}]->(darkKnight)
CREATE (hathaway)-[:ACTED_IN {role: 'Brand'}]->(interstellar)
CREATE (caine)-[:ACTED_IN {role: 'Alfred'}]->(darkKnight)
CREATE (caine)-[:ACTED_IN {role: 'Miles'}]->(inception)
CREATE (caine)-[:ACTED_IN {role: 'Professor Brand'}]->(interstellar)
CREATE (mcconaughey)-[:ACTED_IN {role: 'Cooper'}]->(interstellar)
// Connect movies to genres
CREATE (inception)-[:HAS_GENRE]->(scifi)
CREATE (inception)-[:HAS_GENRE]->(action)
CREATE (darkKnight)-[:HAS_GENRE]->(action)
CREATE (interstellar)-[:HAS_GENRE]->(scifi)
CREATE (tenet)-[:HAS_GENRE]->(scifi)
CREATE (tenet)-[:HAS_GENRE]->(action)
CREATE (prestige)-[:HAS_GENRE]->(thriller)
// Queries
// Find all actors who worked with Christopher Nolan
MATCH (nolan:Director {name: 'Christopher Nolan'})-[:DIRECTED]->(m:Movie)<-[:ACTED_IN]-(actor:Actor)
RETURN DISTINCT actor.name AS actor, COLLECT(m.title) AS movies
ORDER BY SIZE(movies) DESC
// Find actors who worked together
MATCH (a1:Actor)-[:ACTED_IN]->(m:Movie)<-[:ACTED_IN]-(a2:Actor)
WHERE a1 <> a2 AND id(a1) < id(a2)
RETURN a1.name AS actor1, a2.name AS actor2, COLLECT(m.title) AS movies_together
ORDER BY SIZE(movies_together) DESC
// Find the "Kevin Bacon" number (degrees of separation)
MATCH path = shortestPath(
(dicaprio:Actor {name: 'Leonardo DiCaprio'})-[:ACTED_IN*]-(mcconaughey:Actor {name: 'Matthew McConaughey'})
)
RETURN length(path) / 2 AS degrees_of_separation,
[node IN nodes(path) WHERE node:Actor | node.name] AS actors,
[node IN nodes(path) WHERE node:Movie | node.title] AS movies
// Recommend movies based on actor preferences
// "If you like movies with Actor X, you might like..."
MATCH (caine:Actor {name: 'Michael Caine'})-[:ACTED_IN]->(m1:Movie)<-[:ACTED_IN]-(costar:Actor)-[:ACTED_IN]->(m2:Movie)
WHERE NOT (caine)-[:ACTED_IN]->(m2)
RETURN m2.title AS recommended, COLLECT(DISTINCT costar.name) AS via_actors
ORDER BY SIZE(via_actors) DESC
LIMIT 5
Exercise 2: Organizational Network (15 minutes)
Objective: Model an organization’s reporting structure and collaboration
Challenge:
- Track reporting relationships (manager-employee)
- Track project collaboration
- Find paths of influence for decisions
Solution:
// Create organizational structure
CREATE (ceo:Employee {name: 'Jane CEO', title: 'CEO', department: 'Executive'})
CREATE (cto:Employee {name: 'Bob CTO', title: 'CTO', department: 'Engineering'})
CREATE (cfo:Employee {name: 'Carol CFO', title: 'CFO', department: 'Finance'})
CREATE (vpEng:Employee {name: 'David VP', title: 'VP Engineering', department: 'Engineering'})
CREATE (mgr1:Employee {name: 'Eve Manager', title: 'Engineering Manager', department: 'Engineering'})
CREATE (dev1:Employee {name: 'Frank Dev', title: 'Senior Developer', department: 'Engineering'})
CREATE (dev2:Employee {name: 'Grace Dev', title: 'Developer', department: 'Engineering'})
CREATE (analyst:Employee {name: 'Henry Analyst', title: 'Financial Analyst', department: 'Finance'})
// Reporting relationships
CREATE (cto)-[:REPORTS_TO]->(ceo)
CREATE (cfo)-[:REPORTS_TO]->(ceo)
CREATE (vpEng)-[:REPORTS_TO]->(cto)
CREATE (mgr1)-[:REPORTS_TO]->(vpEng)
CREATE (dev1)-[:REPORTS_TO]->(mgr1)
CREATE (dev2)-[:REPORTS_TO]->(mgr1)
CREATE (analyst)-[:REPORTS_TO]->(cfo)
// Projects
CREATE (proj1:Project {name: 'Project Alpha', budget: 500000})
CREATE (proj2:Project {name: 'Project Beta', budget: 300000})
// Project assignments
CREATE (dev1)-[:WORKS_ON {role: 'Tech Lead', hours_per_week: 30}]->(proj1)
CREATE (dev2)-[:WORKS_ON {role: 'Developer', hours_per_week: 40}]->(proj1)
CREATE (dev1)-[:WORKS_ON {role: 'Contributor', hours_per_week: 10}]->(proj2)
CREATE (analyst)-[:WORKS_ON {role: 'Budget Analyst', hours_per_week: 5}]->(proj1)
// Skills
CREATE (java:Skill {name: 'Java'})
CREATE (python:Skill {name: 'Python'})
CREATE (finance:Skill {name: 'Financial Analysis'})
CREATE (dev1)-[:HAS_SKILL {level: 'Expert'}]->(java)
CREATE (dev1)-[:HAS_SKILL {level: 'Intermediate'}]->(python)
CREATE (dev2)-[:HAS_SKILL {level: 'Beginner'}]->(java)
CREATE (analyst)-[:HAS_SKILL {level: 'Expert'}]->(finance)
// Queries
// Find all direct and indirect reports for CTO
MATCH (cto:Employee {title: 'CTO'})<-[:REPORTS_TO*]-(report:Employee)
RETURN report.name, report.title, LENGTH((cto)<-[:REPORTS_TO*]-(report)) AS levels_below
// Find collaboration paths (people working on same projects)
MATCH (e1:Employee)-[:WORKS_ON]->(p:Project)<-[:WORKS_ON]-(e2:Employee)
WHERE e1 <> e2
RETURN e1.name, e2.name, COLLECT(p.name) AS shared_projects
// Find approval chain for decisions (shortest path to CEO)
MATCH path = shortestPath(
(dev1:Employee {name: 'Frank Dev'})-[:REPORTS_TO*]->(ceo:Employee {title: 'CEO'})
)
RETURN [node IN nodes(path) | node.name + ' (' + node.title + ')'] AS approval_chain
// Find people with specific skills
MATCH (e:Employee)-[hs:HAS_SKILL]->(s:Skill {name: 'Java'})
WHERE hs.level IN ['Expert', 'Intermediate']
RETURN e.name, e.title, hs.level AS java_level
// Cross-functional collaboration opportunities
MATCH (e1:Employee)-[:WORKS_ON]->(p:Project)<-[:WORKS_ON]-(e2:Employee)
WHERE e1.department <> e2.department
RETURN p.name AS project, COLLECT(DISTINCT e1.department + ': ' + e1.name) AS team_members
7. Common Pitfalls and Troubleshooting (10 minutes)
Frequent Mistakes
-
Problem: Modeling everything as properties instead of nodes Cause: Thinking in relational terms Solution: If you’ll query by it or connect to it, make it a node
-
Problem: Cartesian products from unconnected patterns Cause: Multiple MATCH clauses without connection Solution: Connect patterns or use WITH to control cardinality
-
Problem: Slow queries on large graphs Cause: Missing indexes on frequently queried properties Solution: Create indexes on lookup properties
-
Problem: Memory issues with large result sets Cause: Unbounded traversals returning too much data Solution: Use LIMIT, limit path depth, aggregate early
Debugging Tips
- Use PROFILE: See query execution plan and DB hits
- Use EXPLAIN: Preview plan without executing
- Check indexes: Ensure frequently queried properties are indexed
- Visualize small results: Use Neo4j Browser to see patterns
- Test patterns: Verify pattern matches expected nodes before full query
8. Best Practices and Professional Tips (5 minutes)
Industry Standards
- Use meaningful relationship types: FRIENDS_WITH not RELATED_TO
- Keep properties minimal: Complex data might need separate nodes
- Index strategically: Index properties used in MATCH or WHERE
- Use parameters: Never concatenate user input into queries
- Design for traversal: Structure to support your query patterns
Performance Tips
- Limit traversal depth: Use relationship bounds
*1..3 - Start from specific nodes: Use indexed property lookups
- Profile queries: Identify bottlenecks in execution
- Consider bidirectional: Some relationships logically go both ways
Next Steps in Your Learning Journey
- Graph Algorithms: Deep dive into GDS library
- Full-Text Search: Neo4j full-text indexes
- Graph Visualization: Building interactive graph UIs
- Real-Time Processing: Streaming data into graphs
9. Summary and Review (5 minutes)
Key Takeaways
- Graph databases model relationships as first-class citizens, not foreign key afterthoughts
- Cypher provides intuitive pattern matching for traversing connected data
- Graph algorithms enable powerful analytics like community detection and influence scoring
- Use graphs when relationships are central to your domain and queries
Quick Review Questions
-
What makes graph databases faster than SQL for relationship queries? Answer: Index-free adjacency—each node stores direct pointers to neighbors, eliminating JOIN operations
-
What are nodes, relationships, and properties in graph terminology? Answer: Nodes are entities, relationships connect nodes with direction and type, properties are key-value attributes on both
-
When would you NOT use a graph database? Answer: Simple CRUD operations, aggregation-heavy analytics without relationships, high-volume write-heavy OLTP systems
For Instructors
Teaching Tips
- Use visual examples extensively—graphs are inherently visual
- Start with familiar domains (social networks, recommendations)
- Live-code queries and show execution plans
- Compare equivalent SQL queries to demonstrate complexity difference
Assessment Ideas
- Graph modeling from domain description
- Query writing for specific traversal patterns
- Optimization of slow queries
- Algorithm selection for business problems
Adaptation Notes
- For beginners: Focus on basic CRUD and simple traversals
- For advanced students: Cover GDS algorithms, clustering, and optimization
- Online delivery: Use Neo4j Sandbox or Aura free tier
- Time constraints: Basic concepts and Cypher can be covered in 60 minutes