首页 > 解决方案 > 在 neo4j 中使用展开和转储数据 - 查询优化

问题描述

我正在批量插入以在neo4j中插入数据,但我的事务需要大量时间,因为我的数据库也在不断增加。

在我的项目中,仅在一种情况下,我有超过18,000 条记录,这些记录将存储在数据库中,并将与目标节点建立关系。每条记录都将存储为朋友节点

关系就像

Target_Node-[r:followed_by]->Friend_Node

Target_Node-[r:Friends_with]-> Friend_Node

Target_Node-[r:Performs_Activity]->Friend_Node

我的查询分别针对所有情况执行,并且很有可能目标和朋友节点之间可能存在所有三种关系。

我为单个插入发送 20 条记录,该插入展开记录数组并检查记录是否已存在于 Friend_Node 或 Target_Node 中,如果不存在,则将其创建为 Friend_Node,然后为其分配关系;如果节点已经有关系并且新的关系被传递给查询,那么两个节点之间也将添加一个新的关系。

此外,如果记录确实具有Location属性,我也会检查我的查询,然后我会创建一个 Location Node 并分配与它的关系。

注意:create_rel 变量可以是 Friends_with、Followed_by 或 Activity_p

我的查询如下

 """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 错误。

感谢您的反馈,因为我是一名学习者,并将感谢您提出宝贵的建议。

提前致谢

标签: neo4jcypherneo4j-apoc

解决方案


  1. 您应该避免同时运行多个写入查询(可以触及相同的节点和关系),因为这可能会导致间歇性TransientErrors,正如您所见。(但是,可以重试导致暂时错误的查询。)

  2. 您应该将user和作为参数l传递给您的查询,以便 Cypher 计划程序只需要编译一次查询,并使查询不太容易受到 Cypher 注入攻击。(此外,不需要一个总是只有一个地图的列表——您可以通过 直接使用地图。但是,正如我所提到的,您应该只将地图作为参数传递,这样您就可以有效地更改用户无需强制重新编译。)UNWINDWITH {id: "1235" , uid : "0"} AS useruser

  3. 为避免重新编译,您还需要将create_rel字符串设为常量字符串(因此,它也可能直接位于主查询字符串中)。同样,您还应该将所需的任何变量作为参数传递。

  4. 您应该在and上创建索引(或唯一性约束),以加快您的and子句。:Target(id):Friend(id)MATCHMERGE

  5. (a)MERGE (u:Target {id: user.id , uid : user.uid})只需要执行一次,而不是每个c值。所以,它应该.UNWIND

    (b) 此外,此查询不一定要创建u,因为查询中没有任何内容使用它。MERGE因此,您应该考虑将其取出并运行一个单独的独立查询,而不是每个线程运行一次相同的子句。

这是一个结合了建议 #2 和 #5a 的查询(但您必须自己处理其他建议),以及使用模式理解进行一些重构以避免不必要的数据库命中:

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

推荐阅读