## 12.6. Expand a spanning tree

This section describes a procedure that finds a spanning tree that starts from a set of start nodes.

Expands a spanning tree reachable from start node following relationships to max-level adhering to the label filters. The paths returned collectively form a spanning tree.

This procedure has the same behaviour as Section 12.3, “Expand paths with config” with the config `uniqueness: "NODE_GLOBAL"`.

- Section 12.6.1, “Procedure Overview”
- Section 12.6.2, “Configuration parameters”

- Section 12.6.3, “Examples”

  - Section 12.6.3.1, “Relationship Type and Node Label filters”
  - Section 12.6.3.2, “Terminator Nodes and End Nodes”
  - Section 12.6.3.3, “Whitelist Nodes and Blacklist Nodes”
  - Section 12.6.3.4, “Sequences of relationship types”

### 12.6.1. Procedure Overview

The procedure is described below:

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

procedure

`apoc.path.spanningTree`

`apoc.path.spanningTree(start :: ANY?, config :: MAP?) :: (path :: PATH?)`

apoc.path.spanningTree(startNode <id>|Node|list, {maxLevel,relationshipFilter,labelFilter,bfs:true, filterStartNode:false, limit:-1, optional:false, endNodes:[], terminatorNodes:[], sequence, beginSequenceAtStart:true}) yield path - expand a spanning tree reachable from start node following relationships to max-level adhering to the label filters

### 12.6.2. Configuration parameters

The procedures support the following config parameters:

Table 12.42. Config parameters
name | type | default | description
--- | --- | --- | ---
minLevel | Long | -1 | the minimum number of hops in the traversal. Must be 0 or 1 if specified
maxLevel | Long | -1 | the maximum number of hops in the traversal
relationshipFilter | String | null | the relationship types and directions to traverse. See Section 12.5.2.1, “Relationship Filters”.
labelFilter | String | null | the node labels to traverse. See Section 12.5.2.2, “Label Filters”.
beginSequenceAtStart | Boolean | true | starts matching sequences of node labels and/or relationship types (defined in `relationshipFilter`, `labelFilter`, or `sequences`) one node away from the start node.
bfs | Boolean | true | use Breadth First Search when traversing. Uses Depth First Search if set to `false`
filterStartNode | Boolean | false | whether the `labelFilter` and `sequence` apply to the start node of the expansion.
limit | Long | -1 | limit the number of paths returned. When using `bfs:true`, this has the effect of returning paths to the `n` nearest nodes with labels in the termination or end node filter, where `n` is the limit given. If set to `true`, a `null` value is yielded whenever the expansion would normally eliminate rows due to no results.
endNodes | List<Node> | null | only these nodes can end returned paths, and expansion will continue past these nodes, if possible.
terminatorNodes | List<Node> | null | Only these nodes can end returned paths, and expansion won’t continue past these nodes.
whiteListNodes | List<Node> | null | Only these nodes are allowed in the expansion (though endNodes and terminatorNodes will also be allowed, if present).
blackListNodes | List<Node> | null | None of the paths returned will include these nodes.

It also has the following fixed parameter:

Table 12.43. Config parameters
name | type | default | description
--- | --- | --- | ---
uniqueness | String | NODE_GLOBAL | the strategy to use when expanding relationships in a traversal. `NODE_GLOBAL` means that a node cannot be traversed more than once. This is what the legacy traversal framework does.

#### 12.6.2.1. 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

#### 12.6.2.2. 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.

Label filter operator precedence and behavior

Multiple label filter operators are allowed at the same time. Take the following example:

`labelFilter:'+Person|Movie|-SciFi|>Western|/Romance'`

If we work through this label filter, we can see that:

- `:Person` and `:Movie` labels are whitelisted
- `:SciFi` is blacklisted
- `:Western` is an end node label
- `:Romance` is as a termination label.

The precedence of operator evaluation isn’t dependent upon their location in the labelFilter but is fixed:

Blacklist filter `-`, termination filter `/`, end node filter `>`, whitelist filter `+`.

This means:

- No blacklisted label `-` will ever be present in the nodes of paths returned, even if the same label (or another label of a node with a blacklisted label) is included in another filter list.
- If the termination filter `/` or end node filter `>` is used, then only paths up to nodes with those labels will be returned as results. These end nodes are exempt from the whitelist filter.
- If a node is a termination node `/`, no further expansion beyond the node will occur.
- The whitelist only applies to nodes up to but not including end nodes from the termination or end node filters. If no end node or termination node operators are present, then the whitelist applies to all nodes of the path.
- If no whitelist operators are present in the labelFilter, this is treated as if all labels are whitelisted.

### 12.6.3. Examples

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

    MERGE (mark:Person:DevRel {name: "Mark"})
    MERGE (lju:Person:DevRel {name: "Lju"})
    MERGE (praveena:Person:Engineering {name: "Praveena"})
    MERGE (zhen:Person:Engineering {name: "Zhen"})
    MERGE (martin:Person:Engineering {name: "Martin"})
    MERGE (joe:Person:Field {name: "Joe"})
    MERGE (stefan:Person:Field {name: "Stefan"})
    MERGE (alicia:Person:Product {name: "Alicia"})
    MERGE (jake:Person:Product {name: "Jake"})
    MERGE (john:Person:Product {name: "John"})
    MERGE (jonny:Person:Sales {name: "Jonny"})
    MERGE (anthony:Person:Sales {name: "Anthony"})
    MERGE (rik:Person:Sales {name: "Rik"})

    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 (jonny)-[:KNOWS]-(anthony)
    MERGE (john)-[:KNOWS]-(rik)

    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)
    MERGE (lju)-[:FOLLOWS]->(jake)
    MERGE (alicia)-[:FOLLOWS]->(jonny)
    MERGE (zhen)-[:FOLLOWS]->(john)
    MERGE (anthony)-[:FOLLOWS]->(joe)

The Neo4j Browser visualization below shows the sample graph:

Figure 12.19. 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.

#### 12.6.3.1. Relationship Type and Node Label filters

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 `relationshipFilter` parameter.

The following returns the spanning tree starting from Praveena and traversing the `KNOWS` relationship type for 1 to 2 hops.

    MATCH (p:Person {name: "Praveena"})
    CALL apoc.path.spanningTree(p, {
    	relationshipFilter: "KNOWS",
        minLevel: 1,
        maxLevel: 2
    })
    YIELD path
    RETURN path;

We can see a Neo4j Browser visualization of the spanning tree in Figure 12.20, “Spanning tree from Praveena”.

Figure 12.20. Spanning tree from Praveena

The spanning tree contains 4 nodes apart from Praveena. Praveena only has a direct `KNOWS` relationship to Zhen, but Zhen has `KNOWS` relationships to 3 other people, which means they’re also included in the spanning tree.

We can also provide a node label filter to restrict the nodes that are returned. If we want to only return paths where every node has the `Engineering` label, we’ll provide the value `+Engineering` to the `labelFilter` parameter.

The following returns the spanning tree starting from Praveena and traversing the `KNOWS` relationship type for 1 to 2 hops, only includin `Engineering` nodes.

    MATCH (p:Person {name: "Praveena"})
    CALL apoc.path.spanningTree(p, {
    	relationshipFilter: "KNOWS",
    	labelFilter: "+Engineering",
        minLevel: 1,
        maxLevel: 2
    })
    YIELD path
    RETURN path;

We can see a Neo4j Browser visualization of the spanning tree in Figure 12.21, “Spanning tree from Praveena to engineering nodes”.

Figure 12.21. Spanning tree from Praveena to engineering nodes

We lose Lju and Stefan from the spanning tree 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 the spanning tree starting from Alicia and traversing the `FOLLOWS` or `KNOWS` relationship type for 1 to 3 hops.

    MATCH (p:Person {name: "Alicia"})
    CALL apoc.path.spanningTree(p, {
        relationshipFilter: "FOLLOWS>|KNOWS",
        minLevel: 1,
        maxLevel: 3
    })
    YIELD path
    RETURN path;

We can see a Neo4j Browser visualization of the spanning tree in Figure 12.22, “Spanning tree from Alicia”.

Figure 12.22. Spanning tree from Alicia

This query returns paths to 11 of the 12 people in the graph, which indicates that 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 the spanning tree starting from Alicia and traversing the `FOLLOWS` or `KNOWS` relationship type for 1 to 3 hops, terminating as soon as a node with the `Engineering` label is reached.

    MATCH (p:Person {name: "Alicia"})
    CALL apoc.path.spanningTree(p, {
        relationshipFilter: "FOLLOWS>|KNOWS",
        labelFilter: "/Engineering",
        minLevel: 1,
        maxLevel: 3
    })
    YIELD path
    RETURN path;

We can see a Neo4j Browser visualization of the spanning tree in Figure 12.23, “Spanning tree from Alicia terminating at `Engineering` nodes”.

Figure 12.23. Spanning tree from Alicia terminating at `Engineering` nodes

Our spanning tree has been reduced to only 3 other nodes apart from Alicia. But this query doesn’t capture the complete spanning tree from Alicia containing nodes 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 the spanning tree starting from Alicia and traversing the `FOLLOWS` or `KNOWS` relationship type for 1 to 3 hops, where paths end with a node with the `Engineering` label.

    MATCH (p:Person {name: "Alicia"})
    CALL apoc.path.spanningTree(p, {
        relationshipFilter: "FOLLOWS>|KNOWS",
        labelFilter: ">Engineering",
        minLevel: 1,
        maxLevel: 3
    })
    YIELD path
    RETURN path;

We can see a Neo4j Browser visualization of the spanning tree in Figure 12.24, “Spanning tree from Alicia to `Engineering` nodes”.

Figure 12.24. Spanning tree from Alicia to `Engineering` nodes

The spanning tree now also reaches Martin, via a relationship from Zhen.

#### 12.6.3.2. Terminator Nodes and End Nodes

As well as specifying terminator and end labels for traversals, we can also specify terminator and end nodes.

Let’s build on the previous query that found people that Alicia `KNOWS` or `FOLLOWS`. We want the returned spanning tree to stop as soon as the Mark, Joe, Zhen, or Praveena nodes are reached. We can do that by passing those nodes to the `terminatorNodes` parameter.

The following returns the spanning tree of people that Alicia `FOLLOWS` or `KNOWS` from 1 to 3 hops, terminating as soon as Mark, Joe, Zhen, or Rik nodes are reached.

    MATCH (p:Person {name: "Alicia"})
    MATCH (terminator:Person)
    WHERE terminator.name IN ["Mark", "Joe", "Zhen", "Rik"]
    WITH p, collect(terminator) AS terminatorNodes
    CALL apoc.path.spanningTree(p, {
        relationshipFilter: "FOLLOWS>|KNOWS",
        minLevel: 1,
        maxLevel: 3,
        terminatorNodes: terminatorNodes
    })
    YIELD path
    RETURN path;

We can see a Neo4j Browser visualization of the spanning tree in Figure 12.25, “Spanning tree from Alicia, terminating at Mark, Joe, Zhen, or Rik”.

Figure 12.25. Spanning tree from Alicia, terminating at Mark, Joe, Zhen, or Rik

Mark and Joe are included in the spanning tree, but Rik and Zhen can’t be reached. This could be because there is no path to Zhen and Rik that doesn’t go through Mark and Joe, or it could mean that there’s no path based on the other traversal criteria.

We can find out whether Mark, Joe, Zhen, or Rik are reachable by passing these nodes to the `endNodes` parameter.

The following returns the spanning tree of people that Alicia `FOLLOWS` or `KNOWS` from 1 to 3 hops, ending as soon as Mark, Joe, Zhen, or Rik nodes are reached.

    MATCH (p:Person {name: "Alicia"})
    MATCH (end:Person)
    WHERE end.name IN ["Mark", "Joe", "Zhen", "Rik"]
    WITH p, collect(end) AS endNodes
    CALL apoc.path.spanningTree(p, {
        relationshipFilter: "FOLLOWS>|KNOWS",
        minLevel: 1,
        maxLevel: 3,
        endNodes: endNodes
    })
    YIELD path
    RETURN path;

