双向遍历框架
双向遍历包括两个从不同节点开始的遍历,并返回两个遍历碰撞处的路径。双向遍历框架允许使用 BidirectionalTraversalDescription
描述此类遍历。
以下是从起始节点 1
和结束节点 5
的双向遍历的视觉表示,其中所有粗体关系都在结果路径中,给定以下限制
-
起始侧遍历器仅遍历类型为
A
的关系。 -
结束侧遍历器仅遍历类型为
B
的关系。
请注意,仅其中一个遍历器到达相反的起始/结束节点的路径也包含在内,例如 (1)-[:A]→(5)
和 (1)-[:B]→(5)
。在接受的路径 (1)-[:A]→(2)-[:A]→(3)-[:B]→(4)-[:B]→(5)
中,遍历器在中间的节点 3
处碰撞。
|
定义双向遍历器
在创建 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)
方法设置此双向遍历的起始侧和结束侧。但是,结束侧是起始侧的逆向。对于 Relationships
的 Direction
,这意味着 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);