在neo4j查询优化中使用展开和转储数据

在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节点 我的查询分别针对所有情况执行,目标节点和朋友节点之间很可能存

我正在批量插入以在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

我的查询如下

 """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)
    上创建(或唯一性约束),以加快
    匹配
    合并
    子句的速度

  • (a)
    MERGE(u:Target{id:user.id,uid:user.uid})
    只需执行一次,而不是按
    c
    值执行。因此,它应该在
    展开之前执行

    (b) 此外,由于查询中没有任何内容使用它,因此此查询不一定要创建
    u
    。因此,不是每线程运行一个相同的<代码>合并< /COD>子句,应该考虑将它取出并运行单独的独立查询。

  • 下面是一个结合了建议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
    

    我正在基于id创建索引,但在我的场景中不可能有唯一约束。原因是,我的软件可能有一个朋友节点存在于系统中,稍后用户希望将其更改为目标,因此约束检查失败,因为我在本场景中复制该节点,并且在本例中仅更改用户id(如果我需要,则无需更改)。。还有其他建议吗?也许你应该重新考虑一下你的图形模式库,以获得非常好的建议;这真的很有帮助。请您在查询中添加create_rel参数化代码,因为此时我对分配关系有点困惑。期待您的建议,我无法添加适当的参数化
    create_rel
    代码,因为您的问题从未显示原始的
    create_rel
    代码。我只是说你不应该在这个查询中插入一个单独的
    create\u rel
    字符串——你应该直接在这个查询中包含这个字符串中的任何内容。如果不知道那根绳子里有什么,我就不能告诉你怎么做。如果您最终需要帮助,您可能需要创建一个单独的问题。这个问题已经太复杂了。