Multi-Hop Traversals for Recommendations

Introduction

You created CONTAINS relationships and completed the Customer→Order→Product path. Now you’ll see the full power of graph traversals.

In this lesson, you will learn how to write multi-hop queries that would be nightmares in SQL but are natural in Cypher.

Understanding the complete path

You now have this structure:

mermaid
%%{init: {
  "theme": "base",
  "themeVariables": {
    "primaryColor": "#eef6f9",
    "primaryBorderColor": "#c7e0ec",
    "lineColor": "#94a3b8",
    "fontFamily": "Public Sans, Arial, Helvetica, sans-serif"
  }
}}%%
graph LR
    Customer((Customer)):::primary -->|PLACED| Order((Order)):::highlight
    Order -->|ORDERS| Product((Product)):::forest

    classDef primary fill:#eef6f9,stroke:#c7e0ec,stroke-width:1.25px,color:#0b5c7a
    classDef highlight fill:#f4f5ff,stroke:#c7d2fe,stroke-width:1.25px,color:#3730a3
    classDef forest fill:#edf6e8,stroke:#b7df9c,stroke-width:1.25px,color:#2f5d1e
    linkStyle default stroke:#94a3b8,stroke-width:1.25px

This enables multi-hop traversals:

  • Customer → Order → Product (what did they buy?)

  • Product ← Order ← Customer (who bought this?)

  • Customer → Order → Product ← Order ← Customer (who bought similar products?)

Finding what customers bought

The fundamental query - traverse two hops:

cypher
Products purchased by ALFKI
MATCH (c:Customer {id: 'ALFKI'})-[:PLACED]->(:Order)-[:CONTAINS]->(p:Product)        // (1)
RETURN DISTINCT p.name                                                                // (2)
ORDER BY p.name                                                                       // (3)
  1. Two-hop pattern - Customer through Order to Product (anonymous Order node)

  2. Distinct - Eliminate duplicates (customer may order same product multiple times)

  3. Sort - Alphabetical order

Click Run to see all products ALFKI has ever purchased.

SQL equivalent (3 JOINs)

The same query in SQL requires three table joins:

sql
SQL query with three table joins
SELECT DISTINCT p.productName
FROM customers c
JOIN orders o ON c.customerID = o.customerID
JOIN order_details od ON o.orderID = od.orderID
JOIN products p ON od.productID = p.productID
WHERE c.customerID = 'ALFKI'
ORDER BY p.productName;

In Cypher, you follow relationships directly. In SQL, you must explicitly join every table along the path.

Finding who bought products

Traverse backward from product to customers:

cypher
Customers who bought Chai
MATCH (c:Customer)-[:PLACED]->(:Order)-[:CONTAINS]->(p:Product {name: 'Chai'})        // (1)
RETURN DISTINCT c.name, c.city, c.country                                             // (2)
ORDER BY c.name                                                                       // (3)
  1. Reverse pattern - Start at Product, traverse back through orders to customers

  2. Customer info - Return company details

  3. Sort - By company name

Click Run to see all customers who have purchased Chai.

Analyzing customer favorites

Aggregate relationship properties across the path:

cypher
ALFKI’s most ordered products
MATCH (c:Customer {id: 'ALFKI'})-[:PLACED]->(:Order)-[r:CONTAINS]->(p:Product)
RETURN p.name,
       sum(r.quantity) AS totalQuantity,
       count(DISTINCT r) AS numberOfOrders,
       avg(r.unitPrice) AS avgPrice
ORDER BY totalQuantity DESC
LIMIT 10

Click Run to see ALFKI’s favorite products by volume.

Understanding the aggregation

cypher
MATCH (c:Customer {id: 'ALFKI'})-[:PLACED]->(:Order)-[r:CONTAINS]->(p:Product)  // (1)
RETURN p.name,                                                                   // (2)
       sum(r.quantity) AS totalQuantity,                                         // (3)
       count(DISTINCT r) AS numberOfOrders                                       // (4)
  1. Capture relationship - Variable r accesses CONTAINS properties

  2. Group by product - Implicitly groups by product name

  3. Sum quantities - Total units ordered across all orders

  4. Count orders - How many times this product was ordered

Measuring product popularity

Find the most popular products across all customers:

cypher
Top 10 most ordered products
MATCH (:Customer)-[:PLACED]->(:Order)-[r:CONTAINS]->(p:Product)
RETURN p.name,
       sum(r.quantity) AS totalQuantity,
       count(DISTINCT r) AS timesOrdered
ORDER BY totalQuantity DESC
LIMIT 10

Click Run to see your bestsellers.

Understanding product popularity query

