在neo4j查询优化中使用展开和转储数据
我正在批量插入以在neo4j中插入数据,但我的事务花费了大量时间,因为我的数据库也在不断增加 在我的项目中,仅在一种情况下,我拥有18000条以上的记录,这些记录将存储在db中,并且将与目标节点建立关系。 每条记录将存储为朋友节点 关系就像 目标节点-[r:后跟]->Friend\u节点 Target\u Node-[r:Friends\u with]->Friends\u Node 目标\u节点-[r:执行\u活动]->朋友\u节点 我的查询分别针对所有情况执行,目标节点和朋友节点之间很可能存在所有三种关系 对于单个插入,我将为每个线程发送20条记录,该插入将在记录数组上展开,并检查记录是否已存在于Friend_节点或Target_节点中,如果不存在,则将其创建为Friend_节点,然后为其分配关系;如果节点已经有关系,并且一个新关系被传递给查询,那么两个节点之间也将添加一个新关系 此外,如果记录具有Location属性,我会在查询中进行检查,然后创建一个Location节点并分配与该节点的关系 注意:create\u rel变量可以是Friends\u with,后跟或Activity\u p 我的查询如下在neo4j查询优化中使用展开和转储数据,neo4j,cypher,neo4j-apoc,Neo4j,Cypher,Neo4j Apoc,我正在批量插入以在neo4j中插入数据,但我的事务花费了大量时间,因为我的数据库也在不断增加 在我的项目中,仅在一种情况下,我拥有18000条以上的记录,这些记录将存储在db中,并且将与目标节点建立关系。 每条记录将存储为朋友节点 关系就像 目标节点-[r:后跟]->Friend\u节点 Target\u Node-[r:Friends\u with]->Friends\u Node 目标\u节点-[r:执行\u活动]->朋友\u节点 我的查询分别针对所有情况执行,目标节点和朋友节点之间很可能存
"""UNWIND [{id: "1235" , uid : "0"}] as user
UNWIND """+ l +""" as c
OPTIONAL MATCH (n:Target {id : c.id , uid : "0"})
OPTIONAL MATCH (m:Friend {id : c.id , screen_name:c.screen_name, uid : "0"})
WITH coalesce(n, m) as node,user,c // returns first non-null value
CALL apoc.do.when(node is null, "MERGE (n:Friend {id:c.id, name:c.name, profile: c.profile, location:c.location, uid : user.uid}) RETURN n", '', {c:c,user:user}) YIELD value
with coalesce(node, value.n) as y,user,c
MERGE (u:Target {id: user.id , uid : user.uid})
"""+create_rel+"""
foreach (sc in c.cityn | merge(cn:Location {location:sc.location, loc_lower : sc.loc_lower}) merge (y)-[:`located_at`]-(cn))
"""
Db有时也会给出TransientError错误
作为一名学习者,我非常感谢反馈,也非常感谢有价值的建议
提前感谢我认为您的主要问题在于如何合并和匹配节点。理想情况下,您总是希望节点具有唯一标识符。我可以看到,
Friend
节点有一个属性id
,我将假定该属性对于每个Friend
和Target
都是唯一的
首先,要在该特性上创建唯一约束:
CREATE CONSTRAINT ON (f:Friend) ASSERT f.id IS UNIQUE;
CREATE CONSTRAINT ON (f:Target) ASSERT f.id IS UNIQUE;
对于位置
节点,您也需要类似的东西。似乎您同时存储了位置值和位置的小写值,因此它们中的任何一个对于每个节点都应该是唯一的
CREATE CONSTRAINT ON (l:Location) ASSERT l.id IS UNIQUE;
现在,您可以像这样优化查询:
"""UNWIND [{id: "1235" , uid : "0"}] as user
UNWIND """+ l +""" as c
OPTIONAL MATCH (n:Target {id : c.id})
OPTIONAL MATCH (m:Friend {id : c.id})
WITH coalesce(n, m) as node,user,c // returns first non-null value
CALL apoc.do.when(node is null,
"MERGE (n:Friend {id:c.id})
ON CREATE SET n+= {name:c.name, profile: c.profile,
location:c.location, uid : user.uid}
RETURN n", '', {c:c,user:user})
YIELD value
with coalesce(node, value.n) as y,user,c
MERGE (u:Target {id: user.id , uid : user.uid})
"""+create_rel+"""
foreach (sc in c.cityn |
merge(cn:Location {location:sc.location})
ON CREATE SET cn.loc_lower = sc.loc_lower
merge (y)-[:`located_at`]-(cn))
"""
TransientError
s。(但是,可以重试导致暂时错误的查询。)
user
和l
作为传递给您的查询,以便Cypher planner只需编译查询一次,并使查询不易受到Cypher注入攻击。(另外,不需要展开总是只有一个映射的列表,您可以通过直接使用映射,{id:“1235”,uid:“0”}作为用户。但是,正如我提到的,您应该将用户映射作为参数传递,这样您就可以在不强制重新编译的情况下有效地更改用户。)
create_rel
字符串设置为常量字符串(因此,它也可以直接位于主查询字符串中)。同样,您还应该将它所需的任何变量作为参数传递
:Target(id)
和:Friend(id)
上创建(或唯一性约束),以加快匹配
和合并
子句的速度
MERGE(u:Target{id:user.id,uid:user.uid})
只需执行一次,而不是按c
值执行。因此,它应该在展开之前执行
(b) 此外,由于查询中没有任何内容使用它,因此此查询不一定要创建u
。因此,不是每线程运行一个相同的<代码>合并< /COD>子句,应该考虑将它取出并运行单独的独立查询。
MERGE (u:Target {id: $user.id, uid: $user.uid})
WITH u
UNWIND $l as c
WITH u, c, [(n:Target {id : c.id})-[*0]-()|n] AS nodeList
WITH u, c, CASE WHEN SIZE(nodeList) = 0 THEN [(n:Friend {id : c.id})-[*0]-()|n] ELSE nodeList END AS nodeList
CALL apoc.do.when(SIZE(nodeList) = 0, 'MERGE (n:Friend {id: c.id, name: c.name, profile: c.profile, location: c.location, uid: user.uid}) RETURN n', 'RETURN nodeList[0] AS n', {c:c,user:$user,nodeList:nodeList}) YIELD value
WITH u, c, value.n AS node
FOREACH (sc IN c.cityn | MERGE (cn:Location {location: sc.location, loc_lower: sc.loc_lower}) MERGE (node)-[:located_at]-(cn))
// Put your parameterized create_rel code here
我正在基于id创建索引,但在我的场景中不可能有唯一约束。原因是,我的软件可能有一个朋友节点存在于系统中,稍后用户希望将其更改为目标,因此约束检查失败,因为我在本场景中复制该节点,并且在本例中仅更改用户id(如果我需要,则无需更改)。。还有其他建议吗?也许你应该重新考虑一下你的图形模式库,以获得非常好的建议;这真的很有帮助。请您在查询中添加create_rel参数化代码,因为此时我对分配关系有点困惑。期待您的建议,我无法添加适当的参数化
create_rel
代码,因为您的问题从未显示原始的create_rel
代码。我只是说你不应该在这个查询中插入一个单独的create\u rel
字符串——你应该直接在这个查询中包含这个字符串中的任何内容。如果不知道那根绳子里有什么,我就不能告诉你怎么做。如果您最终需要帮助,您可能需要创建一个单独的问题。这个问题已经太复杂了。