Cypher allShortestPaths just return one path? - graph

Background statement:
I have a graph like bellow:
I want to find all the path between Node A and Node F (something like how many ways I can reach F from A), then my Cypher like this bellow:
MATCH (start:kg:test), (end:kg:test), p = allShortestPaths((start)-[*..8]-(end))
where start.value = 'A' and end.value = 'F'
RETURN start, end, p
As I expected, this query will return the whole graph, but it just returns A->F (return the same thing with using the shortestPath function), like bellow:
Problems
Why that query won't return all the different paths in the graph?
Do I misuse the allShortestPaths function?
How can I get all the path from Node A to Node F?
thanks

shortestPath() returns the single shortest path between the nodes (and if there are multiple of the same size it just returns the first that it finds).
If there are multiple paths that could have been returned by shortestPath() (they will all have the same size), then allShortesPaths() will return them.
If you just want to find all possible paths between two nodes (the length of the path doesn't matter, and you don't care about shortest paths at all), then you don't need to use either of these functions.
MATCH p=(start:kg:test)-[*..8]-(end:kg:test)
where start.value = 'A' and end.value = 'F'
RETURN start, end, p

Related

Enumerating k number of paths passing through a node in Neo4j

Currently, I am using the random walk algorithm in Neo4j to do some path computations. The random walk algorithm has the option of specifying source nodes from which the walks can be constructed. However, I am wondering if there is a way to enumerate k number of paths passing through a certain node than making it a source node and combining the paths? Here k is a parameter, say, 2 paths or 3 paths.
If I'm reading your question correctly you want to find k random paths of length i that pass through node n and you don't want to combine the paths of two queries.
If so then the easiest way might be to find all paths containing n and take a random subset of that, i.e.:
MATCH (n) WHERE id(n) = $n_id // Find the node you want the paths to contain
path = MATCH (foo)-[:REL_TYPE*..i]->(bar) // Find all paths containing the node
WHERE n in nodes(path)
AND NOT (foo = n OR bar = n) // Paths should not start or end with n
WITH collect(path) AS paths // Put all the paths in a list
RETURN apoc.coll.randomItems(paths, $k) // Return a random subset of size k

Repeat in gremlin

Two queries related to gremlin are as follows:
Want to stop the traversal when a condition is satisfied during repeated condition check.
g.V().has('label_','A')).emit().repeat(inE().outV()).until(has('stop',1)).project('depth','values').by(valueMap('label_','stop'))
I want the query to stop returning further values when the stop is equal to 1 for the node encountered during the repeat statement. But the query doesn't stop and return all the records.
Output required:
=>{label_='A',stop=0}
=>{label_='B',stop=0}
=>{label_='C',stop=1}
Query to return traversal values in the following format considering if edge exists between them. Considering the graph as A->E1->B->E2->C. The output must be as follows
=> A,E1,B
=> B,E2,C
A, B, C, E1, E2 represents properties respectively where is the starting node
For the first part, it seems you traversing on the in edges and not on the out is this on purpose? if so replace the out() in the repeat to in
g.V().has(label, 'A').emit().
repeat(out()).until(has('stop', 1)).
project('label', 'stop').
by(label).
by(values('stop'))
example: https://gremlify.com/ma2xkkszkzr/1
for the second part, I'm still not sure what you meant if you just want to get all edges with their out and in you can use elementMap:
g.E().elementMap()
example: https://gremlify.com/ma2xkkszkzr/4
and if not supported you can maybe do something like this:
g.E().local(union(
outV(),
identity(),
inV()
).label().fold())
example: https://gremlify.com/ma2xkkszkzr/2

Zero or more length of path in Cypher

For example I have a path:
1-[:A]->2-[:B]->3
And we can use the * operator to define if a particular edge can be repeated. I would like to use the * operator on the entire path, or both edges combined. I would like to follow: (A AND B) zero or more times.
Example:
1-[:A]->2-[:B]->3-[:A]->4-[:B]->5...
I am not sure how to apply the * operator for the entire path in Cypher. My intent is to express a pattern that allows a specific path to be repeated 0 or more times.
This is something variable-length patterns cannot do in Cypher. However, because of this, we added repeating sequences functionality to path expander procs in APOC Procedures.
As an example:
MATCH (n)
WHERE id(n) = 123
CALL apoc.path.expandConfig(n, {relationshipFilter:'A>, B>'}) YIELD path
RETURN path
This expands from a start node (n) expanding out only a repeating sequence of outgoing :A and :B relationships. No minLevel or maxLevel properties were provided, so this has a minimum of 0 length and no bounds on max length.

How to find the shortest path with the minimum number of hops in Neo4j?

