我正在玩Neo4j。我有一个包含大约400,000个节点的数据库。我想从CSV文件中插入关系。有大约140万个关系。
我目前正在使用REST API。 REST请求看起来像这样的例子:
POST http://localhost:7474/db/data/cypher
Accept: application/json; charset=UTF-8
Content-Type: application/json
{"query": "MATCH (a { ConceptId: '280844000' }), (b { ConceptId: '71737002' }) CREATE (a)-[:Is_a]->(b) RETURN a"}
问题是每个请求都需要几秒钟。对于我希望插入的关系数量来说,这太慢了。
我无法访问底层节点ID,只能访问插入它们时给出的属性。
有更快的方法吗?
注意:我目前没有使用索引(我还没有弄清楚如何添加它们),但明天会再次使用索引。我只是想知道是否有某种方式以批量方式插入关系。
答案 0 :(得分:8)
第一个改进可能是为节点分配标签,以便您可以使用索引。如果没有conceptId
上的索引,则每次执行查询时,它将扫描400,000个节点两次,对于您匹配的两个节点中的每个节点扫描一次。根据您的查询进行推测,您可以为您的节点提供标签:Concept
并将conceptId
属性编入索引,如下所示
MATCH (n)
// WHERE HAS (n.conceptId) //if you have some nodes that don't represent concepts, and conceptId distinguishes the ones that do from others
SET n:Concept
然后是索引
CREATE INDEX ON :Concept(conceptId)
或如果conceptId
是唯一标识值,则可以使用约束
CREATE CONSTRAINT ON (c:Concept) ASSERT c.conceptId IS UNIQUE
设置标签并创建索引后,您可以使用它们快速查找要连接的节点。您需要做的就是在查询中包含label和indexed属性。您已经使用了索引属性,因此添加查询的标签
MATCH (a:Concept {ConceptId: '280844000'}), (b:Concept {ConceptId: '71737002'})
CREATE (a)-[:Is_a]->(b)
RETURN a
您可以阅读有关架构in the Neo4j documentation的更多信息。
第二项改进可能是使用LOAD CSV
,正如@stephenmuss建议的那样。
如果您将来的查询不是基于csv文件,还有两件事需要考虑。第一个是参数化您的查询。您的HTTP
电话会看起来像这样
POST http://localhost:7474/db/data/cypher
Accept: application/json; charset=UTF-8
Content-Type: application/json
{"query": "MATCH (a { ConceptId: {a} }), (b { ConceptId: {b} }) CREATE (a)-[:Is_a]->(b) RETURN a","params":{"a":"280844000","b":"71737002"}}
这允许执行引擎为该结构的第一个查询创建一次执行计划。下次发出具有相同结构的查询时,将重用缓存的执行计划。这将显着提高具有相同结构的重复查询的性能。
最后一件事就是@ulkas评论,批量插入。 LOAD CSV
更快的一个原因是它在一个事务中执行多个操作。您可以使用事务性密码端点执行类似操作。然后,您可以为每个事务执行几千个小语句,这对于在数据库上操作的性能要高得多,并且还将减少线路上的开销。为事务端点设计有效负载以及处理异常稍微复杂一些。下面是一个简单示例,您可以在Neo4j manual pages。
POST http://localhost:7474/db/data/transaction
Accept: application/json; charset=UTF-8
Content-Type: application/json
{"statements":[
{"statement":"MATCH (a:Concept {ConceptId: {a}}), (b:Concept {ConceptId: {b}}) CREATE (a)-[:Is_a]->(b) RETURN a","parameters":{"a":"280844000","b":"71737002"}},
{"statement":"MATCH (a:Concept {ConceptId: {a}}), (b:Concept {ConceptId: {b}}) CREATE (a)-[:Is_a]->(b) RETURN a","parameters":{"a":"199401294","b":"51233509"}}
]}
服务器返回新事务的location
,例如"http://localhost:7474/db/data/transaction/1"
。您可以继续在同一事务中执行语句
POST http://localhost:7474/db/data/transaction/1
Accept: application/json; charset=UTF-8
Content-Type: application/json
{"statements":[...]}
当你完成后,你承诺。提交调用也可以包含语句。
POST http://localhost:7474/db/data/transaction/1/commit
Accept: application/json; charset=UTF-8
Content-Type: application/json
{"statements":[...]}
答案 1 :(得分:3)
如果您使用的是Neo4j 2.1+,我认为您最好的选择是使用LOAD CSV
。
然后您可以使用如下语法
USING PERIODIC COMMIT
LOAD CSV WITH HEADERS FROM "file:/path/to/file.csv" AS csvLine
MATCH (a{ConceptId: csvLine.aId}), (b{ConceptId: csvLine.bId})
CREATE (a)-[:Is_a]->(b)
我建议您查看docs for importing csv files。