## 12.2. Expand paths

This section describes a procedure that can be used to expand the paths of variable length path traversals.

The expand paths procedure is the most basic of the path expanders. This procedure enables path traversals based on relationship filters and node filters. See Section 12.3, “Expand paths with config” if more control is required over the traversal.

This section includes the following:

- Section 12.2.1, “Procedure Overview”
- Section 12.2.2, “Parameter Syntax”
- Section 12.2.3, “Examples”

### 12.2.1. Procedure Overview

The procedure is described below:

type | qualified name | signature | description
--- | --- | --- | ---

procedure

`apoc.path.expand`

`apoc.path.expand(start :: ANY?, relationshipFilter :: STRING?, labelFilter :: STRING?, minLevel :: INTEGER?, maxLevel :: INTEGER?) :: (path :: PATH?)`

apoc.path.expand(startNode <id>|Node|list, 'TYPE|TYPE_OUT>|<TYPE_IN', '+YesLabel|-NoLabel', minLevel, maxLevel ) yield path - expand from start node following the given relationships from min to max-level adhering to the label filters

### 12.2.2. Parameter Syntax

This procedure takes the following parameters:

- `start` - a list of nodes or node ids
- `relationshipFilter` - the relationship types to be expanded
- `labelFilter` - the node labels to be expanded
- `minLevel` - the minimum number of hops in our traversal
- `maxLevel` - the maximum number of hops in our traversal

Relationship Filters

The syntax for relationship filters is described below:

Syntax: `[<]RELATIONSHIP_TYPE1[>]|[<]RELATIONSHIP_TYPE2[>]|…​`

input | type | direction
--- | --- | ---
`LIKES>` | `LIKES` | OUTGOING
`<FOLLOWS` | `FOLLOWS` | INCOMING
`KNOWS` | `KNOWS` | BOTH
`>` | `any type` | OUTGOING
`<` | `any type` | INCOMING

Label Filters

The syntax for label filters is described below:

Syntax: `[+-/>]LABEL1|LABEL2|*|…​`

input | result
--- | ---
`-Foe` | blacklist filter - No node in the path will have a label in the blacklist.
`+Friend` | whitelist filter - All nodes in the path must have a label in the whitelist (exempting termination and end nodes, if using those filters). If no whitelist operator is present, all labels are considered whitelisted.
`/Friend` | termination filter - Only return paths up to a node of the given labels, and stop further expansion beyond it. Termination nodes do not have to respect the whitelist. Termination filtering takes precedence over end node filtering.
`>Friend` | end node filter - Only return paths up to a node of the given labels, but continue expansion to match on end nodes beyond it. End nodes do not have to respect the whitelist to be returned, but expansion beyond them is only allowed if the node has a label in the whitelist.

### 12.2.3. Examples

The examples in this section are based on the following sample graph:

    MERGE (mark:Person:DevRel {name: "Mark"})
    MERGE (praveena:Person:Engineering {name: "Praveena"})
    MERGE (joe:Person:Field {name: "Joe"})
    MERGE (lju:Person:DevRel {name: "Lju"})
    MERGE (zhen:Person:Engineering {name: "Zhen"})
    MERGE (stefan:Person:Field {name: "Stefan"})
    MERGE (alicia:Person:Product {name: "Alicia"})
    MERGE (martin:Person:Engineering {name: "Martin"})
    MERGE (jake:Person:Product {name: "Jake"})

    MERGE (zhen)-[:KNOWS]-(stefan)
    MERGE (zhen)-[:KNOWS]-(lju)
    MERGE (zhen)-[:KNOWS]-(praveena)
    MERGE (zhen)-[:KNOWS]-(martin)
    MERGE (mark)-[:KNOWS]-(jake)
    MERGE (alicia)-[:KNOWS]-(jake)

    MERGE (alicia)-[:FOLLOWS]->(joe)
    MERGE (joe)-[:FOLLOWS]->(mark)
    MERGE (joe)-[:FOLLOWS]->(praveena)
    MERGE (joe)-[:FOLLOWS]->(zhen)
    MERGE (mark)-[:FOLLOWS]->(stefan)
    MERGE (stefan)-[:FOLLOWS]->(joe)
    MERGE (praveena)-[:FOLLOWS]->(joe)

