我开发了一个路由系统,通过使用多个移动性优惠来查找最佳路由,例如: G。公共交通工具或汽车共用。 基本上我有节点,代表公共汽车站,火车站等。这些站点包括每天的时间表。另外我有行人站。这些站点通过关系连接起来。如果我从行人变为公共汽车或公交线路之间,则该关系称为SWITCH_TO。如果我在不更改服务线的情况下驾驶多个停靠点,则停靠点通过名为CONNECTED_TO的关系连接。如果我想从A点到Z点,我必须在公交车站C换到公交车站D的另一条服务线,路径看起来像这样:
(A:Stop:Pedestrian)-[SWITCH_TO{switchTime:2}]->
(A:Stop:Bus{serviceLine:1})-[CONNECTED_TO{travelTime:5}]->
(B:Stop:Bus{serviceLine:1})-[CONNECTED_TO{travelTime:6}]->
(C:Stop:Bus{serviceLine:1})-[SWITCH_TO{switchTime:2}]->
(C:Stop:Pedestrian)-[CONNECTED_TO{travelTime:7}]->
(D:Stop:Pedestrian)-[SWITCH_TO{switchTime:2}]->
(D:Stop:Bus{serviceLine:2})-[CONNECTED_TO{travelTime:8}]->(Z:Stop:Bus{serviceLine:2})-[SWITCH_TO{switchTime:2}]->
(Z:Stop:Pedestrian)
我想基于用户的期望出发时间(或者期望的到达时间)计算完整的旅行时间并获得5个最佳连接(更少的时间)。 在上面的例子中,您可以看到SWITCH_TO关系的switchTime为2.这意味着我需要2分钟才能从当前位置切换到公共汽车站(例如,我必须查找它)。 CONNECTED_TO关系travelTimes是公交车需要从一站到另一站的时间段。 让我们假设我想在7:00开始。第一个开关需要2分钟。因此,如果在7:02之后出发,我必须查看(A:停止:公共汽车)的时间表。让我们假设下一次出发是在7:10。然后我要等8分钟。该等待时间不是固定值,而是每个特定请求的可变时间段。我从7点10分开始。我需要5 + 6 = 11分钟才能停下来(C:停止:公共汽车)和2分钟去公共汽车(切换到)。然后我必须步行7分钟。所以如果要检查服务项目2的时间表。如果在7:00 + 2 + 8 + 5 + 6 + 2 + 7 + 2 = 7:32之后有出发,请选择此项。如果下一次出发是在7:35,我将在7:00 + 2 + 8 + 5 + 6 + 2 + 7 + 2 + 3 + 8 + 2 = 7:45到达目的地。我知道,我有点复杂。 :)
我在这里准备了一个例子:
CREATE (newStop:Stop:Pedestrian {
stopName : 'A-Pedestrian',
mode : 'Pedestrian'
})
RETURN newStop;
CREATE (newStop:Stop:Bus {
stopName : 'A-Bus',
mode : 'Bus',
serviceLine : '1',
monday:[510,610,710,810,835,910],
tuesday:[510,610,710,810,835,910],
wednesday:[510,610,710,810,835,910],
thursday:[510,610,710,810,835,910],
friday:[510,610,710,810,835,910],
saturday:[510,610,710,810,835,910],
sunday:[510,610,710,810,835,910]
})
RETURN newStop;
CREATE (newStop:Stop:Bus {
stopName : 'B-Bus',
mode : 'Bus',
serviceLine : '1',
monday:[515,615,715,815,840,915],
tuesday:[515,615,715,815,840,915],
wednesday:[515,615,715,815,840,915],
thursday:[515,615,715,815,840,915],
friday:[515,615,715,815,840,915],
saturday:[515,615,715,815,840,915],
sunday:[515,615,715,815,840,915]
})
RETURN newStop;
CREATE (newStop:Stop:Bus {
stopName : 'C-Bus',
mode : 'Bus',
serviceLine : '1',
monday:[521,621,711,821,846,921],
tuesday:[521,621,711,821,846,921],
wednesday:[521,621,711,821,846,921],
thursday:[521,621,711,821,846,921],
friday:[521,621,711,821,846,921],
saturday:[521,621,711,821,846,921],
sunday:[521,621,711,821,846,921]
})
RETURN newStop;
CREATE (newStop:Stop:Pedestrian {
stopName : 'C-Pedestrian',
mode : 'Pedestrian'
})
RETURN newStop;
CREATE (newStop:Stop:Pedestrian {
stopName : 'D-Pedestrian',
mode : 'Pedestrian'
})
RETURN newStop;
CREATE (newStop:Stop:Bus {
stopName : 'D-Bus',
mode : 'Bus',
serviceLine : '2',
monday:[535,635,735,835,935],
tuesday:[535,635,735,835,935],
wednesday:[535,635,735,835,935],
thursday:[535,635,735,835,935],
friday:[535,635,735,835,935],
saturday:[535,635,735,835,935],
sunday:[]
})
RETURN newStop;
CREATE (newStop:Stop:Bus {
stopName : 'Z-Bus',
mode : 'Bus',
serviceLine : '2',
monday:[543,643,743,843,943],
tuesday:[543,643,743,843,943],
wednesday:[543,643,743,843,943],
thursday:[543,643,743,843,943],
friday:[543,643,743,843,943],
saturday:[543,643,743,843,943],
sunday:[]
})
RETURN newStop;
CREATE (newStop:Stop:Pedestrian {
stopName : 'Z-Pedestrian',
mode : 'Pedestrian'
})
RETURN newStop;
MATCH (s1:Stop), (s2:Stop)
WHERE s1.stopName = 'A-Pedestrian' AND s2.stopName = 'A-Bus'
CREATE
(s1)-[r:SWITCH_TO{ switchTime : 2 } ]->(s2)
RETURN s1, s2, r;
MATCH (s1:Stop), (s2:Stop)
WHERE s1.stopName = 'A-Bus' AND s2.stopName = 'B-Bus'
CREATE
(s1)-[r:CONNECTED_TO{ travelTime : 5 } ]->(s2)
RETURN s1, s2, r;
MATCH (s1:Stop), (s2:Stop)
WHERE s1.stopName = 'B-Bus' AND s2.stopName = 'C-Bus'
CREATE
(s1)-[r:CONNECTED_TO{ travelTime : 6 } ]->(s2)
RETURN s1, s2, r;
MATCH (s1:Stop), (s2:Stop)
WHERE s1.stopName = 'C-Bus' AND s2.stopName = 'C-Pedestrian'
CREATE
(s1)-[r:SWITCH_TO{ switchTime : 2 } ]->(s2)
RETURN s1, s2, r;
MATCH (s1:Stop), (s2:Stop)
WHERE s1.stopName = 'C-Pedestrian' AND s2.stopName = 'D-Pedestrian'
CREATE
(s1)-[r:CONNECTED_TO{ travelTime : 7 } ]->(s2)
RETURN s1, s2, r;
MATCH (s1:Stop), (s2:Stop)
WHERE s1.stopName = 'D-Pedestrian' AND s2.stopName = 'D-Bus'
CREATE
(s1)-[r:SWITCH_TO{ switchTime : 2 } ]->(s2)
RETURN s1, s2, r;
MATCH (s1:Stop), (s2:Stop)
WHERE s1.stopName = 'D-Bus' AND s2.stopName = 'Z-Bus'
CREATE
(s1)-[r:CONNECTED_TO{ travelTime : 8 } ]->(s2)
RETURN s1, s2, r;
MATCH (s1:Stop), (s2:Stop)
WHERE s1.stopName = 'Z-Bus' AND s2.stopName = 'Z-Pedestrian'
CREATE
(s1)-[r:SWITCH_TO{ switchTime : 2 } ]->(s2)
RETURN s1, s2, r;
正如您所看到的,在某些停靠点中,出发时间的int数组也可能为空(如果这些天没有提供连接)。行人站当然不包括时间表。 我的问题是:如何通过cypher进行此查询?我必须总结这些时间来选择正确的下一个出发时间。我必须知道我何时到达目的地。我想向用户展示最好的5个连接。有没有办法做到这一点?如果没有,有任何建议如何解决这个问题?
非常感谢! 斯蒂芬
Edit1:有没有办法在Java中开发它?在最简单的情况下,它可能只是一条最短的路径,但具有智能成本函数?而不是使用修复值使用函数来计算一个特定边的成本?任何帮助将不胜感激!
答案 0 :(得分:2)
[事先道歉,当我不看时,这变成了一部俄罗斯小说。而且它仍然只能让你获得最快速的单一路线,而不是最快的路线,但希望有人比我在这方面能有所改进更聪明。]
你正试图根据难以计算的成本做一些非常复杂的路径。你肯定需要重构一些,以便你可以更紧密地修复成本,然后应用apoc.algo.dijkstra
来获得一个可行的重量路径。要做到这一点,你需要从一般模型转变为某种事件“链”,由物理位置组织。过境模式与每周时间表相结合,将为您提供一些结构;在不同地点之间行走的能力只能适度地复杂化。在此过程中,我们最终将剥离一些不太相关和冗余的节点。让我们深入挖掘。
首先,我们需要能够将您的时间转换为机器可解析的东西。你可以使用apoc
将它们转换为isoformat
或类似的,但是对于需要频繁订购的周期性时间,并且只存在于分钟刻度,我说开始计算周日早上的午夜时刻为0分钟然后从那里算起来。所以,从星期日到午夜的分钟,基本上是你关注的时间,然后通过一些技巧,你可以处理周期性的部分。
MATCH (stop:Stop:Bus)
WITH stop, ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'] AS days
UNWIND RANGE(0, 6) AS day_index
WITH stop, day_index, days[day_index] AS day_key
UNWIND stop[day_key] AS int_time
WITH stop, day_index * 24 * 60 AS day_minutes, int_time % 100 AS minutes, ((int_time - (int_time % 100))/100)*60 AS hour_minutes
WHERE day_minutes IS NOT NULL
WITH stop, day_minutes + hour_minutes + minutes AS time_since_sunday
MERGE (dep:Departure:Bus {time: time_since_sunday})
MERGE (dep) - [:AT] -> (stop)
WITH stop, time_since_sunday
ORDER BY time_since_sunday
WITH stop, collect(time_since_sunday) AS times
SET stop.departure_times = times
好吧,这给了我们每个公共汽车站周围的出发事件,代表你可以在其他地方开始定时旅行的时间,如果你在那个时间之前在车站。现在,如果我们只考虑基于传输的移动,我们可以将每个:Departure
节点连接到一个:Stop
,以便在传输时间过去后,在下一站点可用的任何:Departure
节点连接(并考虑等待时间)。然而,添加步行(多模式传输)会改变这一点,因为每当过境到达某个地方时,您可以立即“离开”自己的两只脚。因此,我们应该将:Arrival
节点建模为与基于:Departure
的旅行的另一端相对应,以便我们可以区分等待下一次基于公交的:Departure
和步行,时间
MATCH (stop:Stop:Bus) <- [:AT] - (dep:Departure:Bus)
WITH stop, dep, dep.time AS dep_time
MATCH (stop) - [r:CONNECTED_TO] -> (other:Stop:Bus)
WITH dep, dep_time, dep_time + r.travelTime AS arrival_time, other
MERGE (a:Arrival:Bus {time: arrival_time})
MERGE (a) - [:AT] -> (other)
MERGE (dep) - [:TRAVEL {time: arrival_time - dep_time, mode: 'Bus'}] -> (a)
WITH a, arrival_time, other, other.departure_times AS dep_times
WITH a, other, arrival_time, REDUCE(s = HEAD(dep_times), x IN TAIL(dep_times) | CASE WHEN x < arrival_time THEN s WHEN s < x THEN s ELSE x END) AS next_dep_time
WITH a, other, next_dep_time, next_dep_time - arrival_time AS wait_time
MATCH (other) <- [:AT] - (next_dep:Departure:Bus {time: next_dep_time})
MERGE (a) - [:TRAVEL {time: wait_time, mode: 'Wait'}] -> (next_dep)
好的,所以对于每条单独的公交线路,我们现在可以计算单独从该公交线路上的A到B需要多长时间,即使行程是非连续的(火车在停靠时停了下来)等等)现在有趣的部分:整合步行选项! “行人停留”的想法没有多大意义(除非你在建模所有相关的离散空间,在这种情况下好运和上帝速度),所以我们基本上会完全放弃它们。在两个非行人站之间操作的:SWITCH_TO
将被建模为在两个站点之间行走,而:SWITCH_TO
行人过境将被转换为结合开关和步行本身的长途步行。首先是简单的(不在您的样本路径中,但我认为最终对您有价值):
MATCH (stop:Stop:Bus) - [r:SWITCH_TO] -> (other:Stop)
WHERE NOT other:Pedestrian
WITH stop, other, r.switchTime AS walking_time, other.departure_times AS dep_times
MATCH (stop) <- [:AT] - (arr:Arrival)
WITH arr, other, walking_time, dep_times, arr.time + walking_time AS new_arr_time
MERGE (new_arr:Arrival:Pedestrian {time: new_arr_time})
MERGE (new_arr) - [:AT] -> (other)
MERGE (arr) - [:TRAVEL {time:walking_time, mode: 'Pedestrian'}] -> (new_arr)
WITH new_arr, other, new_arr_time, REDUCE(s = HEAD(dep_times), x IN TAIL(dep_times) | CASE WHEN x < new_arr_time THEN s WHEN s < x THEN s ELSE x END) AS next_dep_time
WITH new_arr, other, next_dep_time, next_dep_time - new_arr_time AS wait_time
MATCH (other) <- [:AT] - (next_dep:Departure {time:next_dep_time})
MERGE (new_arr) - [:TRAVEL {time: wait_time, mode: 'Wait'}] -> (next_dep)
好的,这样可以处理基本转移。这个模型假设,如果你要在公共汽车或火车“线路”之间切换,那么你将把它们建模为单独的停靠点(如果他们真的是同一个地方,步行时间为0就好了,但它要复杂得多跟踪您是否共享:Stop
s)。现在要处理更复杂的转移,这些转移以前被建模为切换到:Pedestrian
,旅行,然后转回:
MATCH (stop:Stop:Bus) - [r1:SWITCH_TO] -> (:Stop:Pedestrian) - [r2:CONNECTED_TO] -> (:Stop:Pedestrian) - [r3:SWITCH_TO] -> (other:Stop)
WITH stop, other, other.departure_times AS dep_times, REDUCE(s = 0 , x IN [r1, r2, r3] | s + COALESCE(x.travelTime, x.switchTime) ) AS walking_time
MATCH (stop) <- [:AT] - (arr:Arrival)
WITH arr, other, dep_times, walking_time, arr.time + walking_time AS new_arr_time
MERGE (new_arr:Arrival:Pedestrian {time:new_arr_time})
MERGE (new_arr) - [:AT] -> (other)
MERGE (arr) - [:TRAVEL {time:walking_time, mode: 'Pedestrian'}] -> (new_arr)
WITH new_arr, new_arr_time, other, REDUCE(s = HEAD(dep_times), x IN TAIL(dep_times) | CASE WHEN x < new_arr_time THEN s WHEN s < x THEN s ELSE x END) AS next_dep_time
WITH new_arr, other, next_dep_time, next_dep_time - new_arr_time AS wait_time
MATCH (other) <- [:AT] - (next_dep:Departure {time:next_dep_time})
MERGE (new_arr) - [:TRAVEL {time: wait_time, mode: 'Wait'}] -> (next_dep)
这为您提供了加权关系的基本框架,以便将dijkstra
算法应用于您的停止运动。可能会有一些估计来确定哪些停止可能会产生良好的结果,以及如何从/到达给定点(参见前面的语句re:建模所有离散空间),但是如果你可以精确定位{{1或:Departure
节点从(或几个候选者)开始,以及:Arrival
节点用于查询的另一端:
:Stop