My question is I have one graph with lot of nodes representing stops of buses.
How should I include bus information like which buses available between nodes.
I am thinking of creating a buses relationship between nodes that will have info of all the buses between two nodes and a relationship property marking distance between two stops.
buses[500A,182A],distance:500m buses[121B,542W,222A,111Z],distance:400m
Like A------------------------------------------------------->B----------------------------------------------------------->C
So how I will find out the bus or busses( If no direct path is available) to reach M from A?
First I will find out the path (a neo4j query) , how to reach M from A .
Say my path is
buses[11A],distance:1000m buses[11A],distance:250m buses[13B,100A],distance:2000m
A----------------------------------------->L----------------------------------->N------------------------------------------->M
The problem is I how to programmatically check whether a direct bus to M is available or not , or I how to interchange the bus in between.
According to above scenario I can go A to N through 11A then from N to M by taking either 13B or 100A.
I have to do that programmatically.
I want to retrieve all possible paths between two stations and total distance of a path along with bus information.
Your model needs to be more graphy-y. That is, I don't think you should have an array property on the relationship between Stop nodes with the bus information. Rather, the buses should be nodes themselves with a relationship to indicate which stops they stop at. Consider the following example data:
CREATE (a:Stop {name:'A'}),
(b:Stop {name:'B'}),
(c:Stop {name:'C'}),
(d:Stop {name:'D'}),
(a)-[:NEXT {distance:1}]->(b),
(b)-[:NEXT {distance:2}]->(c),
(c)-[:NEXT {distance:3}]->(d),
(b1:Bus {id:1}),
(b2:Bus {id:2}),
(b3:Bus {id:3}),
(b1)-[:STOPS_AT]->(a),
(b1)-[:STOPS_AT]->(b),
(b2)-[:STOPS_AT]->(a),
(b2)-[:STOPS_AT]->(b),
(b2)-[:STOPS_AT]->(c),
(b3)-[:STOPS_AT]->(b),
(b3)-[:STOPS_AT]->(c),
(b3)-[:STOPS_AT]->(d);
The graph now looks like this:
With this model, it's easy to find an itinerary that minimizes the number of transfers and also returns all the necessary transfer information, if applicable. For example, all shortest itineraries (shortest in terms of # of transfers) from A to D:
MATCH (a:Stop {name:'A'}), (d:Stop {name:'D'})
MATCH p = allShortestPaths((a)-[:STOPS_AT*]-(d))
RETURN EXTRACT(x IN NODES(p) | CASE WHEN x:Stop THEN 'Stop ' + x.name
WHEN x:Bus THEN 'Bus ' + x.id
ELSE '' END) AS itinerary
Three routes were found, which all have one transfer:
Stop A, Bus 2, Stop C, Bus 3, Stop D
Stop A, Bus 1, Stop B, Bus 3, Stop D
Stop A, Bus 2, Stop B, Bus 3, Stop D
Of course, you can return this information however you want with the EXTRACT()
function.
Another example. Find an itinerary from A to C:
MATCH (a:Stop {name:'A'}), (c:Stop {name:'C'})
MATCH p = allShortestPaths((a)-[:STOPS_AT*]-(c))
RETURN EXTRACT(x IN NODES(p) | CASE WHEN x:Stop THEN 'Stop ' + x.name
WHEN x:Bus THEN 'Bus ' + x.id
ELSE '' END)
One route was found, and there are no transfers:
Stop A, Bus 2, Stop C
Please let me know if this answers your question.
EDIT: To get the distance:
MATCH (a:Stop {name:'A'}), (d:Stop {name:'D'})
MATCH route = allShortestPaths((a)-[:STOPS_AT*]-(d)),
stops = (a)-[:NEXT*]->(d)
RETURN EXTRACT(x IN NODES(route) | CASE WHEN x:Stop THEN 'Stop ' + x.name
WHEN x:Bus THEN 'Bus ' + x.id
ELSE '' END) AS itinerary,
REDUCE(d = 0, x IN RELATIONSHIPS(stops) | d + x.distance) AS distance
itinerary distance
Stop A, Bus 1, Stop B, Bus 3, Stop D 6
Stop A, Bus 2, Stop B, Bus 3, Stop D 6
Stop A, Bus 2, Stop C, Bus 3, Stop D 6
Since you can create multiple relationships between the same nodes, I would suggest creating a relationship for each bus. So from your example:
A<-----------------B<-------------------------------C
buses[180Q,171B] buses[80A,43B,121S]
You might do something like:
<somehow MATCH on A and B>
CREATE B-[:connects_to {bus: '180Q'}]->A
CREATE B-[:connects_to {bus: '171B'}]->A
and so on...
That way you can do
MATCH path=(start {id: '123'})-[:connects_to {bus: '180Q'}*1..10]-(end: {id: '321'})
UNWIND relationships(path) AS hop
WITH path, hop
WITH path, collect(DISTINCT hop.bus) AS busses
WHERE length(busses) <= 2
RETURN path
Honestly I've never used both a relationship property match at the same time as a variable length specification, but I imagine it would work
I tried quite of cypher queries but can not get what I am looking for, the bus interchange info. Until now my query is.
MATCH (from:Stop { name:"A" }), (to:Stop { name: "S"}) , path = (from)-[:CONNECTED*]->(to)
unwind relationships(path) as hop
RETURN extract(n IN nodes(path)| n.name) AS Shortest_Route,collect(hop.Buses) as Buses,length(path) as Stop_Count,
reduce(distance = 0, r in relationships(path) | distance+r.distance) AS Shortest_Distance
ORDER BY Shortest_Distance ASC
LIMIT 1 .
I am finding it hard to get bus interchange information.I think I have to do it programatically.It does not look very complex , but I was thinking If I can get it from cypher query itself.