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()


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM