Complex Pattern Matching
Introduction
Welcome to the final lesson of Advanced Queries! You've completed an impressive journey through aggregations, pattern matching, multi-hop traversals, and shortest paths. Each lesson has built upon the last, expanding your ability to extract insights from connected data.
Throughout this course, we've learned powerful techniques for querying graphs, but we've primarily focused on one pattern at a time. However, real-world problems often require combining multiple patterns to capture complex relationships. For instance, we might want to recommend movies based on both similar viewing history and shared genre preferences, or find friends who not only share connections but also have common interests.
In this lesson, we'll discover how to combine multiple patterns within a single query to perform sophisticated analysis. We'll learn how to match independent patterns simultaneously, filter across these patterns to refine results, and understand how Neo4j executes these queries efficiently. These techniques are essential for building recommendation systems, analyzing multi-faceted relationships, and extracting nuanced insights from graph data.
Combining Multiple Patterns
When analyzing graphs, we often need to consider several types of relationships at once. A single pattern tells us about one aspect of the data, but combining patterns reveals richer insights. For example, knowing that two users are friends is useful, but knowing they're friends who also share interests is far more meaningful.
Neo4j allows us to specify multiple patterns in a single query using two approaches. First, we can include several comma-separated patterns within one MATCH clause. Second, we can use consecutive MATCH clauses to build up complexity gradually. Both approaches enable us to express sophisticated queries that consider multiple relationship types and node properties simultaneously.
The key advantage of combining patterns is that we can express conditions that span different parts of the graph. We might match one pattern to find potential recommendations and another pattern to verify shared characteristics. This multidimensional matching is what makes graph databases exceptionally powerful for complex analysis.
Finding Friends with Shared Interests
Let's start with a practical example: finding which of Alice's friends share common interests with her. This requires matching two distinct patterns simultaneously:
This query combines two patterns separated by a comma. The first pattern (me)-[:FRIENDS_WITH]->(friend) finds all of Alice's friends. The second pattern (me)-[:LIKES]->(interest)<-[:LIKES]-(friend) finds interests that both Alice and her friend like. Notice how both patterns reference the same me and friend variables, creating a connection between them.
The structure here is important: we're not just finding friends, and we're not just finding interests; we're finding friends who share specific interests with Alice. The comma separating the patterns tells Neo4j to match both conditions simultaneously. The collect() function then aggregates all shared interests for each friend into a convenient list.
Visualizing Pattern Overlap
To see how these patterns connect, let's visualize the graph structure:

In this visualization, you can clearly see how the two patterns overlap through shared nodes. Alice connects to both her friends (Bob and David) through FRIENDS_WITH relationships, and the LIKES relationships converge on the shared Interest nodes. This visual representation makes it immediately clear how the variable connections work—me and friend appear in both patterns, creating the join condition that finds shared interests.
Understanding the Shared Interests Output
When we run this query, we get a clear picture of social overlap:
This output reveals that Bob shares one interest with Alice (Sci-Fi), while Charlie shares two interests with Alice (Photography and Sci-Fi). This kind of information is invaluable for recommendations or community detection. We might suggest that Alice and Charlie attend a photography workshop together, or we could identify clusters of friends with similar interests.
The aggregation into a list makes the results immediately actionable. Instead of seeing separate rows for each shared interest, we get a consolidated view showing the full overlap between Alice and each friend.
Building Multi-Signal Recommendations
Now let's tackle a more sophisticated scenario: recommending movies based on multiple signals. We want to suggest movies that similar users have watched, but only if those movies match Alice's genre preferences. This requires combining patterns with intermediate processing:
This query combines multiple patterns to create recommendations based on both collaborative filtering (similar users) and content-based filtering (matching genres). Let's break down each step in the next section.
Understanding the Query Logic
Let's examine this complex query step by step. The first line finds all movies Alice has watched, then we use WITH to collect their genres into a list while preserving the me variable. This creates a checkpoint in our query, aggregating data before moving forward.
After collecting Alice's preferred genres, we introduce two more MATCH clauses:
The first pattern finds users who have watched the same movies as Alice; these are users with similar viewing histories. The second pattern finds movies those similar users have watched. The WHERE clause applies two critical filters: we exclude movies Alice has already watched, and we only consider movies in genres Alice likes.
When we execute this complete query, we get a ranked list of recommendations:
These results show us which movies are most strongly recommended based on similar users' viewing patterns. "Interstellar" appears at the top because three users with viewing habits similar to Alice's have watched it. This multi-signal approach combines collaborative filtering (similar users) with content-based filtering (matching genres), creating more relevant recommendations than either approach alone.
Visualizing Similar Users Pattern
Here's how the similarity pattern looks in the graph:

