I have three Person nodes with various relationships. Each node has a latitude and longitude.
First, I find the combinations of pairs of nodes:
MATCH (p1: Person)-[]->(p2: Person)
RETURN p1.name, p2.name
My output is correct:
Next, I attempt to find the distances between the pairs of nodes:
MATCH (p1:Person)-[]->(p2:Person)
WITH point({longitude: p1.longitude, latitude: p1.latitude}) AS p1Point,
point({longitude: p2.longitude, latitude: p2.latitude}) AS p2Point
RETURN (distance(p1Point, p2Point)) AS distance
My output is here:
Finally, I want to put it all together. I want to list the names of each node pair and the associated distance between them:
MATCH (p1:Person)-[]->(p2:Person)
WITH point({longitude: p1.longitude, latitude: p1.latitude}) AS p1Point,
point({longitude: p2.longitude, latitude: p2.latitude}) AS p2Point
RETURN p1.name, p2.name, (distance(p1Point, p2Point)) AS distance
I get an error that p1 is not defined.
What is the problem here? There is similar syntax described here:
https://neo4j.com/docs/graph-algorithms/current/labs-algorithms/all-pairs-shortest-path/
The WITH clause unbinds all variables except for the ones it carries forward.
This should work:
MATCH (p1:Person)-->(p2:Person)
WITH p1, p2,
point({longitude: p1.longitude, latitude: p1.latitude}) AS p1Point,
point({longitude: p2.longitude, latitude: p2.latitude}) AS p2Point
RETURN p1.name, p2.name, distance(p1Point, p2Point) AS distance
This should also work (since WITH is not really needed):
MATCH (p1:Person)-->(p2:Person)
RETURN p1.name, p2.name,
distance(
point({longitude: p1.longitude, latitude: p1.latitude}),
point({longitude: p2.longitude, latitude: p2.latitude})) AS distance
[UPDATED]
By the way, with an always-bidirectional relationship like RELATED_TO, you should just use a single undirected relationship instead of two directed relationships pointing in opposite directions. Note, though, that the CREATE clause only supports creating directed relationships, so just pick any arbitrary direction -- it does not matter which. Later, when you do a search, just use an undirected search (like MATCH (p1:Person)--(p2:Person) ...). Also, you should look into whether you should use MERGE instead, since it does allow an undirected relationship pattern.
Related
I have a graph with structure (:Person)-[:KNOW]->(:Person).
Now a Person node has a latitude and longitude. One Person is connected to 10 other Persons.
For each Person, I want to keep the relationships to only 5 closest Persons. Since the graph is very large, I'm thinking about using apoc.periodic.iterate.
Here is what I have now, but I don't know how to Delete the relationships for the last 5 Persons:
CALL apoc.periodic.iterate("MATCH (n:Person) RETURN n",
"WITH n
MATCH (n)-[r:KNOW]->(m:Person)
WITH point({longitude: TOFLOAT(n.long), latitude: TOFLOAT(n.lat)}) AS p1, point({longitude: TOFLOAT(m.long), latitude: TOFLOAT(m.lat)}) AS p2, r
WITH point.distance(p1, p2) AS Distance, r ORDER BY Distance",
{batchSize:10000, parallel:false})
Could you suggest a solution?
After you sort (ORDER BY) by distance, you can collect the relationships then get the 6th to the last item of the list. Then you can remove the farthest nodes to n.
CALL apoc.periodic.iterate("MATCH (n:Person) RETURN n",
"WITH n
MATCH (n)-[r:KNOW]->(m:Person)
WITH point({longitude: TOFLOAT(n.long), latitude: TOFLOAT(n.lat)}) AS p1, point({longitude: TOFLOAT(m.long), latitude: TOFLOAT(m.lat)}) AS p2, r, n
WITH point.distance(p1, p2) AS Distance, n, r ORDER BY n, Distance
WITH n, collect(r)[5..] as farthest_dist
FOREACH (farthest_r in farthest_dist|DELETE farthest_r)",
{batchSize:10000, parallel:false})
I added n on the sorting because batch_size is 10000 so there are 10k n persons per batch. This notation collect(r)[5..] means put all relationships in a list and give me the 6th item, 7th item, up to the max item. You can also use UNWIND() at the last statement instead of FOREACH(), if you like.
UNWIND farthest_dist as farthest_r
DELETE farthest_r
Before you remove the relationships, I would suggest that you backup your database first so that you can restore your data when needed.
My task is to calculate the total length of roads from city. I'm using OSM data. After importing it to the database I have the following structure (This seemed logic to me but I can change if you think there is a better way):
There is a root node for each rode segment (way tag in OSM XML) that holds an ID and a type (I have other types as well but they are irrelevant now)
To the root node is connected to the first node of the road with a relation 'defines'
Every node is connected to the next one with a relation called 'connected' that has a property 'entity_id' which is the root nodes id. (One node can appear in more road segments, for example intersections, so I'm trying to avoid circles with this property.
I'm pretty new to Neo4J. I only have experience in SQL databases but based on that I feel like even if my approach was working, it would loose the advantage of the query language (referring to speed).
So here is what I have so far, but it is not even close. It outputs the same number (wrong number) a lot of times instead of one total. I'm pretty sure I did not get the whole idea of with but can't figure out what would be the solution:
CREATE (t:Tmp {total:0})
with t
MATCH (e:Entity {type:'road'})
with collect(e) as es, t
unwind es as entity
match p = ()-[r:connected {entity_id:entity.int_id}]->()
with entity, p,t
SET entity.lng = 0
with entity, p, t
unwind nodes(p) as nd
with t,nd,point({longitude:toFloat(nd.lon), latitude: toFloat(nd.lat)}) as point1, entity
SET entity.lng = entity.lng + distance(entity.p, point1)
with t,nd,point({longitude:toFloat(nd.lon), latitude: toFloat(nd.lat)}) as point1, entity
SET entity.p = point1
with entity, t
SET t.total = t.total + entity.lng
return t.total
Your query is returning the current t.total result per node, instead of an overall total value. And it seems to be incorrectly calculating a distance for the first node in a segment (the first node should have a 0 distance). It is also very inefficient. For example, it does not bother to leverage the defines relationship. In a neo4j query, it is vitally important to use the power of relationships to avoid scanning through a lot of irrelevant data.
In addition, there is no mention of a particular "city". Your query is for all Entity nodes. If your DB only contains Entity nodes for a single city, then that is OK. Otherwise, you will need to modify the query to only match Entity nodes for a specific city.
The following query may do what you want (going simply by what I gleaned from your question, and assuming your DB only has data for a single city), using the defines relationship to efficiently match the start node for each Entity segment, and using that start node to efficiently find the connected nodes of interest:
MATCH (entity:Entity {type:'road'})-[:defines]->(start)
MATCH p=(start)-[:connected* {entity_id:entity.id}]->(end)
WHERE NOT EXISTS((end)-[:connected {entity_id:entity.id}]->())
SET entity.lng = 0
SET entity.p = point({longitude:toFloat(start.lon), latitude: toFloat(start.lat)})
WITH entity, p
UNWIND TAIL(NODES(p)) AS nd
WITH point({longitude:toFloat(nd.lon), latitude: toFloat(nd.lat)}) as pt, entity
SET entity.lng = entity.lng + distance(entity.p, pt)
SET entity.p = pt
RETURN SUM(entity.lng) AS total
The aggregating function SUM() is used to return the total lng for all entities. The t node is not needed. The WHERE clause only matches paths that comprise complete segments. This query also initializes entity.p to the point of the start node, and UNWINDS the nodes after the start node.
If there are many Entity nodes with type values other than "road", you may also want to create an index on :Entity(type).
short version: I need to get a path that can contain different relationships in different directions. However, I have a constraint on my path where if it contains successive relationships of a particular type, then both relationship must be in the same direction.
long version:
I am using the query below to get a path between two nodes:
MATCH p=shortestPath((n:Class { code: '1' })-[r*]-(m:Class { code: '4'})) WHERE NONE(x IN NODES(p) WHERE 'Ontology' in labels(x)) return p
The query correctly returns me a shortest path between the two nodes. However I need to further constraint this query so that it returns only path where successive relationship of a particular type are in the same direction.
For example, suppose the relationship -a-> need to be in the same direction, it should not return (1)-a->(2)<-a-(3)-b->(4) but can return (1)-a->(6)-a->(3)-b->(7)<-c-(5)<-d-(6)-e->(4) or (3)-b->(7)<-c-(4) .
The above examples were just a simplification of my real data. In my real use case, I need to find a shortest path between a node with IRI
http://elite.polito.it/ontologies/dogont.owl#Actuator and another node with IRI http://elite.polito.it/ontologies/dogont.owl#StateValue. The query below is a specific query that encodes the path I need and it returns a path, that is the path exist. I need to make it more generic using shortestpath.
MATCH p=(n:Class {iri: 'http://elite.polito.it/ontologies/dogont.owl#Actuator'})-->(a:Class)<--(b:ObjectProperty{iri:'http://elite.polito.it/ontologies/dogont.owl#hasState'})-->(c:Class{iri:'http://elite.polito.it/ontologies/dogont.owl#State'})<--(d:Class{iri:'http://elite.polito.it/ontologies/dogont.owl#hasStateValue'})-->(e:Class{iri:'http://elite.polito.it/ontologies/dogont.owl#StateValue'}) return p
Is this possible with cypher ?
This query should work if you want to capture paths that are consistent in either direction (but it has to invoke shortestPath() twice):
MATCH (n:Class {code: '1'}), (m:Class {iri: '4'})
OPTIONAL MATCH p1=shortestPath((n)-[*]->(m))
WHERE NONE(x IN NODES(p1) WHERE 'Ontology' in labels(x))
OPTIONAL MATCH p2=shortestPath((n)<-[*]-(m))
WHERE NONE(y IN NODES(p2) WHERE 'Ontology' in labels(y))
RETURN p1, p2
p1 and/or p2 will be null if there is no consistently rightward or leftward path, respectively.
However, if you know that you want a specific direction (say, rightward), then this should work:
MATCH p=shortestPath((:Class {code: '1'})-[*]->(:Class {iri: '4'}))
WHERE NONE(x IN NODES(p) WHERE 'Ontology' in labels(x))
RETURN p
I have Cities, Roads and Transporters in my database.
A Road is connected with a From and To relationship to two (different) Cities. Each road has also a property distance (in kilometers).
Multiple Transporters could have a relationship to Roads. Every Transporter has a price (per kilometer).
Now my question. I want the cheapest option to get a packet from city A to B. There could be a direct road or else we have to go via other cities and transporters. And I want explicitly use the Dijkstra algorithm for this.
Can this query be done in Cypher? And if not, how can it be done using the Neo4J Java API?
Based on your sample dataset, I think there is a modelisation problem that makes maybe things difficult, certainly for matching on directed relationships.
However this is already how you can find the lowest cost path :
MATCH (a:City { name:'CityA' }),(d:City { name:'CityD' })
MATCH p=(a)-[*]-(d)
WITH p, filter(x IN nodes(p)
WHERE 'Road' IN labels(x)) AS roads
WITH p, reduce(dist = 0, x IN roads | dist + x.distance) AS totalDistance
RETURN p, totalDistance
ORDER BY totalDistance
LIMIT 5
I am very new to Neo4j. Could someone kindly explain to me (step-by-step please) how I could implement the Dijkstra's algorithm to find the shortest path between two nodes? Is it possible to simply do it using Cypher?
I already tried the shortestPath algorithm, but it is very slow:
MATCH (from: Location {LocationName:"x"}), (to: Location {LocationName:"y"}) , path = (from)-[:CONNECTED_TO*1..5]->(to)
RETURN path AS shortestPath,
reduce(distance = 0, r in relationships(path)| distance+toInt(r.Distance))
AS totalDistance
ORDER BY totalDistance ASC
LIMIT 1
my nodes are: Location with properties LocationID and LocationName
my relationships are: CONNECTED_TO with property Distance
I have more than 6000 relationships
please note in the code above that I have limited to 1..5
when I do not define this limit, the query does not give any results (keeps on executing)
thanks
Yes it is possible with Cypher or with a dedicated endpoint of the Neo4j ReST API.
BTW, the examples from the Cypher Neo4j documentation are self explaining :
http://neo4j.com/docs/milestone/query-match.html#_shortest_path
To get the shortestPath between two nodes :
MATCH (from: Location {LocationName:"x"}), (to: Location {LocationName:"y"}) ,
path = shortestPath((from)-[:CONNECTED_TO*]->(to))
RETURN path
If you want to get all the shortest
MATCH (from: Location {LocationName:"x"}), (to: Location {LocationName:"y"}) ,
paths = allShortestPaths((from)-[:CONNECTED_TO*]->(to))
RETURN paths
If you want to order by the length (number of hops) of the paths in descending order :
MATCH (from: Location {LocationName:"x"}), (to: Location {LocationName:"y"}) ,
paths = allShortestPaths((from)-[:CONNECTED_TO*]->(to))
RETURN paths
ORDER BY length(paths) DESC
If you to to get the shortestPath + the sum of the relationship distance property :
MATCH (from: Location {LocationName:"x"}), (to: Location {LocationName:"y"}) ,
path = shortestPath((from)-[:CONNECTED_TO*]->(to))
WITH REDUCE(dist = 0, rel in rels(path) | dist + rel.distance) AS distance, p
RETURN p, distance
Another way to perform the query:
install "APOC" in neo4j
https://neo4j-contrib.github.io/neo4j-apoc-procedures/#utilities
MATCH (start:node {name:'First_node_name'}), (end:node {name: 'end_node_name'})
CALL apoc.algo.dijkstra(start, end, 'weight_property',1.0) YIELD path, weight
return path, weight
P.S.
'weight_property' this is PropertyName which must have your relationship (weight_property=225454)
returned two values:
path - this is the path that was found;
weight - summary number of weight that was colculated from each relationship