双向遍历框架

双向遍历包括两个从不同节点开始的遍历,并返回两个遍历碰撞处的路径。双向遍历框架允许使用 BidirectionalTraversalDescription 描述此类遍历。

以下是从起始节点 1 和结束节点 5 的双向遍历的视觉表示,其中所有粗体关系都在结果路径中,给定以下限制

  • 起始侧遍历器仅遍历类型为 A 的关系。

  • 结束侧遍历器仅遍历类型为 B 的关系。

bidirectional example

请注意,仅其中一个遍历器到达相反的起始/结束节点的路径也包含在内,例如 (1)-[:A]→(5)(1)-[:B]→(5)。在接受的路径 (1)-[:A]→(2)-[:A]→(3)-[:B]→(4)-[:B]→(5) 中,遍历器在中间的节点 3 处碰撞。

Uniqueness 在两个遍历之间共享,这意味着如果 Uniqueness 设置为 Uniqueness.NODE_GLOBAL(默认值),则不会有结果,因为两个遍历都需要到达同一个节点才能发生碰撞。因此,在使用 BidirectionalTraversalDescription 时,始终手动设置 Uniqueness。有关可用选项的更多信息,请参见 Uniqueness 选项.

定义双向遍历器

在创建 BidirectionalTraversalDescription 时,可以选择每侧采用的 TraversalDescription。这可以通过分别为每侧(startSide/endSide)设置它,或者为两者设置一个(mirroredSides)来完成。

起始侧和结束侧遍历

以下是具有 startSide()endSide() 的单独遍历器的 BidirectionalTraversalDescription 示例

BidirectionalTraversalDescription td = transaction
        .bidirectionalTraversalDescription()
        .startSide(transaction
                .traversalDescription()
                .expand(PathExpanders.forTypeAndDirection(RelationshipType.withName("A"), Direction.OUTGOING))
                .uniqueness(Uniqueness.RELATIONSHIP_GLOBAL))
        .endSide(transaction
                .traversalDescription()
                .expand(PathExpanders.forTypeAndDirection(RelationshipType.withName("B"), Direction.INCOMING))
                .uniqueness(Uniqueness.RELATIONSHIP_GLOBAL));
td.traverse(startNode, endNode);

一侧是从起始节点向前遍历并扩展所有类型为 A 的传出关系。另一侧是从结束节点向后遍历并扩展所有类型为 B 的传入关系。

最终路径从起始节点到结束节点,其中所有关系都具有传出方向。可能的路径是

  • 所有关系都是类型 A

  • 所有关系都是类型 B

  • 从起始节点的关系是类型 A,在碰撞后,它们都是类型 B

镜像遍历

mirroredSides(TraversalDescription) 方法设置此双向遍历的起始侧和结束侧。但是,结束侧是起始侧的逆向。对于 RelationshipsDirection,这意味着 OUTGOING 变成 INCOMING,反之亦然。PathExpander 接口还附带 reverse() 函数,如果在 mirroredSides 实现中使用,则应重写此函数。

BidirectionalTraversalDescription td = transaction
        .bidirectionalTraversalDescription()
        .mirroredSides(transaction
                               .traversalDescription()
                               .uniqueness(Uniqueness.RELATIONSHIP_GLOBAL));
td.traverse(startNode, endNode);

侧选择器

在双向遍历中,遍历器会选择在每一步中要移动哪一侧(起始侧或结束侧)。可以通过实现 SideSelectorPolicy 接口来更改此工作方式,因为它具有一个函数用于确定从哪一侧遍历下一步。

  • Direction.OUTGOING — 来自起始节点的遍历器。

  • Direction.INCOMING — 来自结束节点的遍历器。

内置策略包括

  • SideSelectorPolicies.LEVEL — 如果组合深度大于给定的最大深度,则停止遍历。

  • SideSelectorPolicies.LEVEL_STOP_DESCENT_ON_RESULT — 一旦找到结果就停止。

  • SideSelectorPolicies.ALTERNATING — 交替哪个分支继续遍历。

SideSelectorPolicy 可选地采用 maxDepth,它表示两个侧都必须遵守的组合深度。

以下是如何使用具有最大组合深度为 5 的内置 SideSelectorPolicy 的示例

BidirectionalTraversalDescription td = transaction
        .bidirectionalTraversalDescription()
        .mirroredSides(transaction
                               .traversalDescription()
                               .uniqueness(Uniqueness.RELATIONSHIP_GLOBAL))
                               .sideSelector(SideSelectorPolicies.LEVEL, 5);
td.traverse(startNode, endNode);

碰撞策略

BranchCollisionPolicy 定义何时在双向遍历中检测到碰撞并接受碰撞。给定一个评估器和一个路径谓词,BranchCollisionPolicy 创建 BranchCollisionDetector,它检测两个遍历器之间的碰撞,如果它满足碰撞评估器和 Uniqueness 约束给定的条件,则将生成的路径添加到结果中。

以下是一些内置 BranchCollisionPolicies

  • STANDARD — 返回所有具有碰撞的路径。

  • SHORTEST_PATH — 返回所有具有最小遍历深度的路径。

以下是如何使用内置 BranchCollisionPolicy 的示例

BidirectionalTraversalDescription td = transaction
        .bidirectionalTraversalDescription()
        .mirroredSides(transaction
                               .traversalDescription()
                               .uniqueness(Uniqueness.RELATIONSHIP_GLOBAL))
        .collisionPolicy(BranchCollisionPolicies.SHORTEST_PATH);
td.traverse(startNode, endNode);

碰撞评估器

便捷方法 collisionEvaluator() 添加 PathEvaluator,它验证有效碰撞中的路径。多次使用该方法会导致评估器组合。

BidirectionalTraversalDescription td = transaction
    .bidirectionalTraversalDescription()
    .mirroredSides(transaction
       .traversalDescription()
       .uniqueness(Uniqueness.RELATIONSHIP_GLOBAL))
    .collisionEvaluator(Evaluators.atDepth(3));
td.traverse(startNode, endNode);