This visualization shows how similarity is discovered through shared connections. Alice and Emma are connected indirectly through movies they've both watched (Inception). This shared movie creates a bridge that identifies Emma as a similar user. From Emma, we can then traverse to movies she's watched that Alice hasn't (Tenet), creating the foundation for recommendations.
This layered approach demonstrates how multiple patterns work together. Each MATCH builds on variables from previous patterns, creating a chain of logical conditions. The patterns aren't independent; they're interconnected through shared variables like me and similar.
The Role of WITH in Complex Queries
The WITH clause in our recommendation query serves a crucial purpose: it controls the flow of data through the query. After finding movies Alice has watched, we could continue immediately with more patterns, but aggregating the genres first gives us a filtered list to work with:
This line creates a pipeline break. It tells Neo4j to process everything before this point, produce a result with just the variables we specify (me and myGenres), and then continue. This approach is particularly useful when we need to aggregate data before using it in subsequent patterns or when we want to reduce the amount of data flowing through the rest of the query.
The WITH clause also allows us to perform calculations, aggregations, or filtering between patterns. It gives us control over query execution, letting us structure complex analyses into manageable steps. Without it, expressing certain multistage queries would be difficult or impossible.
Query Execution and Optimization
When Neo4j executes a query with multiple patterns, it doesn't necessarily process them in the order we write them. The query planner analyzes the patterns and chooses an efficient execution strategy based on available indexes, node counts, and relationship densities. However, the order we present matters for readability and can influence planning decisions.
The planner looks for selective starting points, ideally using indexes on node properties like User.name. It then expands along relationships, trying to filter results as early as possible to avoid processing unnecessary data. When we split patterns across multiple MATCH clauses, we give the planner opportunities to apply filters between expansions.
This optimization process happens automatically, but understanding it helps us write better queries. We should anchor our queries with specific nodes when possible, use labels and relationship types to narrow the search space, and consider using WITH to control cardinality when patterns might generate large intermediate results.
Practical Considerations for Multi-Pattern Queries
When combining patterns, we must be mindful of potential performance pitfalls. The most common issue is accidentally creating a Cartesian product by matching patterns that don't share variables. For example, matching all users and all products without connecting them would multiply every user by every product, creating an enormous result set.
Another consideration is the order of patterns and filters. While the planner can optimize, we can help by placing more selective patterns first and applying filters as early as possible. If one pattern is highly selective (like matching a specific user by ID), we should typically start with that pattern to anchor the query.
For recommendation queries like our movie example, limiting the scope at each step prevents the explosion of intermediate results. Notice how we filter movies both by exclusion (movies not already watched) and by genre preference. These filters dramatically reduce the working set, making the query practical even on large datasets.
Conclusion and Next Steps
In this final lesson of Advanced Queries, we've mastered the art of combining multiple patterns to perform sophisticated graph analysis. We learned how to match independent patterns simultaneously, apply filters across these patterns to refine results, and use the WITH clause to control query flow and reduce cardinality. These techniques enable us to build complex recommendation systems, analyze multi-faceted relationships, and extract nuanced insights from connected data.
This completes your journey through Neo4j's query capabilities. From basic pattern matching to aggregations, from multi-hop traversals to shortest paths, and now to complex pattern combinations, you've built a comprehensive toolkit for working with graph databases. These skills form the foundation for analyzing social networks, building recommendation engines, detecting fraud patterns, and solving countless other real-world problems with connected data.
Now it's your turn to put these concepts into practice! The upcoming exercises will challenge you to write your own multi-pattern queries, combine different types of relationships, and optimize complex graph analyses. Show what you've learned by crafting queries that reveal the hidden connections in graph data!
Note: Most query outputs shown throughout this lesson (such as results with Alice, Bob, Carol, etc.) are illustrative examples designed to demonstrate concepts. When working through the practice exercises, your actual query results will reflect the specific data in the practice database.
