traversing based on path weights and node properties dijkstra - neo4j

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

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)

Find a set of (n) nodes where relationship weight between each pair of node is greater than a value(w)

I have a database where each node is connected to all other nodes with a relationship, and each relationship has a weight. I need a query where given a weight w and a number of nodes n, I want all n nodes where each pair of relationship has a weight greater than w.
Any help on this would be great
It depends on what you would like your result set to look like. Something as simple as this query would return all paths that fall under your criteria:
MATCH p=()-[r:my_rel]->() WHERE r.weight > w RETURN p;
This would return all such paths.
If you would like the two nodes only (and not the entire pattern's results), you can return only those two nodes:
MATCH (n1)-[r:my_rel]->(n2) WHERE r.weight > w RETURN n1,n2;
Do note that due to Neo4J's storage internals, performing a search based on the properties of a relationship tends to not perform as well as those based on properties of a node.

Neo4j shortestPath with highest aggregated relationship propertie

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;

Neo4j: Customize Path Traversal

I am pretty new to Neo4j. I have implemented an example use case with the following setup:
acyclic directed graph
nodes have a property called externalID
Nodes:
Node Type S (Start Node)
Node Type E (End Node)
Node Type I (Intermediate Node)
Relations:
Node Type S can only have outgoing relations to Nodes of Type I
Node Type I can have ingoing relations from I and S
Node Type I can have outgoing relations to I and E
Node Type E can only have incomming relations from I
All relations have a weight property assigned which can be any number
With the help of stackoverflow and several tutorials I was able to formulate a Cypher query which gets me all paths from any start node with one externalID to the matching end node with the same externalID.
MATCH p=(a:S)-[r*]->(b:E)
WHERE a.externalID=b.externalID
WITH p, relationships(p) as rcoll
RETURN p
The query works more or less good so far ...
However, I have no idea how to change the behavior on how the graph is scanned for possible paths. Actually I only need a subset of all possible paths. Such paths fulfill the following requirement:
The path traversal is started at a Start Node S with a given capacity C.
if a relationship is traversed the weight property of this relationship is subtracted from the current capacity C (that means negative weights are added)
if the capacity gets negative the path up to this point is invalid (the path up to the previous node is still valid and may continue with other relationships)
if the capacity is still positive continue with another relationship from this point and use the result of C - weight as new C
Can I somehow adjust the query or is there any other possibility with Neo4j to get all paths using the strategy above?
Thanks a lot for your help in advance.
This Cypher query might be suitable for your use case:
MATCH p = (a:S)-[r*]->(b:E)
WHERE a.externalID = b.externalID
WITH
p,
REDUCE(c = a.capacity, r IN RELATIONSHIPS(p) |
CASE WHEN c < 0 THEN -1 ELSE c - r.weight END) AS residual
WHERE residual >= 0
RETURN p;
The REDUCE clause will set residual to a negative value if the capacity is ever reduced below 0, even if subsequent weights would normally cause it to go positive.

Resources