我正在使用Neo4J,NodeJS和GTFS数据构建流量计划应用程序;目前,我正试图获得 在柏林地铁网络上为一天工作的东西。这些是总计 到目前为止我收集了:
10 routes
211 stops
4096 trips
83322 stoptimes
stoptime
的概念,表示
特定火车或公共汽车的事件停止乘客上车和下车。 stoptime
发生trip
,
这是一系列stoptimes
,它们发生在特定的日期和时间,它们发生在给定的日期和时间
对于传输网络中的给定stop
(或'行'),route
。所以这里有很多参考资料。
我遇到的问题是数据量和构建数据库所需的时间。为了 为了加快速度,我已经(1)将数据减少到一天,(2)删除了数据库文件 并让服务器创建一个新的(非常有效!),(3)搜索了很多,以获得更好的查询。唉, 如上图所示,获得图表的所有边缘仍需要30~50分钟。
这些是我建立的指数:
CREATE CONSTRAINT ON (n:trip) ASSERT n.id IS UNIQUE;
CREATE CONSTRAINT ON (n:stop) ASSERT n.id IS UNIQUE;
CREATE CONSTRAINT ON (n:route) ASSERT n.id IS UNIQUE;
CREATE CONSTRAINT ON (n:stoptime) ASSERT n.id IS UNIQUE;
CREATE INDEX ON :trip(`route-id`);
CREATE INDEX ON :stop(`name`);
CREATE INDEX ON :stoptime(`trip-id`);
CREATE INDEX ON :stoptime(`stop-id`);
CREATE INDEX ON :route(`name`);
我猜测唯一的主键应该是最重要的。
这里的查询占用了80%的运行时间(10%与Neo4J无关, 使用纯HTTP后请求提供节点数据需要10%:
MATCH (trip:`trip`), (route:`route`)
WHERE trip.`route-id` = route.id
CREATE UNIQUE (trip)-[:`trip/route` {`~label`: 'trip/route'}]-(route);
MATCH (stoptime:`stoptime`), (trip:`trip`)
WHERE stoptime.`trip-id` = trip.id
CREATE UNIQUE (trip)-[:`trip/stoptime` {`~label`: 'trip/stoptime'}]-(stoptime);
MATCH (stoptime:`stoptime`), (stop:`stop`)
WHERE stoptime.`stop-id` = stop.id
CREATE UNIQUE (stop)-[:`stop/stoptime` {`~label`: 'stop/stoptime'}]-(stoptime);
MATCH (a:stoptime), (b:stoptime)
WHERE a.`trip-id` = b.`trip-id`
AND ( a.idx + 1 = b.idx OR a.idx - 1 = b.idx )
CREATE UNIQUE (a)-[:linked]-(b);
MATCH (stop1:stop)-->(a:stoptime)-[:next]->(b:stoptime)-->(stop2:stop)
CREATE UNIQUE (stop1)-[:distance {`~label`: 'distance', value: 0}]-(stop2);
第一个查询仍然在几分钟的范围内,我觉得只有
数据库中有数千(不是数十万或数百万)trips
个stoptime
。随后的查询
让{{1}}在我的台式机上花费几十分钟。
(我还计算了每天的时间表是否确实包含83322个停止时间,是的,这似乎是合理的: 在柏林,地铁列车每天运行10班,每天20小时,每小时6或12次,共有173班 地铁站:10线x 2个方向x每线17.3个站点x 20个小时x每小时9个站点给出62280, 足够近。那些 有些错吗?数据中的/ double / extra stop节点(211 停止而不是173),但那些很少。)
坦率地说,如果我找不到加速至少十倍(更多)的方法,那么使用Neo4J就没什么意义了 对于这个项目。只是为了覆盖柏林的单个城市许多,许多必须添加更多停留时间, 因为地铁只是这里整体公共交通的一小部分(例如公共汽车和有轨电车都有 拥有7,000个站点的170条路线,因此每天需要大约7,000,000个停车时间。)更新上面的边缘创建查询,我逐个执行,现在已经运行了一个多小时但还没有完成,这意味着 - 如果事情以线性方式扩展 - 时间需要在一天内提供柏林公共交通数据,这将花费一周的时间。因此,代码目前执行几个数量级太慢而无法生存。
更新 @ MichaelHunger的解决方案确实有效;请参阅下面的回复。
答案 0 :(得分:2)
我使用LOAD CSV在10分钟内导入12M节点和12M rels到Neo4j。
当您在shell中对查询进行概要分析时,您应该会看到问题。
使用profile
为您的查询添加前缀,如果它提到使用索引或者只是标签扫描,则查看配置文件输出。
您是否为插入查询使用参数?那么Neo4j可以重用构建的查询吗?
对于这样的查询:
MATCH (trip:`trip`), (route:`route`)
WHERE trip.`route-id` = route.id
CREATE UNIQUE (trip)-[:`trip/route` {`~label`: 'trip/route'}]-(route);
很可能不会使用您的索引。 你能指出你的数据源吗?我们可以将其转换为CSV,如果它不是,然后更快地导入。 也许我们可以为您的模型创建图形要点?
我宁愿使用:
MATCH (route:`route`)
MATCH (trip:`trip` {`route-id` = route.id)
CREATE (trip)-[:`trip/route` {`~label`: 'trip/route'}]-(route);
对于您的初始导入,您也不需要创建唯一的,因为您只匹配每次旅行一次。 而且我不确定你的标签是什么"〜标签"有用吗?
与您的其他查询相似。
由于数据是公开的,因此在这方面合作会很酷。
我喜欢听到更多关于您计划如何表达查询用例的内容。
上次在莱比锡与培训与会者进行了公共交通时间表的讨论。您也可以通过neo4j.org发送电子邮件给我michael。
也许你想查看这些链接:
答案 1 :(得分:1)
详细解决方案
我很高兴地报道@ MichaelHunger的解决方案就像一个魅力。我修改了边缘构建查询 从以下形状的问题出发,建议查询大纲:
MATCH (route:`route`)
MATCH (trip:`trip` {`route-id`: route.id})
CREATE (trip)-[:`trip/route` {`~label`: 'trip/route'}]->(route)
MATCH (trip:`trip`)
MATCH (stoptime:`stoptime` {`trip-id`: trip.id})
CREATE (trip)-[:`trip/stoptime` {`~label`: 'trip/stoptime'}]->(stoptime)
MATCH (stop:`stop`)
MATCH (stoptime:`stoptime` {`stop-id`: stop.id})
CREATE (stop)-[:`stop/stoptime` {`~label`: 'stop/stoptime'}]->(stoptime)
MATCH (a:stoptime)
MATCH (b:stoptime {`trip-id`: a.`trip-id`, `idx`: a.idx + 1})
CREATE (a)-[:linked {`~label`: 'linked'}]->(b)
MATCH (stop1:stop)--(a:stoptime)-[:linked]-(b:stoptime)--(stop2:stop)
CREATE (stop1)-[:distance {`~label`: 'distance', value: 0}]->(stop2)
可以看出,这里的技巧是为每个参与节点提供自己的MATCH
语句
在第二个匹配条件中移动WHERE
子句;据推测,如上所述,Neo4J只能
然后利用其索引。
在这些查询到位后,读取节点和构建边缘的过程大约需要13分钟;
在这13分钟内,从外部源获取数据,构建节点表示并发出CREATE
个查询
需要大约10分钟,并且在大约3分钟内完成它们之间近50万个边缘。
现在没有我的查询(特别是节点CREATE
语句和停止距离更新)使用
参数化查询,这是性能提升的另一个潜在来源。
至于~label
字段以及为什么我在名称中使用dahes的问题,其中下划线会更多
很方便,这是一个很长的故事,关于我认为有时冲突的好的和实用的命名
用一些语言的语法(大多数语言,我应该说)。但那是无聊的细节。也许更多
intersting是一个问题:为什么有~label
属性重复元素标签所说的内容(什么
你在冒号之后写的?)好吧,这是尝试遵守Neo4J惯例(我们在这里使用标签),采取
cypher查询的“标识符,冒号,标签”语法的优点,以及标签所做的那样
出现在返回的值中。
请注意,标签是图形思维Neo4J方式的核心,但*在查询结果中,标签是 明显缺席。当您在结果集中包含除标签之外的任何关系时, 然后那个边缘将以空的形式到达 对象,只告诉你有某些东西而不是是什么。所以我决定复制一下 每个节点和每个边缘上的标签。不是最佳解决方案,但至少现在我得到了一个信息 在Neo4J浏览器中显示图形。
至于如何表达查询用例,这对我来说是一个活跃的研究领域。我猜它会 一切都从一个“感兴趣的领域”开始,比如“显示所有柏林地铁站”或“所有离开的公共汽车” 从我附近的公共汽车站下一个15分钟。数据已经允许查看哪些停靠点直接连接 通过地铁线,他们的地理距离,提供什么服务以及他们采取什么样的路线。这个想法 是抓住数据并以新颖,实用和美丽的方式呈现它们。 9292是完全正确的 接近我的想象;缺少的是空间和时间关系的图形表示。