首页 > 解决方案 > 我可以使用 1 个密码命令从 csv 文件加载节点和关系吗?

问题描述

我有 2 个 csv 文件,我正在尝试使用 cypher 将它们加载到 Neo4j 数据库中:drivers.csv 包含每个公式 1 驱动程序和 laptimes.csv,它存储 F1 中的每一圈。

我已经设法加载了所有节点,虽然单圈时间文件非常大,所以花了很长时间!然后我尝试在之后添加关系,但是需要添加的东西太多了,我放弃了等待(它花了好几天,仍然没有完全加载)。

我很确定有一种方法可以同时加载节点和关系,这将允许我对我现在无法执行的关系使用定期提交。本质上,我只需要将 2 个命令合二为一,经过一些尝试,我似乎无法弄清楚该怎么做?

// load in the lap_times.csv, changing the variable names - about half million nodes (takes 3-4 days)
PERIODIC COMMIT 25000
LOAD CSV WITH HEADERS from 'file:///lap_times.csv'
AS row
MERGE (lt: lapTimes {raceId: row.raceId, driverId: row.driverId, lap: row.lap, position: row.position, time: row.time, milliseconds: row.milliseconds})
RETURN lt;
// add a relationship between laptimes, drivers and races - takes 3-4 days
MATCH (lt:lapTimes),(d:Driver),(r:race)
WHERE lt.raceId = r.raceId AND lt.driverId = d.driverId
MERGE (d)-[rel8:LAPPING_AT]->(lt)
MERGE (r)-[rel9:TIMED_LAP]->(lt)
RETURN type(rel8), type(rel9)

提前感谢您的帮助!

标签: neo4jcyphercypher-shell

解决方案


您应该在此处查看索引文档: https ://neo4j.com/docs/cypher-manual/current/administration/indexes-for-search-performance/

基本上,索引一旦创建,就允许快速查找给定标签的节点,用于给定的一个或多个属性。如果您没有索引并且您对节点进行 MATCH 或 MERGE,那么对于该 MATCH 或 MERGE 的每一行,它必须对给定标签的所有节点进行标签扫描并检查它们的所有属性以找到节点,这变得非常昂贵,尤其是在加载 CSV 时,因为这些操作可能发生在 CSV 中的每一行

对于您的 :lapTimes 节点(尽管我们建议您在大多数情况下使用单数标签),如果您的图表中没有它们开始,那么 CREATE 而不是 MERGE 就可以了。您可能需要一个复合索引 on :lapTimes(raceId, driverId, lap),因为如果您以后需要查找它,它应该唯一地标识节点。在这里使用 CREATE 而不是 MERGE 应该处理得更快。

您的第二个查询应该是 MATCHing :lapTimes 节点(标签扫描),并且每个查询都在 :race 和 :driver 节点上进行索引查找,因此索引是性能的关键。

你需要索引::race(raceId):Driver(driverId).

MATCH (lt:lapTimes)
WITH lt, lt.raceId as raceId, lt.driverId as driverId
MATCH (d:Driver), (r:race)
WHERE r.raceId = raceId AND d.driverId = driverId
MERGE (d)-[:LAPPING_AT]->(lt)
MERGE (r)-[:TIMED_LAP]->(lt)

如果您知道没有重复的条目,您可以考虑为关系创建而不是合并。

我删除了您的 RETURN 因为返回类型不是有用的信息。

此外,请考虑为您的节点标签使用一致的案例,并且您在图表中的标签和您创建的索引之间使用相同的案例。

此外,您可能希望批量处理这些更改,而不是尝试一次处理所有更改。

如果您安装 APOC 程序,您可以使用apoc.periodic.iterate(),它可用于批量更改,这将在您的堆上更快、更容易。您仍然需要首先使用索引。

CALL apoc.periodic.iterate("
 MATCH (lt:lapTimes)
 WITH lt, lt.raceId as raceId, lt.driverId as driverId
 MATCH (d:Driver), (r:race)
 WHERE r.raceId = raceId AND d.driverId = driverId
 RETURN lt, d, ir",
 "MERGE (d)-[:LAPPING_AT]->(lt)
 MERGE (r)-[:TIMED_LAP]->(lt)", {}) YIELD batches, total, errorMessages
RETURN batches, total, errorMessages

单个 CSV 加载

如果您想在单个 CSV 加载中一次处理所有内容,您可以这样做,但您首先需要索引。这是您至少需要的:

CREATE INDEX ON :Driver(driverId);
CREATE INDEX ON :Race(raceId);

创建这些之后,您可以使用它,假设您从头开始(我修复了标签的大小写并将它们设为单数:

USING PERIODIC COMMIT 25000
LOAD CSV WITH HEADERS from 'file:///lap_times.csv' AS row
MERGE (d:Driver {driverId:row.driverId})
MERGE (r:Race {raceId:row.raceId})
CREATE (lt:LapTime {raceId: row.raceId, driverId: row.driverId, lap: row.lap, position: row.position, time: row.time, milliseconds: row.milliseconds})
CREATE (d)-[:LAPPING_AT]->(lt)
CREATE (r)-[:TIMED_LAP]->(lt)

推荐阅读