您的位置:首页 > 其它

Traversal框架 neo4j

2016-02-16 17:10 232 查看

2.5.2. Traversal框架(Java版本)

发表于
2012 年 7 月 27 日 由
neo4j

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
以外的一系列主要接口组合而成:

TraversalDescription
Evaluator
Traverser
 和  
Uniqueness
 是最主要的. 
Path
 在遍历中也有一定的用途, 因为在评价一个位置他用来表示一个位置。此外,
PathExpander
 (或者 
RelationshipExpander
) 和 
Expander
接口也属于traversals,但用户使用他们时需要自己实现。这里有大量的接口的高级用法,当在遍历时要求明确控制顺序时:
BranchSelector
BranchOrderingPolicy
 和 
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
请注意宽度优先比深度优先消耗更多的内存。
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
,确保他能自定义
Expander
TraversalDescription
的实现使用这个提供定义关系类型的方法,

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

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

2.5.2.10. 如何使用Traversal框架

图2.5.2.10.1. Traversal 范例图





定义
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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: