Traversal框架 neo4j

来源:互联网 发布:詹姆斯身体素质数据 编辑:程序博客网 时间:2024/05/18 03:20

2.5.2. Traversal框架(Java版本)

  • 2.5.2.1. TraversalDescription
  • 2.5.2.2. Evaluator
  • 2.5.2.3. Traverser
  • 2.5.2.4. Uniqueness
  • 2.5.2.5. Order
  • 2.5.2.6. BranchSelector
  • 2.5.2.7. Path
  • 2.5.2.8. PathExpander/RelationshipExpander
  • 2.5.2.9. Expander
  • 2.5.2.10. 如何使用Traversal框架

traversal框架由除了Node 和 Relationship以外的一系列主要接口组合而成:

TraversalDescriptionEvaluatorTraverser 和  Uniqueness 是最主要的. Path 在遍历中也有一定的用途, 因为在评价一个位置他用来表示一个位置。此外,PathExpander (或者 RelationshipExpander) 和 Expander接口也属于traversals,但用户使用他们时需要自己实现。这里有大量的接口的高级用法,当在遍历时要求明确控制顺序时:BranchSelectorBranchOrderingPolicy 和 TraversalBranch

2.5.2.1. TraversalDescription

TraversalDescription是用于定义和初始化traversals的最主要接口。并不意味着要由用户实现,而是由traversal框架作为一个描述traversals的方法提供。

TraversalDescription的实例是不可改变的,他的方法返回一个新的TraversalDescription,相对对象来说,这个方法可以通过带参数修改调用。

关系

增加一个关系类型到traverse的关系列表中。默认情况下,列表是空的,意味着将遍历所有关系,不管什么类型。如果一个或者更多的关系被加入到列表中,那么就只会遍历增加的类型。这里有两个方法,一个是包括方向,另外一个是排除方向,都被包括在双向中。

2.5.2.2. Evaluator

Evaluator用在决定,在每一个位置(用Path表示):是应该继续遍历,是/否在结果中包含节点。给一个路径,要求指定四个分支中的一个:

  • Evaluation.INCLUDE_AND_CONTINUE: 在结果中包括这个节点并继续遍历
  • Evaluation.INCLUDE_AND_PRUNE: 在结果中包括这个节点并不再遍历
  • Evaluation.EXCLUDE_AND_CONTINUE: 在结果中不包括这个节点并继续遍历
  • Evaluation.EXCLUDE_AND_PRUNE: 在结果中不包括这个节点并不再遍历

可以配置不只一个evaluator。注意evaluator会被遍历中遇到的每一个位置所调用,包括起点。

2.5.2.3. Traverser

Traverser对象是调用traverse()的结果。他表明了遍历在图中的位置,以及返回结果格式的规范。实际的遍历都是赖允许的,只有当调用迭代器中的 next()方法才会真正调用Traverser

2.5.2.4. Uniqueness

当一个遍历启动后,在Uniqueness中设置那些位置可以被重新访问。默认情况下,设置成:NODE_GLOBAL

一个由TraversalDescription提供的Uniqueness可以表明可以被重新访问的相同位置的情况。不同的uniqueness等级如下:

  • NONE - 在图中任何位置都可以被多次访问。
  • NODE_GLOBAL uniqueness – 在整个图中,没有节点可以被访问超过一次。这会消耗大量内存,因为他要求保存所有访问过的节点到内存中。
  • RELATIONSHIP_GLOBAL uniqueness – 在整个图中,没有关系可以被访问超过一次。这会消耗大量内存,因为他要求保存所有访问过的关系到内存中。
  • NODE_PATH uniqueness – 节点不会出现在之前路径出现过的地方。
  • RELATIONSHIP_PATH uniqueness – 关系不会出现在之前路径出现过的地方。
  • NODE_RECENT uniqueness – 是相对于NODE_GLOBAL的简化,表明有一个曾经访问过的节点的全局集合。然而这个等级设置有一个内存消耗的上限,他的集合只包括最近访问过的节点。这个集合的大小可以通过方法TraversalDescription.uniqueness()的第二个参数来指定。
  • RELATIONSHIP_RECENT uniqueness – 跟NODE_RECENT工作原理类似,只是用关系代替了节点。