cypher
MATCH (:Customer)-[:PLACED]->(:Order)-[r:CONTAINS]->(p:Product)  // (1)
RETURN p.name,                                                    // (2)
       sum(r.quantity) AS totalQuantity,                          // (3)
       count(DISTINCT r) AS timesOrdered                          // (4)
  1. All customers - No filter, traverse entire graph

  2. Group by product - Aggregate per product

  3. Total quantity - Sum across all orders from all customers

  4. Order count - How many individual orders included this product

Calculating customer spending

Calculate total spending using relationship properties:

cypher
Top spending customers
MATCH (c:Customer)-[:PLACED]->(:Order)-[r:CONTAINS]->(:Product)
RETURN c.name,
       sum(r.quantity * r.unitPrice) AS totalSpent,
       count(DISTINCT r) AS itemsOrdered
ORDER BY totalSpent DESC
LIMIT 10

Click Run to see your top customers by revenue.

Understanding customer spending query

cypher
MATCH (c:Customer)-[:PLACED]->(:Order)-[r:CONTAINS]->(:Product)  // (1)
RETURN c.name,                                                    // (2)
       sum(r.quantity * r.unitPrice) AS totalSpent,               // (3)
       count(DISTINCT r) AS itemsOrdered                          // (4)
  1. Traverse path - Customer through orders to products

  2. Group by customer - Aggregate per customer

  3. Calculate spending - Multiply quantity × unitPrice, sum total

  4. Count items - Total distinct products ordered

Building the recommendation pattern

All these queries use the same fundamental pattern that powers recommendations:

mermaid
Basic recommendation traversal pattern
%%{init: {
  "theme": "base",
  "themeVariables": {
    "primaryColor": "#eef6f9",
    "primaryBorderColor": "#c7e0ec",
    "lineColor": "#94a3b8",
    "fontFamily": "Public Sans, Arial, Helvetica, sans-serif"
  }
}}%%
graph LR
    Customer((Customer)):::primary -->|PLACED| Order((Order)):::highlight
    Order -->|CONTAINS| Product((Product)):::forest

    classDef primary fill:#eef6f9,stroke:#c7e0ec,stroke-width:1.25px,color:#0b5c7a
    classDef highlight fill:#f4f5ff,stroke:#c7d2fe,stroke-width:1.25px,color:#3730a3
    classDef forest fill:#edf6e8,stroke:#b7df9c,stroke-width:1.25px,color:#2f5d1e
    linkStyle default stroke:#94a3b8,stroke-width:1.25px

To recommend products:

  1. Find products I bought: (me:Customer)-[:PLACED]→(:Order)-[:CONTAINS]→(myProduct)

  2. Find customers who bought those products: (myProduct)←[:CONTAINS]-(:Order)←[:PLACED]-(other:Customer)

  3. Find what THEY bought that I haven’t: (other)-[:PLACED]→(:Order)-[:CONTAINS]→(rec:Product)

You’re one step away from the complete recommendation query!

SQL complexity: 3 JOINs required

Each of these queries requires 3 JOINs in SQL:

sql
Complex SQL with multiple JOINs and aggregation
SELECT c.companyName,
       SUM(od.quantity * od.unitPrice) AS totalSpent,
       COUNT(DISTINCT od.orderDetailID) AS itemsOrdered
FROM customers c
JOIN orders o ON c.customerID = o.customerID
JOIN order_details od ON o.orderID = od.orderID
GROUP BY c.companyName
ORDER BY totalSpent DESC
LIMIT 10;

Cypher simplicity: Direct traversal

The same query in Cypher:

cypher
Simple Cypher traversal with aggregation
MATCH (c:Customer)-[:PLACED]->(:Order)-[r:CONTAINS]->(:Product)
RETURN c.name,
       sum(r.quantity * r.unitPrice) AS totalSpent,
       count(DISTINCT r) AS itemsOrdered
ORDER BY totalSpent DESC
LIMIT 10

The Cypher reads like the question. The SQL is machinery.

Summary

In this lesson, you learned about multi-hop traversals:

  • Two-hop patterns - Customer→Order→Product traverses the complete path

  • Bidirectional - Can traverse forward or backward through relationships

  • Relationship properties - Aggregate quantities, prices, and other connection data

  • Anonymous nodes - Use (:Order) when you don’t need to reference the node

  • Complex analytics - Product popularity, customer spending, favorite products

  • Foundation for recommendations - Same pattern powers the recommendation query

  • SQL comparison - Cypher is readable and performant vs complex JOINs

In the next lesson (optional), you can practice writing multi-hop queries.

Chatbot

How can I help you today?

Data Model

Your data model will appear here.