圖遍歷步驟(Graph Traversal Steps)
在最一般的層次上,Traversal<S,E>實現了Iterator
- 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]
注意幾點:
- 上述
tryNext()
將返回一個Optional的,是一個hasNext()/next()的組合。 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()