Neo4j shortestPath with highest aggregated relationship propertie - neo4j

I want to find the shortest path between two nodes. The path itself is not the problem... The bigger problem is, that I´ll want to return the path, where the aggregated relationship property on the path is highest.
For better understanding, here´s what I want:
This is my query
MATCH
(startNode:Person {id:"887111"}),
(endNode:Person {id:"789321"}),
paths = allShortestPaths((startNode)-[r:KNOWS *..20]-(endNode))
RETURN paths
In this example I´ll want to have the path from Elissa (id: 887111) to Kasey (id: 789321) where the aggregated count ON the relationship is MAX.
I´ve also had a look at 'shortestPath', which only gives me one path. The other way is to call the 'dijkstra'-algo, with this I´ll get only the path with the lowest 'cost' (and not the highest).
So in my example the only path which should shown up is Elissa->Travon->Kasey
I´d think, the problem isn´t that complex, but at the moment I´m gettin stucked with this..
Thanks so far in advance.
UPDATE
after calling the suggested query
MATCH (startNode:Person {id:"789321"}), (endNode:Person {id:"887111"})
CALL apoc.algo.dijkstra(startNode, endNode, 'KNOWS', '_duration') YIELD path, weight
RETURN path, -weight AS weight
my result is the following

[UPDATED]
I present 2 answers, depending one what you are trying to do.
1. Finding path with max total weight
To find the path with max total weight, you can feed to the Dijkstra algorithm the negation of the original weight properties. The resulting "lowest" total weight will be a negative value that, when negated, will actually be the highest total weight (based on the original weight properties).
There is an APOC procedure, apoc.algo.dijkstra that implements the Dijkstra algorithm, but it does not allow you to use the negative value of the specified weight property. So, to use that procedure, you would need to add a new property to each KNOWS relationship with the appropriate negative value. For example, to add the negative weights to existing relationships (assuming w is the original weight property, and _w will contain the corresponding negative value):
MATCH ()-[k:KNOWS]->()
SET k._w = -k.w;
Once you have the negative weights, the following should give you the path with the max weight:
MATCH (startNode:Person {id:"887111"}), (endNode:Person {id:"789321"})
CALL apoc.algo.dijkstra(startNode, endNode, 'KNOWS', '_w') YIELD path, weight
RETURN path, -weight AS weight;
2. Choosing from the shortest paths the one with maximum total weight
MATCH
(startNode:Person {id:"887111"}),
(endNode:Person {id:"789321"}),
path = allShortestPaths((startNode)-[:KNOWS *..20]-(endNode))
RETURN path, REDUCE(s = 0, r IN RELATIONSHIPS(path) | s + r.duration) AS weight
ORDER BY weight DESC
LIMIT 1;

Related

Get the shortest path from certain node to ANY node of a given label in Neo4J

I have a node, let's call it X. I wanna find the closest node to X, let's name it Y and Y has to have a certain label. If there are multiple such Y nodes at the same distance to X, I'd like them all to be returned
Suppose we have nodes A and B of a certain label. The minimum path length from X to A is 3 and from X to B is 5. I want it to return A and only A. If the minimum path length is equal, I'd like it to return both of them (both A and B)
Here's what I have so far :
MATCH p=shortestPath((selectedNode {name:'X'})-[*]-(y:GivenLabel))
WITH y.name as y, length(p)=min(length(p)) AS l
RETURN y
The problem of this query is that it returns both A and B in the above example, no matter what the minimum path to each one of them is. I thought about using LIMIT 1 and ordering them but then it'll only display one of them, even if the minimum path length to each one of them is equal
Thanks in advance!
The collect function will let you return a single row containing a list of values.
MATCH p=shortestPath((selectedNode {name:'X'})-[*]-(y:GivenLabel))
RETURN length(p) AS l, collect(y.name) as targets
ORDER BY l
LIMIT 1
If you want to return the values as individual records, instead of a list, you can use UWIND.
MATCH p=shortestPath((selectedNode {name:'X'})-[*]-(y:GivenLabel))
WITH length(p) AS l, collect(y.name) as targets
ORDER BY l
LIMIT 1
UNWIND targets as target
RETURN l, target
If you have APOC Procedures, you can use one of the path expander procs to find a shortest path to a given label and limit it. Unfortunately you'll need a second call to get multiple nodes of the label at the same distance away.
// assume we've already MATCHed to selectedNode
...
CALL apoc.path.expandConfig(selectedNode, {labelFilter:'/GivenLabel', limit:1}) YIELD path
WITH selectedNode, length(path) as pathLength
CALL apoc.path.subgraphNodes(selectedNode, {labelFilter:'/GivenLabel', maxLevel:pathLength}) YIELD node
RETURN node
In the label filter, the prefix of / indicates that the label will be used in a termination filter, once it finds the first occurrence it will stop expanding, and use the node as a result.
The path expander procs use breadth-first expansion by default, so it will be the shortest path from your starting node to a node of the given label.
limit:1 ensures that we return after finding the first result (this can be very expensive if there is no such node of the given label, or if it's far away, so you might consider providing a maxLevel as an upper bound).
We make a similar path expander call (subgraphNodes(), since we no longer need the path, just the node at the end) using the length of the path found previously as our maxLevel, that will return all nodes with the given label at that distance.

Compute the distances between two nodes and their lowest common ancestor (LCA)

I need to compute the distance that separate two nodes A and B with their lowest common ancestor in a graph. I use the followinf function to find LCA:
match p1 = (A:Category {idCat: "Main_topic") -[*0..]-> (common:Category) <-[*0..]- (B:Category {idCat: "Heat_transfer"})
return common, p1
Is there any function in Neo4j that allows to return the respective distance between d(A,common) and d(B, common).
Thank you fo your help
If I understand the lowest common ancestor correctly, this comes down to finding the shortest path between A and B with at least one node in between. That you can do using this query. Here the condition that the length of p is larger than 1 forces at least one node between the two. Below example uses the IMDB toy database and returns the movie Avatar.
match p=shortestPath((n:Person {name:'Zoe Saldana'})-[r*1..15]-(n1:Person {name:'James Cameron'})) where length(p) > 1 return nodes(p)[1]
Basically you can choose any element from the nodes in the path, except the first and last one (since those will be A and B)

Shortest Paths with Cost Property

I want to look up the top 5 (shortest) path in my graph (Neo4j 3.0.4) from point A to point Z.
The graph consists several nodes that are connected by the relation "CONNECTED_BY". This connection has a cost property that should be minimized.
I started with this:
MATCH p=(from:Stop{stopId:'A'}), (to:Stop{stopUri:'Z'}),
path = allShortestPaths((from)-[:CONNECTED_TO*]->(to))
WITH REDUCE (total = 0, r in relationships(p) | total + r.cost) as tt, path
RETURN path, tt
This query returns always the subgraph with the least hops, the cost property is not considered. There exists another subgraph with more hops that has a lower total cost. What I am doing wrong?
Furthermore, I acutally want to get the TOP 5 subgraphs. If I execute this query:
MATCH p=(from:Stop{stopUri:'A'})-[r:CONNECTED_TO*10]->(to:Stop{stopUri:'Z'}) RETURN p
I can see several paths, but the first one just returns one path.
The path should not contain loops etc. of course.
I want to execute this query via REST API, so a REST Call or cyhper query should do it.
EDIT1:
I want to execute this as REST Call, so I tried the dijkstra algorithm. This seems to be a good way, but I have to calculate the weight by adding 3 different cost properties in the relation. How this could be achieved?
allShortestPaths will find the shortest path between two points and then match every path that has the same number of hops. If you want to minimize based on cost rather than traversal length, try something like this:
MATCH p=(from:Stop{stopId:'A'}), (to:Stop{stopUri:'Z'}),
path = (from)-[:CONNECTED_TO*]->(to)
WITH REDUCE (total = 0, r in relationships(p) | total + r.cost) as cost, path
ORDER BY cost
RETURN path LIMIT 5

how can i get Shortest Path cypher query?

I created a Neo4j database with the cypher statement here:https://gist.github.com/neoecos/8748091
i want to know : how to get :
1.less Transfer Paths (order by transfer)
2.the Shortest Path (order by path length)
3.the optimal Path (less Transfer and the Shortest Path)
please give the corresponding query.
And do you think that is the best way to create a Bus inquiry system?
Thanks a lot.
The shortest path is pretty easy:
MATCH path=shortestPath((station_44:STATION {id:44})-[*0..10]-(station_46:STATION {id:46}))
RETURN path
As far as counting transfers you can do something like this:
MATCH path=allShortestPaths((station_44:STATION {id:44})-[rels*0..10]-(station_46:STATION {id:46}))
RETURN length(path) AS stop_count, length(FILTER(index IN RANGE(1, length(rels)-1) WHERE (rels[index]).bus <> (rels[index - 1]).bus)) AS transfer_count
Once you have those two variables you can calculate / sort however you like. For example:
MATCH path=(station_44:STATION {id:44})-[rels*0..4]-(station_46:STATION {id:46})
WITH length(path) AS stop_count, length(FILTER(index IN RANGE(1, length(rels)-1) WHERE (rels[index]).bus <> (rels[index - 1]).bus)) AS transfer_count
RETURN stop_count, transfer_count
ORDER BY (stop_count * 0.5) + (transfer_count * 2.0) DESC
Here I removed the allShortestPaths call so that you get different lengths of paths. The ORDER BY uses weights on the two metrics. Unfortunately, at least in my DB, if you go beyond a path length of four it starts to get really slow. You might be able to improve that by introducing a direction arrow in the path, if that makes sense in your case.

traversing based on path weights and node properties dijkstra

Can a least cost path between nodes A and B be found with also checking for some properties on the nodes?
For example, I want to find the shortest bus route from A to B with weight of the links being the distance. So the path should not only be least cost but also the nodes on the path should have a bus station / stop.
So the path returned be least cost path with all nodes as bus stops (say a property bus_stop=yes) .
The TraversalDescription provides evaluator where the node properties can be checked. Is similar possible with Dijkstra ?
When using Neo4j's graphalgo package, you need to supply a custom CostEvaluator to the Dijkstra algorithm. Instead of just using the travel time (aka the relationship property) you should return the sum of travel time (relationship property) plus waiting time at the bus stop (node property):
class BusCostEvaluator implements CostEvaluator<Float> {
public Float getCost(Relationship r, Direction d) {
return (Float)r.getStartNode().getProperty("waitTime", 0f) +
(Float)r.getProperty("travelTime", 0f)
}
}
Try the below query. It assumes all links have a property weight and sums the path weights and length combination to order the result. It firstly filters only those paths where all nodes have bus_stop as yes
MATCH p=a-[r*]->b
WHERE all(x1 in nodes(p) where x1.bus_stop = 'yes')
WITH extract(link IN r| link.weight) AS wts, length(p) AS len
RETURN wts, reduce(sum=0, x IN wts| sum+x) AS totalwt, len
order by totalwt

Resources