遍歷策略
一個TraversalStrategy分析一個遍歷,如果遍歷符合它的標准,可以相應地改變它。遍歷策略在編譯時被執行,並構成Gremlin遍歷機的編譯器的基礎。有五類策略分列如下:
- decoration: 在應用程序級別的特性可以嵌入到遍歷邏輯中
- optimization: 在TinkerPop3級別有更高效的方式來表達遍歷
- provider optimization: 在圖的系統/語言/驅動程序級別上有一種更有效的方式來表示遍歷
- finalization: 執行遍歷之前需要進行一些最終的調整/清理/分析
- verification: 某些遍歷對於應用程序或遍歷引擎是不合法的
Note
explain()
步驟向用戶顯示每個注冊策略如何改變遍歷。
如:gremlin> g.V().has('name','marko').explain()
元素ID策略
ElementIdStrategy提供對元素標識符的控制。一些Graph實現(如TinkerGraph)允許在創建元素時指定自定義標識符:
gremlin> g = TinkerGraph.open().traversal()
==>graphtraversalsource[tinkergraph[vertices:0 edges:0], standard]
gremlin> v = g.addV().property(id,'42a').next()
==>v[42a]
gremlin> g.V('42a')
==>v[42a]
來源: http://tinkerpop.apache.org/docs/3.2.6/reference/#traversalstrategy
其他Graph實現(如Neo4j)會自動生成元素標識符,並且不能分配。作為一個幫手,可以使用ElementIdStrategy通過使用頂點和邊索引來使標識符賦值成為可能。
如:
gremlin> graph = Neo4jGraph.open('/tmp/neo4j')
==>neo4jgraph[Community [/tmp/neo4j]]
gremlin> strategy = ElementIdStrategy.build().create()
==>ElementIdStrategy
gremlin> g = graph.traversal().withStrategies(strategy)
==>graphtraversalsource[neo4jgraph[Community [/tmp/neo4j]], standard]
gremlin> g.addV().property(id, '42a').id()
==>42a
Note
用於存儲分配的標識符的key應該在底層圖形數據庫中建立索引。如果沒有建立索引,那么查找使用這些標識符的元素將執行線性掃描。
事件策略
EventStrategy的目的是在遍歷內發生對底層Graph的更改時,將事件引發到一個或多個MutationListener對象。這種策略對記錄更改,觸發基於更改的某些操作或在遍歷期間需要通知某些變異操作的任何應用程序非常有用。如果事務回滾,則重置事件隊列。
以下事件引發MutationListener:
New vertex
New edge
Vertex property changed
Edge property changed
Vertex property removed
Edge property removed
Vertex removed
Edge removed
要開始處理來自Traversal的事件,首先要實現MutationListener接口。此實現的一個示例是ConsoleMutationListener,它將輸出寫入每個事件的控制台。示例如下:
gremlin> graph = TinkerFactory.createModern()
==>tinkergraph[vertices:6 edges:6]
gremlin> l = new ConsoleMutationListener(graph)
==>MutationListener[tinkergraph[vertices:6 edges:6]]
gremlin> strategy = EventStrategy.build().addListener(l).create()
==>EventStrategy
gremlin> g = graph.traversal().withStrategies(strategy)
==>graphtraversalsource[tinkergraph[vertices:6 edges:6], standard]
gremlin> g.addV().property('name','stephen')
Vertex [v[13]] added to graph [tinkergraph[vertices:7 edges:6]]
==>v[13]
gremlin> g.E().drop()
Edge [e[7][1-knows->2]] removed from graph [tinkergraph[vertices:7 edges:6]]
Edge [e[8][1-knows->4]] removed from graph [tinkergraph[vertices:7 edges:5]]
Edge [e[9][1-created->3]] removed from graph [tinkergraph[vertices:7 edges:4]]
Edge [e[10][4-created->5]] removed from graph [tinkergraph[vertices:7 edges:3]]
Edge [e[11][4-created->3]] removed from graph [tinkergraph[vertices:7 edges:2]]
Edge [e[12][6-created->3]] removed from graph [tinkergraph[vertices:7 edges:1]]
Note
EventStrategy並不意味着用於跟蹤不同進程間的全局變化。換句話說,一個JVM進程中的突變不會作為不同JVM進程中的事件引發。
分區策略
PartitionStrategy將圖的頂點和邊分割成String命名的分區(如桶,子圖等)。
PartitionStrategy中有三種主要配置:
- 分區鍵(Partition Key) - 以字符串值的屬性key來表示的分區。
- 寫分區(Write Partition) - 一個字符串,表示將來所有未來寫入元素的分區。
- 讀分區(Read Partitions) - 一個字符串集合
Set<String>
表示可以讀取的分區。
使用分區策略的一個例子:
gremlin> graph = TinkerFactory.createModern()
==>tinkergraph[vertices:6 edges:6]
gremlin> strategyA = PartitionStrategy.build().partitionKey("_partition").writePartition("a").readPartitions("a").create()
==>PartitionStrategy
gremlin> strategyB = PartitionStrategy.build().partitionKey("_partition").writePartition("b").readPartitions("b").create()
==>PartitionStrategy
gremlin> gA = graph.traversal().withStrategies(strategyA)
==>graphtraversalsource[tinkergraph[vertices:6 edges:6], standard]
gremlin> gA.addV() // this vertex has a property of {_partition:"a"}
==>v[13]
gremlin> gB = graph.traversal().withStrategies(strategyB)
==>graphtraversalsource[tinkergraph[vertices:7 edges:6], standard]
gremlin> gB.addV() // this vertex has a property of {_partition:"b"}
==>v[15]
gremlin> gA.V()
==>v[13]
gremlin> gB.V()
==>v[15]
通過將元素寫入特定分區,然后限制讀取分區,開發人員可以在單個地址空間內創建多個圖形。此外,通過支持分區之間的引用,可以合並這些多個圖(即連接分區)。
Note
如果Graph可以支持元屬性,並且在構建PartitionStrategy時將includeMetaProperties值設置為true,則分區也可能擴展到VertexProperty元素。
只讀策略
ReadOnlyStrategy
如其名稱所示,如果Traversal內有任何改變的步驟,則應用此策略的遍歷將拋出IllegalStateException。
子圖策略
SubgraphStrategy
類似於PartitionStrategy,因為它限制了某些頂點,邊和頂點屬性的遍歷。
下例使用相同的查詢對是否使用子圖策略的兩種情景進行查詢,其中子圖策略為:創建一個SubgraphStrategy,其中頂點屬性不能有一個endTime屬性。
gremlin> graph = TinkerFactory.createTheCrew()
==>tinkergraph[vertices:6 edges:14]
gremlin> g = graph.traversal()
==>graphtraversalsource[tinkergraph[vertices:6 edges:14], standard]
gremlin> g.V().as('a').values('location').as('b'). //1\
select('a','b').by('name').by()
==>[a:marko,b:san diego]
==>[a:marko,b:santa cruz]
==>[a:marko,b:brussels]
==>[a:marko,b:santa fe]
==>[a:stephen,b:centreville]
==>[a:stephen,b:dulles]
==>[a:stephen,b:purcellville]
==>[a:matthias,b:bremen]
==>[a:matthias,b:baltimore]
==>[a:matthias,b:oakland]
==>[a:matthias,b:seattle]
==>[a:daniel,b:spremberg]
==>[a:daniel,b:kaiserslautern]
==>[a:daniel,b:aachen]
gremlin> g = g.withStrategies(SubgraphStrategy.build().vertexProperties(hasNot('endTime')).create()) //2\
==>graphtraversalsource[tinkergraph[vertices:6 edges:14], standard]
gremlin> g.V().as('a').values('location').as('b'). //3\
select('a','b').by('name').by()
==>[a:marko,b:santa fe]
==>[a:stephen,b:purcellville]
==>[a:matthias,b:seattle]
==>[a:daniel,b:aachen]
來源: http://tinkerpop.apache.org/docs/3.2.6/reference/#_subgraphstrategy
下面的示例使用所有三個過濾器:vertex,edge和vertex property。Vertices必須居住(location屬性)在三個以上的地方或者沒有居住信息,Edges必須標注為“develops”,VertexProperties必須是當前位置或沒有位置(location)屬性。
gremlin> graph = TinkerFactory.createTheCrew()
==>tinkergraph[vertices:6 edges:14]
gremlin> g = graph.traversal().withStrategies(SubgraphStrategy.build().
vertices(or(hasNot('location'),properties('location').count().is(gt(3)))).
edges(hasLabel('develops')).
vertexProperties(or(hasLabel(neq('location')),hasNot('endTime'))).create())
==>graphtraversalsource[tinkergraph[vertices:6 edges:14], standard]
gremlin> g.V().valueMap(true)
==>[name:[marko],label:person,location:[santa fe],id:1]
==>[name:[matthias],label:person,location:[seattle],id:8]
==>[name:[gremlin],label:software,id:10]
==>[name:[tinkergraph],label:software,id:11]
gremlin> g.E().valueMap(true)
==>[label:develops,id:13,since:2009]
==>[label:develops,id:14,since:2010]
==>[label:develops,id:21,since:2012]
gremlin> g.V().outE().inV().path().by('name').by(label).by('name')
==>[marko,develops,gremlin]
==>[marko,develops,tinkergraph]
==>[matthias,develops,gremlin]
gremlin>