We can see a Neo4j Browser visualization of the returned spanning tree in Figure 12.26, “Spanning tree from Alicia, ending at Mark, Joe, Zhen, or Rik”.

Figure 12.26. Spanning tree from Alicia, ending at Mark, Joe, Zhen, or Rik

Our spanning tree now includes Joe, Mark, and Zhen, but Rik is still unreachable.

#### 12.6.3.3. Whitelist Nodes and Blacklist Nodes

Whitelist and blacklist nodes can also be specified.

Let’s build on the previous query that found people that Alicia `KNOWS` or `FOLLOWS`. We want any returned paths to only include the nodes Mark, Joe, Zhen, and Praveena, which we can do by passing these nodes to the parameter `whitelistNodes`.

The following returns the spanning tree reachable by the `FOLLOWS` or `KNOWS` relationship types at 1 to 3 hops from Alicia, where the paths to those nodes must only include Mark, Jonny, or Zhen.

    MATCH (p:Person {name: "Alicia"})
    MATCH (whitelist:Person)
    WHERE whitelist.name IN ["Jonny", "Mark", "Zhen"]
    WITH p, collect(whitelist) AS whitelistNodes
    CALL apoc.path.spanningTree(p, {
        relationshipFilter: "FOLLOWS>|KNOWS",
        minLevel: 1,
        maxLevel: 3,
        whitelistNodes: whitelistNodes
    })
    YIELD path
    RETURN path;

We can see a Neo4j Browser visualization of the returned spanning tree in Figure 12.27, “Spanning Tree from Alicia where paths to nodes include Mark, Jonny, or Zhen”.

Figure 12.27. Spanning Tree from Alicia where paths to nodes include Mark, Jonny, or Zhen

Only Jonny can be reached. We can therefore infer that Mark and Zhen are only reachable via another node that wasn’t include in the whitelist.

A blacklist is used to exclude nodes from the paths that lead to reachable nodes. If we want to return nodes that are reachable without going through Joe, we can do this by passing the Joe node to the `blacklistNodes` parameter.

The following returns the spanning tree reachable by the `FOLLOWS` or `KNOWS` relationship types at 1 to 3 hops from Alicia, where the paths to those nodes do not go through Joe.

    MATCH (p:Person {name: "Alicia"})
    MATCH (joe:Person {name: "Joe"})
    CALL apoc.path.spanningTree(p, {
        relationshipFilter: "FOLLOWS>|KNOWS",
        minLevel: 1,
        maxLevel: 3,
        blacklistNodes: [joe]
    })
    YIELD path
    RETURN path;

We can see a Neo4j Browser visualization of the returned spanning tree in Figure 12.28, “Spanning tree from Alicia where paths to nodes can’t go via Joe”.

Figure 12.28. Spanning tree from Alicia where paths to nodes can’t go via Joe

#### 12.6.3.4. Sequences of relationship types

Sequences of relationship types can be specified by comma separating the values passed to `relationshipFilter`.

For example, if we want to start from the Joe node and traverse a sequence of the `FOLLOWS` relationship in the outgoing direction and the `KNOWS` relationship in either direction, we can specify the relationship filter `FOLLOWS>,KNOWS`.

The following returns the reachable nodes by following the `FOLLOWS` and `KNOWS` relationship types alternately from Joe.

    MATCH (p:Person {name: "Joe"})
    CALL apoc.path.spanningTree(p, {
    	relationshipFilter: "FOLLOWS>,KNOWS",
    	beginSequenceAtStart: true,
    	minLevel: 1,
    	maxLevel: 4
    })
    YIELD path
    RETURN path;

We can see a Neo4j Browser visualization of the returned spanning tree in Figure 12.29, “Spanning tree from Joe via alternate `FOLLOWS` and `KNOWS` relationship types”.

Figure 12.29. Spanning tree from Joe via alternate `FOLLOWS` and `KNOWS` relationship types