> ## Documentation Index
> Fetch the complete documentation index at: https://resources.devweekends.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Cypher Query Language Mastery

> Master Cypher pattern matching, aggregations, functions, and advanced query optimization techniques

# Cypher Query Language Mastery

<Info>
  **Module Duration**: 6-8 hours
  **Learning Style**: Hands-On + Pattern Recognition + Real-World Queries
  **Outcome**: Write efficient, expressive Cypher queries for any graph problem
</Info>

## Introduction: ASCII Art for Graphs

Cypher uses **ASCII art** to represent graph patterns:

```cypher theme={null}
-- Nodes: ()
(alice)                    -- node variable
(:Person)                  -- node with label
(alice:Person)             -- node variable with label
(alice:Person {name: "Alice"})  -- node with properties

-- Relationships: -[]->
-->                        -- directed relationship
-[:KNOWS]->               -- relationship with type
-[k:KNOWS]->              -- relationship variable
-[k:KNOWS {since: 2020}]-> -- relationship with properties
-[:KNOWS|:FOLLOWS]->      -- multiple types (OR)

-- Patterns: Combine nodes and relationships
(alice:Person)-[:KNOWS]->(bob:Person)
(a)-[:FRIEND*1..3]->(b)   -- variable length path (1-3 hops)
```

This visual syntax makes queries **intuitive** and **readable**.

***

## Part 1: Basic Pattern Matching

### MATCH: Find Patterns

**Simple node match**:

```cypher theme={null}
MATCH (p:Person)
RETURN p.name, p.age
```

**Relationship match**:

```cypher theme={null}
MATCH (alice:Person {name: "Alice"})-[:KNOWS]->(friend)
RETURN friend.name
```

**Multi-hop**:

```cypher theme={null}
-- Friends of friends
MATCH (alice:Person {name: "Alice"})-[:KNOWS]->(friend)-[:KNOWS]->(fof)
RETURN fof.name
```

**Undirected relationship** (match either direction):

```cypher theme={null}
MATCH (alice:Person {name: "Alice"})-[:KNOWS]-(friend)
RETURN friend.name
```

### WHERE: Filter Results

```cypher theme={null}
MATCH (p:Person)-[:KNOWS]->(friend)
WHERE p.name = "Alice"
  AND friend.age > 25
RETURN friend.name, friend.age
```

**Pattern predicates**:

```cypher theme={null}
-- Find people who DON'T know Bob
MATCH (p:Person)
WHERE NOT (p)-[:KNOWS]->(:Person {name: "Bob"})
RETURN p.name
```

**String operations**:

```cypher theme={null}
MATCH (p:Person)
WHERE p.name STARTS WITH "A"   -- or ENDS WITH, CONTAINS
RETURN p.name
```

**Regular expressions**:

```cypher theme={null}
MATCH (p:Person)
WHERE p.email =~ ".*@example\\.com"
RETURN p.name, p.email
```

**List membership**:

```cypher theme={null}
MATCH (p:Person)
WHERE p.name IN ["Alice", "Bob", "Charlie"]
RETURN p.name
```

### CREATE: Add Data

**Create node**:

```cypher theme={null}
CREATE (p:Person {name: "Alice", age: 30})
RETURN p
```

**Create relationship**:

```cypher theme={null}
MATCH (alice:Person {name: "Alice"})
MATCH (bob:Person {name: "Bob"})
CREATE (alice)-[:KNOWS {since: 2020}]->(bob)
```

**Create pattern**:

```cypher theme={null}
CREATE (alice:Person {name: "Alice"})-[:KNOWS]->(bob:Person {name: "Bob"})
```

### MERGE: Create if not exists

**Idempotent create** (prevents duplicates):

```cypher theme={null}
MERGE (p:Person {name: "Alice"})
ON CREATE SET p.created = timestamp()
ON MATCH SET p.accessed = timestamp()
RETURN p
```

