【问题标题】:Optimizing relation creation on large number of existing nodes在大量现有节点上优化关系创建
【发布时间】:2023-03-09 22:03:01
【问题描述】:

用于在大量节点(~1000 - 10000)之间创建关系的密码查询如下所示。

    "MATCH (startNode1),....,(startNode1000),(endNode1),..(endNode1000)
     WHERE ID(startNode1) = 538035 AND .. ID(startNode1000) = 538035 AND ID(endNode1) = 577 ..ID(endNode1000) = 586 
     CREATE 
     (startNode1)-[r1:`ParameterValue`{Name: "Phi"}]->(endNode1),
     (startNode2)-[r2:`ParameterValue`{Type: "block"}]->(endNode2),..
     (startNode1000)-[r100:`ParameterValue`{FontAngle: "auto"}]->(endNode1000)
     RETURN ID(r1), ID(r2), ..... ID(r1000)"

我正在使用官方的 java neo4j 螺栓驱动程序来运行密码查询,平均而言,上述密码命令大约需要 15 秒,这不适合我的应用程序。

我的问题是:

  1. 是否有任何明显的优化我错过了捆绑我的 密码查询这种方式?
  2. 一般来说,循环是个好习惯吗? 在节点上创建关系而不是捆绑它们?

我是新手,欢迎提出任何建议。谢谢!

【问题讨论】:

    标签: neo4j cypher


    【解决方案1】:

    通过这种方式捆绑我的密码查询,我是否遗漏了任何明显的优化?

    是的。有问题的查询使查询优化器和执行引擎的工作变得极其困难。如果您使用 Web 浏览器运行带有 EXPLAIN 的查询计划,它会告诉您此查询计划需要大量元组,而这不是引擎构建的处理方式。

    一般来说,循环节点以创建关系而不是捆绑它们是一种好习惯吗?

    循环是一个很好的方向。事实上,您可以进行多种优化:

    1. [startNode, endNode, relationshipProperties] 三元组的集合作为单个参数传递给驱动程序和UWIND

    2. 使用SET relationship_variable = map_variableconstruct初始化关系的属性。

    3. 或者,您可能希望使用collect 方法在列表中返回结果ID,但这通常不是必需的,因为驱动程序返回一个可迭代的结果。

    这个查询包含了这些技巧:

    UNWIND $rels AS rel
    WITH
      rel[0] AS startNodeId,
      rel[1] AS endNodeId, 
      rel[2] AS relationshipProperties
    MATCH (startNode:SomeLabel), (endNode:SomeLabel)
    WHERE ID(startNode) = startNodeId
      AND ID(endNode) = endNodeId
    CREATE (startNode)-[r:`ParameterValue`]->(endNode1)
    SET r = relationshipProperties
    WITH collect(ID(r)) AS relationshipIds
    RETURN relationshipIds
    

    在客户端,您应该使用StatementRunner.run(statementTemplate, statementParameters) method 传递参数。只是一个带有rels 的单个键和一个三元素列表的映射。您可以使用好的集合库以简洁的方式表达这一点,例如 Guava 的immutable collections

    Map<String, Object> parameters =
      ImmutableMap.of("rels",
        ImmutableList.of(
          ImmutableList.of(1, 2, ImmutableMap.of("prop1", "value1", "prop2", false)),
          ImmutableList.of(3, 4, ImmutableMap.of(...)),
        ...
      )
    );
    StatementRunner.run("UNWIND ...", parameters);
    

    注意 1. Cypher 语言允许您将节点和关系作为参数传递。但是,这仅适用于嵌入式版本和not in the client-server setup,因此您最好传递 id。

    注意 2. 话虽如此,传递内部 ID 是一种不好的做法。虽然它适用于大多数原型,但您可能需要考虑避免使用它们并提供您自己的标识符。见Best and Worst Practices with Node IDs

    注意 3. 除了使用三元组之外,您还可以使用映射来表示要创建的关系。这样,您将它们作为 Java 中的 Map 传递,即您的列表将包含地图:

    ImmutableMap.of("startNode", 1, "endNode", 2, "relationshipProperties", ImmutableMap.of("prop1", "value1", "prop2", true))
    

    然后您可以将这些值称为rel.startNoderel.endNoderel.relationshipProperties。这使得查询的可读性和维护更容易一些,但通常不值得这么麻烦。

    【讨论】:

    • 非常感谢!这是很重要的信息,我会在试用后回复您有关性能的信息。
    • 实施解决方案后更新。我能够将关系创建时间从 15 秒减少到 0.2 秒!再次感谢您的解决方案和其他指示。
    【解决方案2】:

    您实际上可以使用密码创建关系

    MATCH (u:User {username:'admin'}), (r:Role {name:'ROLE_WEB_USER'})
    CREATE (u)-[:HAS_ROLE]->(r)
    

    这只是一个示例,其中我们有一个用户节点并且我们正在创建一个具有角色的关系。

    这里是文档的链接。 https://neo4j.com/docs/developer-manual/current/cypher/clauses/create/#create-create-a-relationship-between-two-nodes

    我认为上述方法不是正确的方法

    【讨论】:

      猜你喜欢
      • 2016-05-01
      • 1970-01-01
      • 1970-01-01
      • 2017-03-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多