问题
I'm looking for a hint to solve the following issue:
Given a Neo4j DB with two node types applied as labels
:Address and :Milestone (one for each node)
Between the different nodes there are directed :LEADS_TO relationships.
Given the following pattern (I skipped the [ ] for the relations):
Pattern 1:
(a:Address)->(:Milestone)->(:Milestone)<-(:Milestone)<-(:Milestone)<-(b:Address)
Pattern 2:
(a:Address)->(:Milestone)->(c:Address)<-(:Milestone)<-(b:Address)
I'm looking for a query that only includes "in-between" nodes of the same type within the shortestPath analysis. In my case it should only include nodes with the label :Milestone and skips shorter paths that have a node labeled :Address somewhere in-between.
In other words: the query should match Pattern 1 and not Pattern 2 even if it is shorter.
I found the following solution which comes close but fails in my case because it covers the start and end node label as well and due to this selects no rows at all:
MATCH p=shortestPath( (a:Address)-[:LEADS_TO*..10]-(b:Address) )
WHERE a.name = 'XYZ'
AND b.name = 'ABC'
AND ALL(x IN nodes(p) WHERE (x:MILESTONE))
RETURN p
My initial idea was to create a first hop:
For the Start node
MATCH (a:Address)-[:LEADS_TO]->(aa:Milestone)
and similar for the end node and start the query from there.
But this is problematic because every :Address node can have multiple :LEADS_TO relationships. Querying the shortest path for a defined start and end node this approach would create n start nodes and m end nodes just for one query that could be executed with a distinct start and end node.
Does anyone have an idea how to exclude the start and end node from the WHERE clause within Cypher?
Any hint is greatly appreciated.
回答1:
Try this, you can use a subset of a collection with a slice operator
MATCH p=shortestPath( (a:Address)-[:LEADS_TO*..10]-(b:Address) )
WHERE a.name = 'XYZ'
AND b.name = 'ABC'
AND ALL(x IN nodes(p)[1..-1] WHERE (x:MILESTONE))
RETURN p
回答2:
You can use a WHERE ALL
clause based on the relationship type and the nodes not being start or end, for eg:
MATCH p=shortestPath( (a:Address)-[:LEADS_TO*..10]-(b:Address) )
WHERE a.name = 'XYZ'
AND b.name = 'ABC'
AND ALL( x IN range( 1, size(nodes(p)-2) )
WHERE 'Milestone' IN labels(nodes(p)[x]) )
回答3:
thanks for the help. It worked with a slight modification. I had to add two more parenthesis (around the size method) to assure that the size calculation within the returned nodes is correct.
MATCH p=shortestPath( (a:Address)-[:LEADS_TO*..10]-(b:Address) )
WHERE a.name = 'XYZ'
AND b.name = 'ABC'
AND ALL( x IN range( 1, (size(nodes(p))-2) )
WHERE 'Milestone' IN labels(nodes(p)[x]) )
Definitely faster than my approach with the initial match.
回答4:
What about using filter() like this:
MATCH p=shortestPath( (a:Address)-[:LEADS_TO*..10]-(b:Address) )
WHERE a.name = 'XYZ'
AND b.name = 'ABC'
RETURN FILTER(x IN nodes(p) WHERE 'Milestone' IN labels(x))
It will return you all Milestone nodes.
来源:https://stackoverflow.com/questions/37865457/neo4j-shortest-path-for-specific-node-labels-in-between-the-path