The Neo4j Browser visualization below shows the sample graph:

The `KNOWS` relationship type is considered to be bidirectional, where if Zhen knows Stefan, we can imply that Stefan knows Zhen. When using the `KNOWS` relationship we will ignore the direction.

The `FOLLOWS` relationship has a direction, so we will specify a direction when we use it.

Let’s start by expanding paths from the Praveena node. We only want to consider the `KNOWS` relationship type, so we’ll specify that as the relationship filter.

The following returns the paths to people that Praveena `KNOWS` from 1 to 2 hops.

    MATCH (p:Person {name: "Praveena"})
    CALL apoc.path.expand(p, "KNOWS", null, 1, 2)
    YIELD path
    RETURN path, length(path) AS hops
    ORDER BY hops;

Table 12.1. Results
path | hops
--- | ---
(:Person:Engineering {name: "Praveena"})←[:KNOWS]-(:Person:Engineering {name: "Zhen"}) | 1
(:Person:Engineering {name: "Praveena"})←[:KNOWS]-(:Person:Engineering {name: "Zhen"})-[:KNOWS]→(:Person:Engineering {name: "Martin"}) | 2
(:Person:Engineering {name: "Praveena"})←[:KNOWS]-(:Person:Engineering {name: "Zhen"})-[:KNOWS]→(:Person:DevRel {name: "Lju"}) | 2
(:Person:Engineering {name: "Praveena"})←[:KNOWS]-(:Person:Engineering {name: "Zhen"})-[:KNOWS]→(:Person:Field {name: "Stefan"}) | 2

Praveena only has a direct `KNOWS` relationship to Zhen, but Zhen has `KNOWS` relationships to 3 other people, which means they’re 2 hops away from Praveena.

We can also provide a node label filter to restrict the nodes that are returned. The following query only returns paths where every node has the `Engineering` label.

The following returns paths containing only `Engineering` people that Praveena `KNOWS` from 1 to 2 hops.

    MATCH (p:Person {name: "Praveena"})
    CALL apoc.path.expand(p, "KNOWS", "+Engineering", 1, 2)
    YIELD path
    RETURN path, length(path) AS hops
    ORDER BY hops;

Table 12.2. Results
path | hops
--- | ---
(:Person:Engineering {name: "Praveena"})←[:KNOWS]-(:Person:Engineering {name: "Zhen"}) | 1
(:Person:Engineering {name: "Praveena"})←[:KNOWS]-(:Person:Engineering {name: "Zhen"})-[:KNOWS]→(:Person:Engineering {name: "Martin"}) | 2

We lose the paths that ended with Lju and Stefan because neither of those nodes had the `Engineering` label.

We can specify multiple relationship types. The following query starts from the Alicia node, and then expands the `FOLLOWS` and `KNOWS` relationships:

The following returns paths containing people that Alicia `FOLLOWS` or `KNOWS` from 1 to 3 hops.

    MATCH (p:Person {name: "Alicia"})
    CALL apoc.path.expand(p, "FOLLOWS>|KNOWS", "", 1, 3)
    YIELD path
    RETURN path, length(path) AS hops
    ORDER BY hops;

