• TinkerPop中的遍历:图的遍历步骤(1/3)


    图遍历步骤(Graph Traversal Steps)

    在最一般的层次上,Traversal<S,E>实现了Iterator,S代表起点,E代表结束。遍历由四个主要组成部分组成:

    • Step<S,E>: 一个用来从S产生E的方法。Step在遍历中是链式的。
    • TraversalStrategy: 拦截器方法来改变遍历的执行(例如查询重写)。
    • TraversalSideEffects: 键/值对,可用于存储有关遍历的全局信息。
    • Traverser: the object propagating through the Traversal currently representing an object of type T.

    示例数据

    示例数据大多出自于Modern数据,如下图:

    可以通过下图加载Modern图数据,并获取图遍历引用g:

    gremlin> graph = TinkerFactory.createModern()
    gremlin> g = graph.traversal()
    

    1. General Steps

    Step Description
    map(Traversal<S, E>) map(Function<Traverser<S>, E>) 将运行程序映射到类型E的某个对象,以便下一步处理。
    flatMap(Traversal<S, E>) flatMap(Function<Traverser<S>, Iterator<E>>) 将遍历器映射到流向下一步的E对象的迭代器。
    filter(Traversal<?, ?>) filter(Predicate<Traverser<S>>) 将运行程序映射为true或false,其中false将不会将运行程序传递给下一步。
    sideEffect(Traversal<S, S>) sideEffect(Consumer<Traverser<S>>) 在移动器上执行一些操作,并将其传递到下一步。
    branch(Traversal<S, M>) branch(Function<Traverser<S>,M>) 将移动器拆分为由M令牌索引的所有遍历。

    • filter步骤的示例
    gremlin> g.V().filter(label().is('person'))
    
    • map步骤的示例
    gremlin> g.V(1).out().map(values('name'))
    
    • sideEffect步骤的示例
    gremlin> g.V().hasLabel('person').sideEffect(System.out.&println)
    
    • branch步骤的示例
    gremlin> g.V().branch(values('name')).
                   option('marko', values('age')).
                   option(none, values('name'))
    

    2. Terminal Steps

    一些步骤不返回遍历,而是执行遍历并返回结果。这些步骤就是Terminal步骤(终端步骤),并且通过下面的示例来解释它们。

    gremlin> g.V().out('created').hasNext()
    ==>true
    gremlin> g.V().out('created').next()
    ==>v[3]
    gremlin> g.V().out('created').next(2)
    ==>v[3]
    ==>v[5]
    gremlin> g.V().out('nothing').tryNext()
    ==>Optional.empty
    gremlin> g.V().out('created').toList()
    ==>v[3]
    ==>v[5]
    ==>v[3]
    ==>v[3]
    gremlin> g.V().out('created').toSet() 
    ==>v[3]
    ==>v[5]
    gremlin> g.V().out('created').toBulkSet()
    ==>v[3]
    ==>v[3]
    ==>v[3]
    ==>v[5]
    gremlin> results = ['blah',3]
    ==>blah
    ==>3
    gremlin> g.V().out('created').fill(results)
    ==>blah
    ==>3
    ==>v[3]
    ==>v[5]
    ==>v[3]
    ==>v[3]
    

    注意几点:

    1. 上述tryNext()将返回一个Optional的,是一个hasNext()/next()的组合。
    2. toSet()toBulkSet()都返回一个去重的集合,区别在于后者通过加权处理重复对象。

    3. AddEdge Step

    推理是明确显示数据中隐含的内容的过程。图中显式的是图形的对象,即顶点和边。图中隐含的是遍历。即,通过遍历揭示了意义,而所谓的意义是有遍历的定义来决定的。
    比如定义一个遍历,找出节点的合作开发者(co-developer),并在这种关系(边)上添加属性“year”:

    gremlin> g.V(1).as('a').out('created').in('created').where(neq('a')).
               addE('co-developer').from('a').property('year',2009)
    

    如此,曾经隐含的含义可以可以通过addE()-step(map/sideEffect)来显式。

    Question
    这里addE()-step(map/sideEffect)意味着addE()步骤产生了map和sideEffect普通步骤的效果吗?

    4. AddVertex Step

    addV()步骤用于向图中添加顶点(map/sideEffect)。
    增加顶点,示例如下:

    gremlin> g.addV('person').property('name','stephen')
    gremlin> g.V().outE('knows').addV().property('name','nothing')
    

    Question
    gremlin> g.V().outE('knows').addV().property('name','nothing')这种方式并没有增加节点之间的关联(边),那么它的意义在哪里?

    5. AddProperty Step

    property() 步骤用于向图形元素添加属性(sideEffect)。与addV()addE()不同,property()是一个完全的sideEffect步骤,它不会返回其创建的属性,而是返回添加属性的元素。那么,可以推测出在addV()addE()步骤后使用property()步骤可以在创建顶点或边的时候一次性创建多个属性。

    • 为一个节点添加一个属性
    gremlin> g.V(1).property('country','usa')
    
    • 为一个节点添加多个属性
    gremlin> g.V(1).property('city','santa fe').property('state','new mexico').valueMap()
    
    • (对顶点)添加多值属性(Cardinality.list)
    gremlin> g.V(1).property(list,'age',35)
    
    • 添加元属性
    gremlin> g.V(1).properties('name').property('author','inspur') 
    

    6. Aggregate Step

    aggregate()步骤(sideEffect)用于将遍历特定点处的所有对象(贪婪评估方式eager evaluation)聚合到集合中。
    aggregate step

    gremlin> g.V(1).out('created').aggregate('x').in('created').out('created').
                    where(without('x')).values('name') 
    ==>ripple
    

    7. And Step

    and()步骤确保所有提供的遍历产生结果(filter)。请参阅or()

    gremlin> g.V().and(
                outE('knows'),
                values('age').is(lt(30))).
                  values('name')
    ==>marko
    

    使用or()步骤会产生以下结果:

    gremlin> g.V().or(
                outE('knows'),
                values('age').is(lt(30))).
                  values('name')
    ==>marko
    ==>vadas
    

    8. As Step

    as()步骤不是一个真正的步骤,而是一个类似于by()option()的“调节器”。使用as(),可以为步骤提供标签,后续的步骤或数据结果可以通过使用这些标签访问这些步骤。

    gremlin> g.V().as('a').out('created').as('b').select('a','b')
    ==>[a:v[1],b:v[3]]
    ==>[a:v[4],b:v[5]]
    ==>[a:v[4],b:v[3]]
    ==>[a:v[6],b:v[3]]
    

    一步可以有任何数量的与之相关联的标签。这对于在将来的步骤中多次引用相同的步骤很有用:

    gremlin> g.V().hasLabel('software').as('a','b','c').
                select('a','b','c').
                  by('name').
                  by('lang').
                  by(__.in('created').values('name').fold())
    ==>[a:lop,b:java,c:[marko,josh,peter]]
    ==>[a:ripple,b:java,c:[josh]]
    

    Note
    以上__.(两个_)通过匿名方式产生了GraphTraversal。并使用了后续介绍的fold()步骤。

    9. Barrier Step

    barrier() 步骤将延迟遍历管道转换为批量同步管道。可类比线程同步中的栅栏。在以下情况下,此步骤很有用:

    • 使用在需要栅栏的场景
    • 进行“膨胀优化”(bulking optimization)

    膨胀优化对于某些程序执行效率的提高是非常明显的,如下示例:

    gremlin> g = graph.traversal().withoutStrategies(LazyBarrierStrategy) //屏蔽默认的遍历策略
    gremlin> clockWithResult(1){g.V().both().both().both().count().next()} //未使用barrier
    gremlin> clockWithResult(1){g.V().both().barrier().both().barrier().both().barrier().count().next()}  //使用barrier
    

    上述优化过程需要去掉LazyBarrierStrategy 遍历策略,即默认情况下遍历器已经使用LazyBarrierStrategy 进行了优化(barrier()步骤会在适当的情况下自动添加)。

    • 支持参数
      如果barrier()被提供一个整数参数n,那么在将聚合的遍历器入下一个步骤之前,栅栏只会在其屏障中保留n个唯一的遍历器。

    10. By Step

    如果一个步骤能够接受遍历、函数或比较器等,那么by()是添加它们的手段。

    gremlin> g.V().group().by(bothE().count()) //将元素按其边数进行分组,属于接受“遍历”的情况
    ==>[1:[v[2],v[5],v[6]],3:[v[1],v[3],v[4]]]
    gremlin> g.V().group().by(bothE().count()).by('name') //将通过其名称(元素属性投影)处理分组的元素
    ==>[1:[vadas,ripple,peter],3:[marko,lop,josh]]
    gremlin> g.V().group().by(bothE().count()).by(count()) //将计算每个组中的元素数量
    ==>[1:3,3:3]
    
    • 哪些步骤支持by()
      以下步骤支持by(),注意有些步骤支持一个by(),有些步骤支持多个by(),使用时需要参考文档。
    dedup()
    cyclicPath()
    simplePath()
    sample()
    where()
    groupCount()
    group()
    order()
    path()
    project()
    select()
    tree()
    aggregate()
    store()
    

    11. Cap Step

    cap()用来将一些副作用步骤产生的结果发射(emits)出来。
    如下使用label进行分组统计,并使用cap()将分组结果展现出来:

    gremlin> g.V().groupCount('a').by(label) //不使用cap()
    ==>v[1]
    ==>v[2]
    ==>v[3]
    ==>v[4]
    ==>v[5]
    ==>v[6]
    gremlin> g.V().groupCount('a').by(label).cap('a') //使用cap()
    ==>[software:2,person:4]
    

    另外,cap()支持多个key值,不同的key会组成 Map<String,Object>效果展现出来。

    12. Choose Step

    choose()将当前遍历器路由到特定的遍历分支选项。可用来实现if-then-else语法结构。
    判断条件可以放在choose步骤中,也可以放在option步骤中:

    • choose中进行判断
    gremlin> g.V().hasLabel('person').
                   choose(values('age').is(lte(30)),
                     __.in(),
                     __.out()).values('name')
    ==>marko
    ==>ripple
    ==>lop
    ==>lop
    
    • option中进行判断
    gremlin> g.V().hasLabel('person').
                   choose(values('age')).
                     option(27, __.in()).
                     option(32, __.out()).values('name') 
    ==>marko
    ==>ripple
    ==>lop
    

    13. Coalesce Step

    coalesce() - 步骤按顺序评估提供的遍历,并返回发出至少一个元素的第一个遍历。
    coalesce()可以传入多个遍历,并对这些遍历按照顺序进行评估。若某个元素匹配上某个遍历,那么返回该元素在该遍历上产生的结果,然后跳到下一个元素进行相同的操作。
    比如,当person含有nickname则返回nickname,当含有name则返回name;不会即返回nickname又返回name;当既没有nickname也没有name,则返回空。

    gremlin> g.V().hasLabel('person').coalesce(values('nickname'), values('name')) 
    ==>okram 
    ==>vadas 
    ==>josh 
    ==>peter
    

    14. Coin Step

    要随机过滤出一个遍历器,请使用coin() 步骤(filter)。

    gremlin> g.V().coin(0.0) //不返回节点
    gremlin> g.V().coin(0.5) //随机返回节点,注意它并不是随机返回一半
    gremlin> g.V().coin(1.0) //返回全部
    

    15. Constant Step

    要为运行程序指定常量值,请使用常量constant() 步骤(map)。对于诸如choose()或`coalesce() 的条件步骤通常很有用。

    gremlin> g.V().hasLabel('person').coalesce(values('nickname11'), values('name11'),constant("haha")) 
    ==>haha
    ==>haha
    ==>haha
    ==>haha
    

    16. Count Step

    属于map步骤范畴。举例如下:

    gremlin> g.V().count()
    ==>6
    gremlin> g.V().hasLabel('person').count()
    ==>4
    

    该步骤是减少栅栏的步骤(reducing barrier step),意味着所有先前的遍历器被折叠成新的遍历器。

    gremlin> g.V().hasLabel('person').outE('created').path()
    ==>[v[1],e[9][1-created->3]]
    ==>[v[4],e[10][4-created->5]]
    ==>[v[4],e[11][4-created->3]]
    ==>[v[6],e[12][6-created->3]]
    gremlin> g.V().hasLabel('person').outE('created').count()
    ==>4
    gremlin> g.V().hasLabel('person').outE('created').count().path()
    ==>[4]
    

    17. CyclicPath Step

    每个遍历器在遍历图形的时候会维护其历史,即其路径。如果重要的是遍历器重复它的过程,那么应该使用cyclic()- 路径(filter)。
    该步骤分析到目前为止的运行程序的路径,并且如果有任何重复,则遍历器被过滤掉以免重复遍历。
    如果需要非循环行为,请参阅simplePath()

    gremlin> g.V(1).both().both().cyclicPath().path() //查看循环的遍历路径
    ==>[v[1],v[3],v[1]]
    ==>[v[1],v[2],v[1]]
    ==>[v[1],v[4],v[1]]
    gremlin> g.V(1).both().both().simplePath().path() //查看非循环的遍历路径
    ==>[v[1],v[3],v[4]]
    ==>[v[1],v[3],v[6]]
    ==>[v[1],v[4],v[5]]
    ==>[v[1],v[4],v[3]]
    

    18. Dedup Step

    使用dedup()步骤(filter),重复的对象将从遍历流中删除。

    gremlin> g.V().values('lang')
    ==>java
    ==>java
    gremlin> g.V().values('lang').dedup()
    ==>java
    

    19. Drop Step

    drop()步骤(filter/sideEffect)用于从图形中删除元素和属性(即删除)。它是一个过滤器步骤,因为遍历不产生传出对象。

    gremlin> g.V().outE().drop() //删除边
    gremlin> g.E()
    gremlin> g.V().properties('name').drop() 删除属性
    gremlin> g.V().valueMap()
    ==>[age:[29]]
    ==>[age:[27]]
    ==>[lang:[java]]
    ==>[age:[32]]
    ==>[lang:[java]]
    ==>[age:[35]]
    gremlin> g.V().drop() //删除图
    gremlin> g.V()
    

    20. Explain Step

    explain() 步骤(terminal)将返回一个TraversalExplanation。遍历说明详细说明了如何根据注册的遍历策略编译遍历(在explain()之前)。

    第一列是应用的遍历策略。第二列是遍历策略类别:[D]ecoration,[O]ptimization,[P]rovider optimization,[F]inalization和[V]erification。最后,第三列是遍历后策略应用的状态。最终遍历是最终的执行计划。

    21. Fold Step

    有些情况下,遍历流需要“栅栏”来聚合所有对象并产生作为聚合函数的计算结果。fold()步骤(map)就是这样的例子。

    gremlin> g.V(1).out('knows').values('name')
    ==>vadas
    ==>josh
    gremlin> g.V(1).out('knows').values('name').fold() //简单合并
    ==>[vadas,josh]
    gremlin> g.V(1).out('knows').values('name').fold(0) {a,b -> a + b.length()} //统计name值的字符长度
    ==>9
    gremlin> g.V(1).out('knows').values('name').fold(0) {a,b -> a + b} //合并所有名字为一个长串
    ==>vadasjosh
    

    22. Graph Step

    V() 步骤通常用于启动GraphTraversal,但也可以在遍历中间使用。

    gremlin> g.V().has('name', within('marko', 'vadas', 'josh')).as('person').
               V().has('name', within('lop', 'ripple')).addE('uses').from('person')
    ==>e[13][1-uses->3]
    ==>e[14][1-uses->5]
    ==>e[15][2-uses->3]
    ==>e[16][2-uses->5]
    ==>e[17][4-uses->3]
    ==>e[18][4-uses->5]
    

    23. From Step

    from()不是一个真正的步骤,而是类似by()as()等的步骤调节器(step-modulator)。如果一个步骤可以接受一个遍历器或者String,那么可以用from()

    • 接受from()的步骤
    simplePath()
    cyclicPath()
    path()
    addE()
    
  • 相关阅读:
    asp.net(c#)网页跳转七种方法小结
    asp.net用Zxing库实现条形码输出的具体实现
    SQL中 patindex函数的用法
    escape()、encodeURI()、encodeURIComponent()区别详解
    sql语句分页代码
    memcache安装
    LVS和Haproxy机器必须注意的几个参数
    Redis 三主三从集群搭建
    mogodb安装步骤及注意事项
    系统故障等级和故障报告规定
  • 原文地址:https://www.cnblogs.com/myitroad/p/7778455.html
Copyright © 2020-2023  润新知