**Merge relationship**:

```cypher theme={null}
MATCH (alice:Person {name: "Alice"})
MATCH (bob:Person {name: "Bob"})
MERGE (alice)-[k:KNOWS]->(bob)
ON CREATE SET k.since = 2020
RETURN k
```

### SET: Update Properties

```cypher theme={null}
MATCH (p:Person {name: "Alice"})
SET p.age = 31,
    p.city = "San Francisco"
RETURN p
```

**Add label**:

```cypher theme={null}
MATCH (p:Person {name: "Alice"})
SET p:Engineer
RETURN labels(p)  -- ["Person", "Engineer"]
```

**Copy properties**:

```cypher theme={null}
MATCH (alice:Person {name: "Alice"})
MATCH (bob:Person {name: "Bob"})
SET bob = alice  -- Copy all properties
```

### DELETE: Remove Data

**Delete node** (must delete relationships first!):

```cypher theme={null}
MATCH (p:Person {name: "Alice"})
DETACH DELETE p  -- Delete node and all relationships
```

**Delete relationship**:

```cypher theme={null}
MATCH (alice)-[k:KNOWS]->(bob)
DELETE k
```

**Conditional delete**:

```cypher theme={null}
MATCH (p:Person)
WHERE p.age > 100
DETACH DELETE p
```

***

## Part 2: Variable-Length Paths

### Syntax: `*min..max`

**Friends within 1-3 hops**:

```cypher theme={null}
MATCH (alice:Person {name: "Alice"})-[:KNOWS*1..3]->(friend)
RETURN DISTINCT friend.name
```

**Any depth** (use carefully!):

```cypher theme={null}
MATCH (alice:Person {name: "Alice"})-[:KNOWS*]->(friend)
RETURN friend.name
LIMIT 100  -- Always limit unbounded paths!
```

### Shortest Path

**Find shortest path between two nodes**:

```cypher theme={null}
MATCH path = shortestPath(
  (alice:Person {name: "Alice"})-[:KNOWS*]-(bob:Person {name: "Bob"})
)
RETURN length(path) AS hops, nodes(path) AS people
```

**All shortest paths**:

```cypher theme={null}
MATCH path = allShortestPaths(
  (alice:Person {name: "Alice"})-[:KNOWS*]-(bob:Person {name: "Bob"})
)
RETURN path
```

### Path Functions

```cypher theme={null}
MATCH path = (alice:Person {name: "Alice"})-[:KNOWS*1..3]->(friend)
RETURN
  length(path) AS hops,           -- Number of relationships
  nodes(path) AS all_nodes,       -- All nodes in path
  relationships(path) AS all_rels -- All relationships in path
LIMIT 10
```

***

## Part 3: Aggregations

### Basic Aggregations

```cypher theme={null}
MATCH (p:Person)
RETURN
  count(p) AS total,
  avg(p.age) AS avg_age,
  min(p.age) AS min_age,
  max(p.age) AS max_age,
  sum(p.salary) AS total_salary
```

### GROUP BY (implicit)

Cypher groups by **non-aggregated** columns:

```cypher theme={null}
-- Count friends per person (grouped by p.name)
MATCH (p:Person)-[:KNOWS]->(friend)
RETURN p.name, count(friend) AS friend_count
ORDER BY friend_count DESC
```

### COLLECT: Aggregate to List

```cypher theme={null}
-- Collect all friends into a list
MATCH (p:Person {name: "Alice"})-[:KNOWS]->(friend)
RETURN p.name, collect(friend.name) AS friends
```

**Collect distinct**:

```cypher theme={null}
MATCH (p:Person)-[:KNOWS]->(friend)
RETURN p.name, collect(DISTINCT friend.name) AS unique_friends
```

### UNWIND: List to Rows

```cypher theme={null}
-- Expand list into rows
UNWIND ["Alice", "Bob", "Charlie"] AS name
CREATE (p:Person {name: name})
```