Table 12.3. Results
path | hops
--- | ---
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"}) | 1
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Sales {name: "Jonny"}) | 1
(:Person:Product {name: "Alicia"})-[:KNOWS]→(:Person:Product {name: "Jake"}) | 1
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Zhen"}) | 2
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Praveena"}) | 2
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:DevRel {name: "Mark"}) | 2
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Sales {name: "Jonny"})-[:KNOWS]→(:Person:Sales {name: "Anthony"}) | 2
(:Person:Product {name: "Alicia"})-[:KNOWS]→(:Person:Product {name: "Jake"})←[:KNOWS]-(:Person:DevRel {name: "Mark"}) | 2
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Zhen"})-[:FOLLOWS]→(:Person:Product {name: "John"}) | 3
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Zhen"})-[:KNOWS]→(:Person:Engineering {name: "Martin"}) | 3
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Zhen"})-[:KNOWS]→(:Person:Engineering {name: "Praveena"}) | 3
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Zhen"})-[:KNOWS]→(:Person:DevRel {name: "Lju"}) | 3
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Zhen"})-[:KNOWS]→(:Person:Field {name: "Stefan"}) | 3
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Praveena"})-[:FOLLOWS]→(:Person:Field {name: "Joe"}) | 3
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Praveena"})←[:KNOWS]-(:Person:Engineering {name: "Zhen"}) | 3
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:DevRel {name: "Mark"})-[:FOLLOWS]→(:Person:Field {name: "Stefan"}) | 3
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:DevRel {name: "Mark"})-[:KNOWS]→(:Person:Product {name: "Jake"}) | 3
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Sales {name: "Jonny"})-[:KNOWS]→(:Person:Sales {name: "Anthony"})-[:FOLLOWS]→(:Person:Field {name: "Joe"}) | 3
(:Person:Product {name: "Alicia"})-[:KNOWS]→(:Person:Product {name: "Jake"})←[:KNOWS]-(:Person:DevRel {name: "Mark"})-[:FOLLOWS]→(:Person:Field {name: "Stefan"}) | 3

This query returns 19 paths, Alicia is very well connected!

We can also specify traversal termination criteria using label filters. If we wanted to terminate a traversal as soon as the traversal encounters a node containing the `Engineering` label, we can use the `/Engineering` node filter.

The following returns paths containing people that Alicia `FOLLOWS` or `KNOWS` from 1 to 3 hops, terminating as soon as a node with the `Engineering` label is reached.

    MATCH (p:Person {name: "Alicia"})
    CALL apoc.path.expand(p, "FOLLOWS>|KNOWS", "/Engineering", 1, 3)
    YIELD path
    RETURN path, length(path) AS hops
    ORDER BY hops;

Table 12.4. Results
path | hops
--- | ---
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Zhen"}) | 2
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Praveena"}) | 2

We’re now down to only two paths. But this query doesn’t capture all of the paths from Alicia that end in a node with the `Engineering` label. We can use the `>Engineering` node filter to define a traversal that:

- only returns paths that terminate at nodes with the `Engineering` label
- continues expansion to end nodes after that, looking for more paths that end with the `Engineering` label

The following returns paths containing people that Alicia `FOLLOWS` or `KNOWS` from 1 to 3 hops, where paths end with a node with the `Engineering` label.

    MATCH (p:Person {name: "Alicia"})
    CALL apoc.path.expand(p, "FOLLOWS>|KNOWS", ">Engineering", 1, 3)
    YIELD path
    RETURN path, length(path) AS hops
    ORDER BY hops;

Table 12.5. Results
path | hops
--- | ---
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Zhen"}) | 2
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Praveena"}) | 2
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Zhen"})-[:KNOWS]→(:Person:Engineering {name: "Martin"}) | 3
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Zhen"})-[:KNOWS]→(:Person:Engineering {name: "Praveena"}) | 3
(:Person:Product {name: "Alicia"})-[:FOLLOWS]→(:Person:Field {name: "Joe"})-[:FOLLOWS]→(:Person:Engineering {name: "Praveena"})←[:KNOWS]-(:Person:Engineering {name: "Zhen"}) | 3

Our query now also returns paths going through Praveena and Zhen, one going to Martin, and other others going back to Zhen and Praveena!