【发布时间】:2021-03-03 04:35:41
【问题描述】:
我使用的是最新的 Neo4j.Driver 包 (4.2.0) 和最新的 Neo4j 服务器社区版 (4.2.3)。
我一定是做错了什么,因为我的查询需要几个小时才能完成。
我有 4 个 CSV 文件:
- XyzTypes.csv - 定义了 96,328 个类型节点。
- XyzMethods.csv - 定义了所有类型的 975,507 个方法。
- XyzTypeTypeDependencies.csv - 定义了 121,834 个类型-类型 DEPENDS_ON 关系。
- XyzTypeMethods.csv - 定义了 973,972 个类型-方法 DECLARES 关系。
下面的代码应该很简单。它只需要加载所有 CSV 并创建相应的类型、方法和关系。
这是我的代码:
var driver = GraphDatabase.Driver("bolt://localhost:7687", AuthTokens.Basic("neo4j", "1"));
var session = driver.AsyncSession(o => o.WithDatabase("neo4j"));
try
{
Console.Write("[DI");
await session.RunAsync("DROP INDEX type_id_index IF EXISTS");
await session.RunAsync("DROP INDEX method_id_index IF EXISTS");
Console.Write("][C");
await session.WriteTransactionAsync(async tx =>
{
await tx.RunAsync("match ()-[r]->() delete r");
await tx.RunAsync("match (n) delete n");
return default(object);
});
Console.Write("][T");
await session.WriteTransactionAsync(async tx =>
{
await tx.RunAsync(@"
LOAD CSV WITH HEADERS FROM 'file:///C:/Temp/XyzTypes.csv' AS line
CREATE (:Type {
typeId: toInteger(line.id),
name: line.name,
fullName: line.fullName,
isCompilerGenerated: toBoolean(line.isCompilerGenerated),
asmName: line.asmName
})");
return default(object);
});
Console.Write("][M");
await session.WriteTransactionAsync(async tx =>
{
await tx.RunAsync(@"
LOAD CSV WITH HEADERS FROM 'file:///C:/Temp/XyzMethods.csv' AS line
CREATE (:Method {
methodId: toInteger(line.id),
name: line.name,
fullName: line.fullName,
isCompilerGenerated: toBoolean(line.isCompilerGenerated)
})");
return default(object);
});
Console.Write("][CI");
await session.RunAsync("CREATE INDEX type_id_index FOR (t:Type) ON (t.typeId)");
await session.RunAsync("CREATE INDEX method_id_index FOR (m:Method) ON (m.methodId)");
Console.Write("][TT");
await session.RunAsync(@"
USING PERIODIC COMMIT LOAD CSV WITH HEADERS FROM 'file:///C:/Temp/XyzTypeTypeDependencies.csv' AS line
MATCH (src:Type), (dst:Type)
WHERE src.typeId = toInteger(line.src) AND dst.typeId = toInteger(line.dst)
CREATE (src)-[:DEPENDS_ON]->(dst)
");
Console.Write("][TM");
await session.RunAsync(@"
USING PERIODIC COMMIT LOAD CSV WITH HEADERS FROM 'file:///C:/Temp/XyzTypeMethods.csv' AS line
MATCH (src:Type), (dst:Method)
WHERE src.typeId = toInteger(line.src) AND dst.methodId = toInteger(line.dst)
CREATE (src)-[:DECLARES]->(dst)
");
Console.Write("] ... ");
}
finally
{
await session.CloseAsync();
await driver.CloseAsync();
}
CREATE INDEX 查询立即返回。可能是合法的,我不知道 Neo4j 可以多快索引大约 1M 节点中的数字属性。在浏览器中运行:schema 可以确认这两个索引,但我感觉它们不起作用。
运行上述代码需要将近 3 个小时。我做错了什么?
编辑 1
所以我将最后两个查询更改为使用MERGE 子句:
Console.Write("][TT");
await session.RunAsync(@"
USING PERIODIC COMMIT LOAD CSV WITH HEADERS FROM 'file:///C:/Temp/XyzTypeTypeDependencies.csv' AS line
MERGE (src:Type {typeId: toInteger(line.src)})-[:DEPENDS_ON]->(dst:Type {typeId: toInteger(line.dst)})
");
Console.Write("][TM");
await session.RunAsync(@"
USING PERIODIC COMMIT LOAD CSV WITH HEADERS FROM 'file:///C:/Temp/XyzTypeMethods.csv' AS line
MERGE (src:Type {typeId: toInteger(line.src)})-[:DECLARES]->(dst:Method {methodId: toInteger(line.dst)})
");
现在应该好多了,因为我认为我之前所做的导致节点之间的笛卡尔乘法。然而最后一个查询花费了未知的时间(目前不知道多长时间) - 仍然很糟糕。
【问题讨论】:
-
索引和约束的创建是异步的,因此您很可能在索引和约束建立之前就抢先一步。您可能应该使用
CALL db.awaitIndexes()以防万一。此外,不推荐使用 MERGE 方法,因为它必须检查这种模式是否存在,如果不存在则创建整个模式,这将导致重复节点。在节点上匹配,然后创建或合并关系,是更好的方法(如果 rel 可能已经存在,则只有 MERGE)。忽略笛卡尔积警告,这正是您创建关系所需要的(每行 1 x 1)。 -
所以基本上,您是在告诉我回到 EDIT 1 之前的版本。我将添加等待索引。
-
@InverseFalcon - 请安排您的评论作为答案,所以我可以相信您,因为它有效!我猜
db.awaitIndexes是关键。非常感谢。