**Flatten nested data**:

```cypher theme={null}
WITH [[1,2], [3,4], [5]] AS nested
UNWIND nested AS list
UNWIND list AS value
RETURN value  -- Returns: 1, 2, 3, 4, 5
```

***

## Part 4: Advanced Patterns

### OPTIONAL MATCH: Left Outer Join

```cypher theme={null}
-- Find all people, with their friends (if any)
MATCH (p:Person)
OPTIONAL MATCH (p)-[:KNOWS]->(friend)
RETURN p.name, collect(friend.name) AS friends
-- People with no friends will have empty list []
```

### Multiple Patterns

**Comma separates independent patterns**:

```cypher theme={null}
MATCH (alice:Person {name: "Alice"}),
      (bob:Person {name: "Bob"})
CREATE (alice)-[:KNOWS]->(bob)
```

**Chained patterns**:

```cypher theme={null}
MATCH (alice:Person {name: "Alice"})-[:KNOWS]->(friend)-[:LIKES]->(movie:Movie)
RETURN movie.title, count(friend) AS popularity
ORDER BY popularity DESC
```

### NOT Pattern

```cypher theme={null}
-- Find people who don't know Bob
MATCH (p:Person)
WHERE NOT (p)-[:KNOWS]->(:Person {name: "Bob"})
RETURN p.name
```

### EXISTS Subquery (Neo4j 4.0+)

```cypher theme={null}
-- Find people who know someone in San Francisco
MATCH (p:Person)
WHERE EXISTS {
  MATCH (p)-[:KNOWS]->(friend:Person)
  WHERE friend.city = "San Francisco"
}
RETURN p.name
```

### CASE Expressions

```cypher theme={null}
MATCH (p:Person)
RETURN p.name,
  CASE
    WHEN p.age < 18 THEN "Minor"
    WHEN p.age < 65 THEN "Adult"
    ELSE "Senior"
  END AS age_group
```

***

## Part 5: Functions

### String Functions

```cypher theme={null}
RETURN
  toUpper("hello") AS upper,      -- "HELLO"
  toLower("WORLD") AS lower,      -- "world"
  substring("Hello", 1, 3) AS sub, -- "ell"
  replace("Hello", "l", "L") AS r, -- "HeLLo"
  split("a,b,c", ",") AS parts,   -- ["a","b","c"]
  trim("  hello  ") AS trimmed    -- "hello"
```

### Math Functions

```cypher theme={null}
RETURN
  abs(-5) AS absolute,       -- 5
  ceil(4.3) AS ceiling,      -- 5
  floor(4.7) AS floor,       -- 4
  round(4.5) AS rounded,     -- 5
  sqrt(16) AS square_root,   -- 4
  rand() AS random           -- 0.0 to 1.0
```

### List Functions

```cypher theme={null}
WITH [1, 2, 3, 4, 5] AS numbers
RETURN
  size(numbers) AS length,           -- 5
  head(numbers) AS first,            -- 1
  tail(numbers) AS rest,             -- [2,3,4,5]
  last(numbers) AS last_elem,        -- 5
  range(1, 10) AS one_to_ten,        -- [1,2,...,10]
  [x IN numbers WHERE x > 3] AS filtered  -- [4,5]
```

### Date/Time Functions

```cypher theme={null}
RETURN
  date() AS today,                    -- 2024-01-15
  datetime() AS now,                  -- 2024-01-15T10:30:00Z
  timestamp() AS unix_timestamp,      -- 1705317000000
  duration.between(date("2020-01-01"), date()) AS days_since
```

### Graph Functions

```cypher theme={null}
MATCH (p:Person)-[r:KNOWS]->(friend)
RETURN
  id(p) AS node_id,          -- Internal node ID
  id(r) AS rel_id,           -- Internal relationship ID
  labels(p) AS node_labels,  -- ["Person"]
  type(r) AS rel_type,       -- "KNOWS"
  keys(p) AS property_keys,  -- ["name", "age"]
  properties(p) AS all_props -- {name: "Alice", age: 30}
```