宽度优先 / 深度优先

有一些方法专门用于设置 BranchSelector|ordering 的宽度优先/深度优先策略。调用 Traversal factory里面的order方法可以达到同样的效果,或者开发你自己的 BranchSelector/BranchOrderingPolicy

2.5.2.5. Order

深度优先/宽度优先方法的一般版本允许通过一个随意的BranchOrderingPolicy被注入description到中。

2.5.2.6. BranchSelector

一个BranchSelector是用来设置traversal下一步将遍历哪一个分支。这个一般用来实现遍历顺序。traversal框架提供了一个基本的顺序实现:

  • Traversal.preorderDepthFirst() - 遍历深度优先, 在访问他的子节点之前访问其他每一个节点。
  • Traversal.postorderDepthFirst() - 遍历深度优先, 在访问他的子节点之后访问其他每一个节点。
  • Traversal.preorderBreadthFirst() - 遍历宽度优先, 在访问他的子节点之前访问其他每一个节点。
  • Traversal.postorderBreadthFirst() - 遍历宽度优先, 在访问他的子节点之后访问其他每一个节点。
[Note]Note请注意宽度优先比深度优先消耗更多的内存。

BranchSelectors没有维持状态,因此需要每个遍历唯一实例。因此通过一个BranchOrderingPolicy接口来提供给TraversalDescription,BranchSelector实例的一个工厂模式。

Traversal框架的一个用户很少需要自己实现BranchSelector或者BranchOrderingPolicy,图算法已经提供了。Neo4j图算法包包含了一些范例,说明BranchSelector/BranchOrderingPolicy中用的一些算法,比如A* 和 Dijkstra。

BranchOrderingPolicy

创建BranchSelectors的工厂决定了返回的分支方向(分支的位置用Path表示)。一般的策略是宽度优先深度优先。举个例子,调用TraversalDescription#depthFirst()

description.order( Traversal.preorderDepthFirst() );

TraversalBranch

被BranchSelector使用的一个对象从某一个分支获取更多的分支。本质上,有一个由一个路径和一个RelationshipExpander(用来从当前分支中获取更多新的TraversalBranch)复合组成。

2.5.2.7. Path

一个路径也是Neo4j API接口的一部分。在traversal API中,路径的用法是双重的。Traversers可以返回他们自己的结果,这些结果由在图中被访问过的并且标记成需要被返回的路径组成。路径对象在图中也被用于路径的评估,用于判断traversal在某个点是否继续或者某个点是否应该包括在结果中。

2.5.2.8. PathExpander/RelationshipExpander

traversal使用PathExpanders (取代 RelationshipExpander) 发现关系,通过这些关系,可以从一个指定的路径遍历更多的分支出来。

2.5.2.9. Expander

注入RelationshipExpander到关系中的一般情况是定义遍历的所有关系。默认情况下,一个默认的expander会被使用,任何其他关系的顺序都无法保证。为了保证在关系类型的顺序中的关系被遍历,还有另外一种实现方式,在其中关系的类型是被新增的。

Expander接口继承了RelationshipExpander,确保他能自定义ExpanderTraversalDescription的实现使用这个提供定义关系类型的方法,

TraversalDescription内部构建一个RelationshipExpander,是用户使用API的一种常用方法。

被Neo4j框架提供的所有RelationshipExpanders也实现了Expander接口。对于traversal API的一个用户来说,实现PathExpander/RelationshipExpander接口变得更加容易,因为他之需要包括一个方法,这个方法用于从路径/节点中获取关系,Expander接口增加的方法之用于构建新的Expanders。

2.5.2.10. 如何使用Traversal框架

图2.5.2.10.1. Traversal 范例图

Traversal-Example-Graph-how-to-use-the-Traversal-framework.svg

定义RelationshipTypes

private enum Rels implements RelationshipType
{
    LIKES, KNOWS
}

下面的放里图数据库将被遍历,从节点”Joe”开始:

for ( Path position : Traversal.description()
        .depthFirst()
        .relationships( Rels.KNOWS )
        .relationships( Rels.LIKES, Direction.INCOMING )
        .evaluator( Evaluators.toDepth( 5 ) )
        .traverse( node ) )
{
    output += position + "\n";
}

