cancel
Showing results for 
Search instead for 
Did you mean: 

Head's Up! These forums are read-only. All users and content have migrated. Please join us at community.neo4j.com.

How to optionally extract a part of the result path if a curtain node came on the path in cypher query result?

For example:

Situation 1:

(a) - [:CONNECTED] -> (z)

result 1:
(a) - (b) - (c) - (d) - (stop) - (e) - (f) - (z)

Now as the stop node came between the result, I want the following result:
(a) - (b) - (c) - (d) - (stop)

Situation 2:

(a) - [:CONNECTED] -> (x)

result:
(a) - (b) - (c) - (d) - (e) - (f) - (x)

Now as the stop node is not present between the result, I want the whole result:
(a) - (b) - (c) - (d) - (e) - (f) - (x)

Can one one help me on this?

Thank you

1 ACCEPTED SOLUTION

daveb
Node Link

Here are a few queries to get you going on your solution.

This is the simplest one. Look for a path, p, that starts at 'A' and ends at a node with either 'Z' or 'stop'. Then return only the shortest of all of the matches if there are multiple. The theory here is that if the 'stop' is encountered first it will stop there.

MATCH p=(a:Node {name: 'A'}) - [:CONNECTED*] -> (end:Node)
WHERE end.name in ['stop','Z']
RETURN p
ORDER BY length(p)
LIMIT 1

Here is a another pure cypher example. This one finds the path from A..Z first and then looks for a stop node afterwards. If there is/are stop node(s) it collects them and then uses the first one to only return from the beginning on the path through the stop node.

MATCH p=(a:Node {name: 'A'})-[:CONNECTED*]->(z:Node {name: 'Z'})
UNWIND range(1,length(p)) AS idx
WITH p, collect(CASE WHEN (nodes(p)[idx-1]).name = 'stop' THEN idx END) AS stops_found
RETURN nodes(p)[0..stops_found[0]]

Here is an APOC example. This example uses apoc.path.expand. It findsthe A and Z nodes first and then it finds a collection of stopper nodes. It then passes these into apoc.path.expandConfig and follows CONNECTED relationships and Node labels until it reaches either a stopper node or Z.

MATCH (a:Node {name: 'A'}), (z:Node {name: 'Z'})
WITH a, z
MATCH (stop:Node {name: 'stop'}) 
WITH a, z, collect(stop) AS stoppers
CALL apoc.path.expandConfig(a, {relationshipFilter:'CONNECTED>', labelFilter:'+Node', terminatorNodes: stoppers + [z], uniqueness: 'NODE_GLOBAL'}) YIELD path
RETURN path

Here is another APOC example. This is similar, but instead of "stop" being an attribute value on a node it uses a termination filter on a Stop label. It does not look for the end node Z but will continue on matching connections provided no Stop label is encountered.

MATCH (a:Node {name: 'A'})
CALL apoc.path.expandConfig(a, {relationshipFilter:'CONNECTED>', labelFilter:'+Node|/Stop', uniqueness: 'NODE_GLOBAL'}) YIELD path
RETURN path

View solution in original post

2 REPLIES 2

daveb
Node Link

Here are a few queries to get you going on your solution.

This is the simplest one. Look for a path, p, that starts at 'A' and ends at a node with either 'Z' or 'stop'. Then return only the shortest of all of the matches if there are multiple. The theory here is that if the 'stop' is encountered first it will stop there.

MATCH p=(a:Node {name: 'A'}) - [:CONNECTED*] -> (end:Node)
WHERE end.name in ['stop','Z']
RETURN p
ORDER BY length(p)
LIMIT 1

Here is a another pure cypher example. This one finds the path from A..Z first and then looks for a stop node afterwards. If there is/are stop node(s) it collects them and then uses the first one to only return from the beginning on the path through the stop node.

MATCH p=(a:Node {name: 'A'})-[:CONNECTED*]->(z:Node {name: 'Z'})
UNWIND range(1,length(p)) AS idx
WITH p, collect(CASE WHEN (nodes(p)[idx-1]).name = 'stop' THEN idx END) AS stops_found
RETURN nodes(p)[0..stops_found[0]]

Here is an APOC example. This example uses apoc.path.expand. It findsthe A and Z nodes first and then it finds a collection of stopper nodes. It then passes these into apoc.path.expandConfig and follows CONNECTED relationships and Node labels until it reaches either a stopper node or Z.

MATCH (a:Node {name: 'A'}), (z:Node {name: 'Z'})
WITH a, z
MATCH (stop:Node {name: 'stop'}) 
WITH a, z, collect(stop) AS stoppers
CALL apoc.path.expandConfig(a, {relationshipFilter:'CONNECTED>', labelFilter:'+Node', terminatorNodes: stoppers + [z], uniqueness: 'NODE_GLOBAL'}) YIELD path
RETURN path

Here is another APOC example. This is similar, but instead of "stop" being an attribute value on a node it uses a termination filter on a Stop label. It does not look for the end node Z but will continue on matching connections provided no Stop label is encountered.

MATCH (a:Node {name: 'A'})
CALL apoc.path.expandConfig(a, {relationshipFilter:'CONNECTED>', labelFilter:'+Node|/Stop', uniqueness: 'NODE_GLOBAL'}) YIELD path
RETURN path

Thank you daved for the solution and for considering different use-cases.

Nodes 2022
Nodes
NODES 2022, Neo4j Online Education Summit

All the sessions of the conference are now available online