***

## Part 6: Performance Optimization

### 1. Use Indexes

**Create index**:

```cypher theme={null}
CREATE INDEX person_name FOR (p:Person) ON (p.name)
```

**Check query plan**:

```cypher theme={null}
EXPLAIN
MATCH (p:Person {name: "Alice"})
RETURN p
-- Look for "NodeIndexSeek" (good) vs "NodeByLabelScan" (bad)
```

### 2. Filter Early

**Bad** (filter after expand):

```cypher theme={null}
MATCH (p:Person)-[:KNOWS]->(friend)
WHERE p.name = "Alice" AND friend.age > 25
RETURN friend.name
```

**Good** (filter before expand):

```cypher theme={null}
MATCH (p:Person {name: "Alice"})-[:KNOWS]->(friend:Person)
WHERE friend.age > 25
RETURN friend.name
```

### 3. Limit Results

```cypher theme={null}
MATCH (p:Person)-[:KNOWS]->(friend)
RETURN friend.name
LIMIT 100  -- Stop after 100 results
```

### 4. Use PROFILE to Find Bottlenecks

```cypher theme={null}
PROFILE
MATCH (p:Person {name: "Alice"})-[:KNOWS*2]->(fof)
RETURN fof.name
```

Look for:

* **High DB Hits** → Optimize
* **NodeByLabelScan** → Add index
* **Cartesian Products** → Fix query logic

### 5. Avoid Cartesian Products

**Bad** (creates all combinations):

```cypher theme={null}
MATCH (p:Person), (m:Movie)  -- Cartesian product!
WHERE p.name = "Alice" AND m.title = "The Matrix"
RETURN p, m
```

**Good** (connected pattern):

```cypher theme={null}
MATCH (p:Person {name: "Alice"})-[:WATCHED]->(m:Movie {title: "The Matrix"})
RETURN p, m
```

***

## Part 7: Common Patterns

### Pattern 1: Recommendations

**"People like you who bought X also bought Y"**:

```cypher theme={null}
MATCH (user:User {id: $userId})-[:PURCHASED]->(product:Product)
MATCH (product)<-[:PURCHASED]-(other:User)-[:PURCHASED]->(recommendation:Product)
WHERE NOT (user)-[:PURCHASED]->(recommendation)
RETURN recommendation.name, count(*) AS score
ORDER BY score DESC
LIMIT 10
```

### Pattern 2: Shortest Path

**"How are Alice and Bob connected?"**:

```cypher theme={null}
MATCH path = shortestPath(
  (alice:Person {name: "Alice"})-[:KNOWS*]-(bob:Person {name: "Bob"})
)
RETURN [node IN nodes(path) | node.name] AS connection_path
```

### Pattern 3: Influence/Centrality

**"Who has the most friends?"** (degree centrality):

```cypher theme={null}
MATCH (p:Person)-[:KNOWS]->(friend)
RETURN p.name, count(friend) AS friend_count
ORDER BY friend_count DESC
LIMIT 10
```

### Pattern 4: Community Detection

**"Find groups of mutual friends"** (triangles):

```cypher theme={null}
MATCH (a:Person)-[:KNOWS]->(b:Person)-[:KNOWS]->(c:Person)-[:KNOWS]->(a)
RETURN a.name, b.name, c.name
```

### Pattern 5: Hierarchical Data

**"Find all employees under a manager"** (recursive):

```cypher theme={null}
MATCH (manager:Employee {name: "Alice"})-[:MANAGES*]->(employee)
RETURN employee.name, length(path) AS levels_down
```

### Pattern 6: Time-Series Analysis

**"What did users buy in the last 30 days?"**:

```cypher theme={null}
MATCH (user:User)-[p:PURCHASED]->(product:Product)
WHERE p.timestamp > datetime() - duration({days: 30})
RETURN user.name, collect(product.name) AS recent_purchases
```

***

## Part 8: Hands-On Exercises

### Exercise 1: Social Network Queries

**Setup**:

```cypher theme={null}
CREATE (alice:Person {name: "Alice", age: 30, city: "NYC"})
CREATE (bob:Person {name: "Bob", age: 28, city: "SF"})
CREATE (charlie:Person {name: "Charlie", age: 35, city: "NYC"})
CREATE (alice)-[:KNOWS {since: 2015}]->(bob)
CREATE (bob)-[:KNOWS {since: 2018}]->(charlie)
CREATE (charlie)-[:KNOWS {since: 2020}]->(alice)
```

**Queries**:

1. Find Alice's direct friends:

```cypher theme={null}
MATCH (alice:Person {name: "Alice"})-[:KNOWS]->(friend)
RETURN friend.name
```

2. Friends of friends (excluding Alice):

```cypher theme={null}
MATCH (alice:Person {name: "Alice"})-[:KNOWS*2]->(fof)
WHERE fof <> alice
RETURN DISTINCT fof.name
```

3. Mutual friends of Alice and Bob:

```cypher theme={null}
MATCH (alice:Person {name: "Alice"})-[:KNOWS]->(mutual)<-[:KNOWS]-(bob:Person {name: "Bob"})
RETURN mutual.name
```

4. Average age of Alice's friends:

```cypher theme={null}
MATCH (alice:Person {name: "Alice"})-[:KNOWS]->(friend)
RETURN avg(friend.age) AS avg_age
```

### Exercise 2: Movie Recommendations

**Setup**:

```cypher theme={null}
CREATE (alice:User {name: "Alice"})
CREATE (bob:User {name: "Bob"})
CREATE (matrix:Movie {title: "The Matrix", year: 1999})
CREATE (inception:Movie {title: "Inception", year: 2010})
CREATE (alice)-[:WATCHED]->(matrix)
CREATE (alice)-[:WATCHED]->(inception)
CREATE (bob)-[:WATCHED]->(matrix)
```

**Query**: Recommend movies Bob hasn't seen (based on Alice):

```cypher theme={null}
MATCH (bob:User {name: "Bob"})-[:WATCHED]->(watched:Movie)
MATCH (watched)<-[:WATCHED]-(other:User)-[:WATCHED]->(rec:Movie)
WHERE NOT (bob)-[:WATCHED]->(rec)
RETURN rec.title, count(*) AS score
ORDER BY score DESC
```

### Exercise 3: Performance Optimization

**Original query** (slow):

```cypher theme={null}
MATCH (p:Person)-[:KNOWS]->(friend)
WHERE p.name = "Alice"
RETURN friend.name
```

**Optimized**:

```cypher theme={null}
CREATE INDEX person_name FOR (p:Person) ON (p.name);

MATCH (p:Person {name: "Alice"})-[:KNOWS]->(friend)
RETURN friend.name
```

**Verify improvement**:

```cypher theme={null}
PROFILE [query]
-- Compare DB Hits before and after
```

***

## Summary

**Pattern Matching**: Use ASCII art for intuitive graph patterns
**Filtering**: WHERE clause for predicates, pattern-based filtering
**Aggregations**: count, avg, sum, collect for grouped data
**Variable Paths**: `*min..max` for flexible traversals
**Functions**: Rich library for strings, math, lists, dates
**Optimization**: Indexes, early filtering, PROFILE for analysis

**Next Steps**: Apply Cypher to real-world data modeling scenarios!

***

## What's Next?

<Card title="Module 5: Graph Data Modeling" icon="diagram-project" href="/distributed-systems-tools/neo4j-data-modeling">
  Design efficient graph schemas, handle many-to-many relationships, and model complex domains
</Card>