遍历后输出结果:

(7)
(7)<--[LIKES,1]--(4)
(7)<--[LIKES,1]--(4)--[KNOWS,6]-->(1)
(7)<--[LIKES,1]--(4)--[KNOWS,6]-->(1)--[KNOWS,4]-->(6)
(7)<--[LIKES,1]--(4)--[KNOWS,6]-->(1)--[KNOWS,4]-->(6)--[KNOWS,3]-->(5)
(7)<--[LIKES,1]--(4)--[KNOWS,6]-->(1)--[KNOWS,4]-->(6)--[KNOWS,3]-->(5)--[KNOWS,2]-->(2)
(7)<--[LIKES,1]--(4)--[KNOWS,6]-->(1)<--[KNOWS,5]--(3)

因为TraversalDescription是不可改变的,所以创建descriptions模版来在不同的遍历中共享使用是个不错的方法。举个例子,让我们开始遍历:

final TraversalDescription FRIENDS_TRAVERSAL = Traversal.description()
        .depthFirst()
        .relationships( Rels.KNOWS )
        .uniqueness( Uniqueness.RELATIONSHIP_GLOBAL );

traverser输出结果 (我们从节点“Joe“开始):

(7)
(7)--[KNOWS,0]-->(2)
(7)--[KNOWS,0]-->(2)<--[KNOWS,2]--(5)
(7)--[KNOWS,0]-->(2)<--[KNOWS,2]--(5)<--[KNOWS,3]--(6)
(7)--[KNOWS,0]-->(2)<--[KNOWS,2]--(5)<--[KNOWS,3]--(6)<--[KNOWS,4]--(1)
(7)--[KNOWS,0]-->(2)<--[KNOWS,2]--(5)<--[KNOWS,3]--(6)<--[KNOWS,4]--(1)<--[KNOWS,5]--(3)
(7)--[KNOWS,0]-->(2)<--[KNOWS,2]--(5)<--[KNOWS,3]--(6)<--[KNOWS,4]--(1)<--[KNOWS,6]--(4)

现在,让我们创建一个遍历,并限制深度为三:

for ( Path path : FRIENDS_TRAVERSAL
        .evaluator( Evaluators.toDepth( 3 ) )
        .traverse( node ) )
{
    output += path + "\n";
}

返回结果:

(7)
(7)--[KNOWS,0]-->(2)
(7)--[KNOWS,0]-->(2)<--[KNOWS,2]--(5)
(7)--[KNOWS,0]-->(2)<--[KNOWS,2]--(5)<--[KNOWS,3]--(6)

如果深度是2或者4呢:

for ( Path path : FRIENDS_TRAVERSAL
        .evaluator( Evaluators.fromDepth( 2 ) )
        .evaluator( Evaluators.toDepth( 4 ) )
        .traverse( node ) )
{
    output += path + "\n";
}

遍历返回结果:

(7)--[KNOWS,0]-->(2)<--[KNOWS,2]--(5)
(7)--[KNOWS,0]-->(2)<--[KNOWS,2]--(5)<--[KNOWS,3]--(6)
(7)--[KNOWS,0]-->(2)<--[KNOWS,2]--(5)<--[KNOWS,3]--(6)<--[KNOWS,4]--(1)

获取各种不同的Evaluator的用法,查看Evaluators Java API 或者自己去实现 Evaluator接口。

如果你对路径没有兴趣,你可以像下面这样将遍历后的节点转换成节点迭代器:

for ( Node currentNode : FRIENDS_TRAVERSAL
        .traverse( node )
        .nodes() )
{
    output += currentNode.getProperty( "name" ) + "\n";
}

在这种情况下,我们用他接受名称:

Joe
Sara
Peter
Dirk
Lars
Ed
Lisa

关系也是非常容易的,下面是获取方法:

for ( Relationship relationship : FRIENDS_TRAVERSAL
        .traverse( node )
        .relationships() )
{
    output += relationship.getType() + "\n";
}

关系类型写入后,我们会得到:

KNOWS
KNOWS
KNOWS
KNOWS
KNOWS
KNOWS

相关源代码下载:TraversalExample.java

0 0
原创粉丝点击