Java Neo4j,使用Cypher命令进行批量加载

Java Neo4j,使用Cypher命令进行批量加载,java,neo4j,cypher,Java,Neo4j,Cypher,我是Neo4j的新手,一定有一些基本知识我不了解 我在Java中有很多对象,我想用它们来填充Neo4j图,使用Java驱动程序和密码。我的代码是这样工作的: // nodes for ( Person person: persons ) session.run ( String.format ( "CREATE ( :Person { id: '%s', name: \"%s\", surname: \"%s\" })", person.getId(), person.ge

我是Neo4j的新手,一定有一些基本知识我不了解

我在Java中有很多对象,我想用它们来填充Neo4j图,使用Java驱动程序和密码。我的代码是这样工作的:

// nodes
for ( Person person: persons )
  session.run ( String.format ( 
    "CREATE ( :Person { id: '%s', name: \"%s\", surname: \"%s\" })",
    person.getId(), person.getName(), person.getSurname ()
  ));

// relations
session.run ( "CREATE INDEX ON :Person(id)" );

for ( Friendship friendship: friendships )
  session.run ( String.format ( 
    "MATCH ( from:Person { id: '%s' } ), ( to:Person { id: '%s' } )\n" +
    "CREATE (from)-:KNOWS->(to)\n",
    friendship.getFrom().getId(), 
    friendship.getTo().getId() 
  )); 
(事实上,它稍微复杂一些,因为我有十几种节点类型和大约相同数量的关系类型)

现在,这非常慢,比如加载300k节点和1M关系需要1个多小时(在相当快的MacBookPro上,Neo4j占用12/16GB的RAM)


我做错了吗?我应该用这个来代替吗?(我希望能够通过网络访问graphDB)。通过将更多的插入分组到一个事务中,我会得到一些东西吗?(从文档中可以看出,事务似乎只对回滚和隔离需求有用)。

我来自Python中的Neo4j,但我认为这里的问题在于您的Cypher命令。我有两个建议

单独匹配边可能会更快。在我的原始基准测试中,我看到了24毫秒与15毫秒的差异(编辑:这个基准测试是可疑的):

另一种选择是使用“展开”。我使用BOLT接口发送更少的事务,但不使用批插入器。请原谅我在这里复制的Python实现,希望您可以将其与Javascript Neo4j驱动程序文档一起进行转换

payload = {"list":[{"a":"Name1","b":"Name2"},{"a":"Name3","b":"Name4"}]}

statement = "UNWIND {list} AS d "
statement += "MATCH (A:Person {name: d.a}) "
statement += "MATCH (B:Person {name: d.b}) " 
statement += "MERGE (A)-[:KNOWS]-(B) "

tx = session.begin_transaction()
tx.run(statement,payload)
tx.commit()

我认为值得报告我在这方面的经验

我已经按照@sjc的建议尝试了放松。然而,这并不是那么简单,因为Cypher不允许您参数化节点标签或关系类型(我有十几个标签和关系类型)。但最终,我能够循环所有可能的类型,并向每个展开块发送足够的项目(大约1000个)

在我看来,使用UNWIND的代码速度要快得多,但还不够快(在一台像样的PC上,节点数不多的情况下应该可以,但在数亿个或更多节点上就不是很好)

inserter组件的速度要快得多(上传100-200万个节点只需几秒钟),尽管它需要关闭HTTP访问,而且它对Lucene 5.4的依赖性也有很多问题,因为我需要在使用Lucene 6的应用程序(生成数据)中使用它,当我试图在类路径中将5.4和6简单地交换时,发生了可怕的事情。我读过这篇文章,但它似乎并不容易,而且肯定没有那么好的文档记录


我绝对没有想到高效地执行这样一个基本操作会遇到这么多麻烦。

谢谢,但我认为在我的情况下,这样做行不通。考虑到我的应用程序在不到3分钟的时间内(使用哈希映射)填充其内部图形,24-15毫秒也没什么不同,而Neo4j也需要很多时间才能完成。这不可能是对的,应该采取大致相同的措施。至于WIND,考虑到我有这么多节点和边缘,我认为发送一个列表作为参数最终会变成一个太大的查询。我的基准测试可能会关闭,但我强烈建议至少尝试放松。您的负载列表不必是所有的数据,您可以将其分块(就像我在实践中所做的那样,而不是在上面的示例代码中)。如果有效负载的大小为~100k,并且您可以使用N个事务填充边缘,那么相对于N*100k个单独事务,这将节省大量时间。Cypher不允许参数化标签或关系类型,但该语句是由字符串构建的,因此,您可以在节点类型列表上循环,只需重新创建每个类型的字符串。Hi@sjc。这是一个老生常谈的问题,自从战争以来发生了很多事情。我必须在每个标签的基础上分割数据,然后将节点类型作为Cypher查询的一个常量部分发送,Cypher查询可以处理许多相同类型的节点。如果我没弄错的话,这和你的建议很相似。我实施这种方法的项目如下:
payload = {"list":[{"a":"Name1","b":"Name2"},{"a":"Name3","b":"Name4"}]}

statement = "UNWIND {list} AS d "
statement += "MATCH (A:Person {name: d.a}) "
statement += "MATCH (B:Person {name: d.b}) " 
statement += "MERGE (A)-[:KNOWS]-(B) "

tx = session.begin_transaction()
tx.run(statement,payload)
tx.commit()