Im modeling a graph where nodes are places and edges indicate that you can go from one place to another.
This is to have all routes that you can take from a place to another,
and you can go from a place to another by different routes, so I want a query that returns me the shortest path with the minimum route changes.
For example, I want to go from A to D, I have two possible paths:
(place {name: "A"})-[:FOLLOWS{route:""R1}]->(place{name: "B" })-[:FOLLOWS{route:""R4}]->(place{name:"C"})-[:FOLLOWS{route:""R2}]->(place{name:"D"})
(place {name: "A"})-[:FOLLOWS{route:""R1}]->(place{name: "B" })-[:FOLLOWS{route:""R1}]->(place{name:"F"})-[:FOLLOWS{route:""R2}]->(place{name:"D"})
In the previous two path, both are the same size, but I would like to get the second one, which is the one who has the minimum route changes.
Thank you.
#DevBennett's answer gets the shortest path with the minimum number of route changes.
To get the shortest path with the minimum number of different routes, which is what the question literally asked for (but which may not have been what the asker actually wanted), you can use this query:
MATCH p=ALLSHORTESTPATHS((a:Place {name: "A"})-[:FOLLOWS*]->(d:Place{name:"D"}))
UNWIND RELATIONSHIPS(p) AS rel
WITH p, COUNT(DISTINCT rel.route) AS nRoutes
RETURN p, nRoutes
ORDER BY nRoutes
LIMIT 1;
Come on, you could not be happy with only 3 answers :)
MATCH (a:Place {name:"A"}), (d:Place {name:"D"})
MATCH p=allShortestPaths((a)-[*]->(d))
RETURN p,
size(filter(x in range(0, size(rels(p)))
WHERE (rels(p)[x]).route <> (rels(p)[x-1]).route)) + length(p) as score
ORDER BY score ASC
Something like this:
MATCH
(a:place {name: "A"}) WITH a
MATCH
(d:place {name: "D"}) WITH a,d
MATCH
p = allShortestPaths ( (a)-[:FOLLOWS*..100]->(d) )
WITH
nodes(p) as places, relationships(p) as paths
UNWIND
paths as path
WITH
places, paths, collect(distinct path.route) as segments
RETURN
places, paths, segments, size(segments) as cost
ORDER BY
cost ASC
LIMIT 1
I think this will meet your needs. It finds all of the shortest paths. Then it processes each one to count the number of route changes. It then orders the result set by the fewest route changes and limits the result set to the first occurrence.
// find all of the shortest paths that match the beginning and ending nodes
match p=allShortestPaths((a:Place {name: 'A'})-[:FOLLOWS*]->(d:Place {name: 'D'}))
// carry forward the path, the relationships in the path and a range that represents the second to last relationships in the path
with p,relationships(p) as rel, range(1,length(p)-1) as index
// use reduce to process the route attribute on the relationship to accumulate the changes
with p, rel, index, reduce (num_chg = 0, i in index | num_chg +
case when (rel[i]).route = (rel[i-1]).route then 0 else 1 end ) as changes
// return the path and the number of route changes
return p, changes
// only return the path with the fewest route change
order by changes
limit 1
The answers using "AllShortestPaths" are wrong.
What if the graph looks like the image below?
Example of Graph
Well, the result of the queries using AllShortestPaths function will be "A-E-D" and not "A-B-C-D" ...

Path properties in Cypher

I have a following graph in Neo4j
(id:5,t:e)<--(id:4,t:w)<--(id:0;t:s)-->(id:1,t:w)-->(id:2,t:b)-->(id:3,t:e)
now I search paths from nodes with t:s to nodes with t:e such that only white-listed nodes with t:w are in-between.
So ideally i need a query to return only (0)-->(4)-->(5) but not (0)-->(1)-->(2)-->(3).
EDIT: i have forgotten to mention that paths may have variable length: from 0 to potentially infinity. It means that I may have an arbitrary number of "t:w" nodes
Best regards
Working just with the information that you have provided above you could use
MATCH p=({t:'s'})-->({t:'w'})-->({t:'e'}) RETURN p
Of course if an s could link directly to an e you will need to use variable length relationships matches.
MATCH p=({t:'s'})-[*0..1]->({t:'w'})-[]->({t:'e'})
RETURN DISTINCT p
EDIT - Paths of any length
MATCH p=({t:'s'})-[*0..1]->({t:'w'})-[*]->({t:'e'})
RETURN DISTINCT p
To match a path of any length use the * operator in the relationship path match. It is usually best to put some bounds on that match, an example of which is the *0..1 (length 0 to 1). You can leave either end open *..6 (length 1 to 6) or *2.. (length 2 to whatever).
The problem with this is that now you cannot guarantee the node types in the intervening nodes (so t:"b" will be matched). To avoid that I think you'll have to filter.
MATCH p=({t:'s'})-[*]->({t:'e'})
WHERE ALL (node IN NODES(p)
WHERE node.t = 's' OR node.t = 'w' OR node.t = 'e' )
RETURN p
End Edit
You should introduce labels to your nodes and use relationship types for traversal though as that is where Neo/Cypher is going to be able to help you out. You should also make sure that if you are matching on properties that they are indexed correctly.

Resources