6
votes

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.

3
Do you want to minimize distance or the number of transfers?Nicole White
I want to get all possible routes with bus interchange info alonng with total dstancce of each path.Mahtab Alam

3 Answers

13
votes

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:

model

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
0
votes

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

0
votes

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.