我有一个目录树(只是一堆行),我按以下方式进行转换:
/
/selinux
/run
/run/pppconfig
/run/sendmail
...
变为
MERGE (r:Root {name: "", treeName: "tree"})
MERGE r<-[:CHILD_OF]-(_1:Node {name:"selinux"})
MERGE r<-[:CHILD_OF]-(_2:Node {name:"run"})
MERGE r<-[:CHILD_OF]-(_2)<-[:CHILD_OF]-(_3:Node {name:"pppconfig"})
MERGE r<-[:CHILD_OF]-(_2)<-[:CHILD_OF]-(_4:Node {name:"sendmail"})
...
但是/bin/neo4j-shell -path ~/dt -file ~/dirs.cypher
需要很长时间才能获得大约500K,这并不是那么大。这种方法有其他替代方案吗?将源转换为CSV并使用LOAD CSV加载会显示更好的结果吗?
UPD:
当CSV看起来像这样:
pid,nid,title
0,1,"a"
1,2,"b"
2,3,"c"
3,4,"d"
2,5,"e"
5,6,"f"
我无法一次创建所有节点,因为它们似乎以不允许引用要插入此CSV文件的节点的方式插入:
USING PERIODIC COMMIT 100
LOAD CSV WITH HEADERS FROM "file:///path/to/dirs1.csv" AS csvLine
MATCH (p:Node {nid: toInt(csvLine.pid)})
MERGE (p)<-[:CHILD_OF]-(c:Node {nid: toInt(csvLine.nid), title: csvLine.title})
因此,首先只插入/a
而/a/b
不插入,因为在进程开始时数据库中不存在/a
。我可以以某种方式克服此限制,以便测量LOAD CSV
的执行速度吗?
更新2:所以我决定尝试另一种方法:首先,做:
USING PERIODIC COMMIT 5000
LOAD CSV FROM 'file:///home/roman/dirs1.csv' AS line
MERGE (c:Node {title: line[2], nid: toInt(line[1])})
然后我将再次浏览CSV以创建关系。但节点创建本身非常慢!像〜1000个节点/秒的东西!现在我正在编写更新,第一阶段仍处于活动状态。难道我做错了什么? (承诺期并不重要,我尝试了10K,结果几乎相同。)
现在有些更新,如果有人仍对我的问题感兴趣。
首先,我设法在第一阶段实现10秒的时间,即只是转储节点,通过创建不是约束而只是节点上的索引。 IDS。但第2阶段(倾销关系)仍然需要3-4分钟,这是相当不可接受的时间。至于现在,我要玩我所拥有的东西,但如果你尝试像我一样的事情 - 如果你有问题,不要在LOAD CSV
之前创建约束跟我的相似。当您从约束退回到指数时,转储时间会急剧减少。
答案 0 :(得分:2)
面对同样的问题,决定尝试neo4j-shell方式。
关于性能,我可以在1.5分钟内在6个secondes和3M关系中插入30k节点。这不是很快,但对我来说足够快。
我们的想法是为节点创建CSV文件nodes.log
:
name:string|doc_id:string
lala|32189
lulu|38124
...
并使用neo4j-shell
查询将其导入CYPHER
:
import-cypher -i path/to/nodes.log -d | -o path/to/batch.out -b 10000 CREATE (n {name: {name}, doc_id:{doc_id}}) SET n:Node RETURN id(n), n.name
然后,batch.out
被转换为node id
的散列映射,用于直接在创建关系的CYPHER
查询中访问节点。我们的想法是跳过非常昂贵的节点查找。
import-cypher -i path/to/relationships.log -d | -b 100000 START n=node({start}), m=node({end}) CREATE (n)-[:IS_LIKE {property:{value}}]->(m)
with relationships.log:
start:int|end:int|value:float
1|2|4.56
1|4|1.23
...
命令的输出显示节点的性能:
finish after 32799 row(s) 3. 99%: nodes = 32799 rels = 0 properties = 163995 time 176 ms total 6449 ms
和关系:
finish after 2925377 row(s) 29. 99%: nodes = 0 rels = 2925377 properties = 2925377 time 1433 ms total 100667 ms
答案 1 :(得分:1)
您不需要多重连接路径进行合并。
用分号结束每个语句块(例如每个级别)。
别忘了
create unique constraint on (n:Node) assert n.name is unique;
或者可能不是这种情况,也许是索引,但是你可能会有一些错误的查找路径。因此,最好使用pid,稍后再使用它。
然后将其中的1k包装到begin
和commit
begin
MERGE (r:Root {name: "", treeName: "tree"})
MERGE r<-[:CHILD_OF]-(_1:Node {name:"selinux"})
MERGE r<-[:CHILD_OF]-(_2:Node {name:"run"});
MATCH (_2:Node {name:"run"})
MERGE (_2)<-[:CHILD_OF]-(_3:Node {name:"pppconfig"})
MERGE (_2)<-[:CHILD_OF]-(_4:Node {name:"sendmail"})
commit
...
花费大量时间解析那个巨大的语句,然后在一个tx中执行它。
要快速加载CSV,您还可以使用我的CSV批量导入程序:
http://github.com/jexp/batch-import
当CSV看起来像这样:
pid,nid,title
0,1,"a"
1,2,"b"
2,3,"c"
3,4,"d"
2,5,"e"
5,6,"f"
你也必须在nid上进行合并。并为:Node.nid
创建约束create unique constraint on (n:Node) assert n.nid is unique;
USING PERIODIC COMMIT 100
LOAD CSV WITH HEADERS FROM "file:///path/to/dirs1.csv" AS csvLine
MERGE (p:Node {nid: toInt(csvLine.pid)})
MERGE (p)<-[:CHILD_OF]-(c:Node {nid: toInt(csvLine.nid)}) ON CREATE SET c.title=csvLine.title
更新2:所以我决定尝试另一种方法:首先,做:
USING PERIODIC COMMIT 5000
LOAD CSV FROM 'file:///home/roman/dirs1.csv' AS line
MERGE (c:Node {title: line[2], nid: toInt(line[1])})
合并仅适用于单个属性,因为它可以使用约束或索引。
您可以尝试的其他方法是将您的CSV拆分为100k的块。
USING PERIODIC COMMIT 5000
LOAD CSV FROM 'file:///home/roman/dirs1.csv' AS line
WITH line
SKIP 200000 LIMIT 100000
MERGE (c:Node {nid: toInt(line[1])}) ON CREATE SET c.title=line[2]
<强>更新强>
不幸的是,您遇到了一个使用LOAD CSV的密码问题:(您是否可以尝试将输入文件拆分为更小的尺寸:
USING PERIODIC COMMIT 5000
LOAD CSV FROM 'file:///home/roman/dirs1.csv' AS line
WITH line
SKIP 40000 limit 20000
MATCH
(parent:Node {nid: toInt(line[0])}),
(child: Node {nid: toInt(line[1])})
CREATE
(child)-[:CHILD_OF]->(parent)
答案 2 :(得分:0)
最后,取得了重大进展。首先,我完全放弃了Cypher,但由于数据库没有被停止,我只使用一个Transaction
进行了一系列的插入。经过一段时间花费不同的事务大小,我找到了最合适的东西 - 例如275行(即275个节点+每次冲洗275个关系(tx.success()
)。但最有帮助的是缓存节点,所以我赢了'我需要昂贵的数据库查找。我使用了一个简单的Map<String, Node>
,其中key是节点的完整路径,value是节点本身。所以,有条目<"", rootNode>
和路径/a
,我们可以轻松创建Node{name : 'a'}
,其父级为cache.get("") == rootNode
,依此类推。现在500K转储需要大约55秒,虽然仍然没有接近预期的5-15秒,但这对我们有用。 / p>