Difference between revisions of "Gremlin"
(119 intermediate revisions by the same user not shown) | |||
Line 89: | Line 89: | ||
As explained in {{Link|target=Gremlin_Basics}}: "The Gremlin graph traversal language defines approximately 30 steps which can be understood as the instruction set of the Gremlin traversal machine. | As explained in {{Link|target=Gremlin_Basics}}: "The Gremlin graph traversal language defines approximately 30 steps which can be understood as the instruction set of the Gremlin traversal machine. | ||
− | A regular computer has a CPU with an Instruction Pointer which tells the machine to take the instruction at that memory address and execute it next. There are also | + | A regular computer has a CPU with an Instruction Pointer which tells the machine to take the instruction at that memory address and execute it next. There are also instructions that can manipulate the instruction pointer with the effect of the return from a function or a goto to a different part of the program. |
Gremlin instead works on a sequence of steps and each step the "graph traversal machine" will take it's current state and execute the step to reach a new state of affairs. | Gremlin instead works on a sequence of steps and each step the "graph traversal machine" will take it's current state and execute the step to reach a new state of affairs. | ||
Line 95: | Line 95: | ||
The gremlin steps are useful in practice, with typically only 10 or so of them being applied in the majority of cases. Each of the provided steps can be understood as being a specification of one of the 5 general types enumerated below". | The gremlin steps are useful in practice, with typically only 10 or so of them being applied in the majority of cases. Each of the provided steps can be understood as being a specification of one of the 5 general types enumerated below". | ||
http://tinkerpop.apache.org/docs/3.4.1/images/step-types.png | http://tinkerpop.apache.org/docs/3.4.1/images/step-types.png | ||
+ | == Alphabetical table of Steps == | ||
+ | There are {{#ask: [[Concept:Step]]|format=count}} Steps described on this page | ||
+ | {{StepRow|#userparam=intro}} | ||
+ | {{#ask: [[Concept:Step]] | ||
+ | |mainlabel=- | ||
+ | | ?Step name = name | ||
+ | | ?Step kind = kind | ||
+ | | ?Step reference = reference | ||
+ | | ?Step javadoc = javadoc | ||
+ | | ?Step text = text | ||
+ | |format=template | ||
+ | |template=StepRow | ||
+ | |userparam=row | ||
+ | |link=none | ||
+ | |named args=yes | ||
+ | |sort=Step name | ||
+ | |order=ascending | ||
+ | |limit=100 | ||
+ | }} | ||
+ | {{StepRow|#userparam=outro}} | ||
+ | |||
== Stephierarchy == | == Stephierarchy == | ||
All steps are based on five {{Link|target=Gremlin#General_Steps|title=general steps}}. | All steps are based on five {{Link|target=Gremlin#General_Steps|title=general steps}}. | ||
Line 102: | Line 123: | ||
rankdir="LR"; | rankdir="LR"; | ||
step [ URL="[[Gremlin#Steps|steps]]" ] | step [ URL="[[Gremlin#Steps|steps]]" ] | ||
+ | subgraph cluster_terminal { | ||
+ | label="terminal steps"; | ||
+ | graph[style=dotted]; | ||
+ | hasNext [ URL="[[Gremlin#hasNext Step|hasNext]]" ] | ||
+ | next [ URL="[[Gremlin#next Step|next]]" ] | ||
+ | tryNext [ URL="[[Gremlin#tryNext Step|tryNext]]" ] | ||
+ | toList [ URL="[[Gremlin#toList Step|toList]]" ] | ||
+ | toSet [ URL="[[Gremlin#toSet Step|toSet]]" ] | ||
+ | toBulkSet [ URL="[[Gremlin#toBulkSet Step|toBulkSet]]" ] | ||
+ | fill [ URL="[[Gremlin#fill Step|fill]]" ] | ||
+ | promise [ URL="[[Gremlin#promise Step|promise]]" ] | ||
+ | explain [ URL="[[Gremlin#explain Step|explain]]" ] | ||
+ | iterate [ URL="[[Gremlin#iterate Step|iterate]]" ] | ||
+ | |||
+ | hasNext -> terminal | ||
+ | next -> terminal | ||
+ | tryNext -> terminal | ||
+ | toList -> terminal | ||
+ | toSet -> terminal | ||
+ | toBulkSet -> terminal | ||
+ | fill -> terminal | ||
+ | iterate -> terminal | ||
+ | promise -> terminal | ||
+ | explain -> terminal | ||
+ | terminal -> step | ||
+ | } | ||
+ | subgraph cluster_general { | ||
+ | label="general steps"; | ||
+ | graph[style=dotted]; | ||
+ | map [ URL="[[Gremlin#map Step|map]]" ] | ||
+ | flatMap [ URL="[[Gremlin#flatMap Step|flatMap]]" ] | ||
+ | filter [ URL="[[Gremlin#filter Step|filter]]" ] | ||
+ | sideEffect [ URL="[[Gremlin#sideEffect Step|sideEffect]]" ] | ||
+ | branch [ URL="[[Gremlin#branch Step|branch]]" ] | ||
− | map [ URL="[[Gremlin# | + | map -> general |
− | + | flatMap -> general | |
− | + | filter -> general | |
− | + | sideEffect -> general | |
− | + | branch -> general | |
+ | general -> step | ||
+ | } | ||
+ | subgraph cluster_map { | ||
+ | label="map steps"; | ||
+ | graph[style=dotted]; | ||
+ | id [ URL="[[Gremlin#id Step|id]]" ] | ||
+ | label [ label="label" URL="[[Gremlin#label Step|label]]" ] | ||
+ | match [ URL="[[Gremlin#match Step|match]]" ] | ||
+ | |||
+ | path [ URL="[[Gremlin#path Step|path]]" ] | ||
+ | select [ URL="[[Gremlin#select Step|select]]" ] | ||
+ | order [ URL="[[Gremlin#order Step|order]]" ] | ||
− | + | id->map | |
− | + | label->map | |
− | + | match->map | |
− | + | path->map | |
− | + | select->map | |
+ | order->map | ||
+ | subgraph cluster_barrier { | ||
+ | label="barrier steps"; | ||
+ | graph[style=dotted]; | ||
+ | max [ URL="[[Gremlin#max Step|max]]" ] | ||
+ | min [ URL="[[Gremlin#min Step|min]]" ] | ||
+ | cap [ URL="[[Gremlin#cap Step|cap]]" ] | ||
+ | count [ URL="[[Gremlin#count Step|count]]" ] | ||
+ | sum [ URL="[[Gremlin#sum Step|sum]]" ] | ||
+ | mean [ URL="[[Gremlin#mean Step|mean]]" ] | ||
+ | fold [ URL="[[Gremlin#fold Step|fold]]" ] | ||
+ | cap -> barrier | ||
+ | count -> barrier | ||
+ | max -> barrier | ||
+ | min -> barrier | ||
+ | sum -> barrier | ||
+ | mean -> barrier | ||
+ | fold -> barrier | ||
+ | barrier -> map | ||
+ | } | ||
+ | |||
+ | } | ||
− | + | subgraph cluster_flatMap { | |
− | + | label="flatMap steps"; | |
− | + | graph[style=dotted]; | |
+ | |||
+ | in [ URL="[[Gremlin#in Step|in]]" ] | ||
+ | out [ URL="[[Gremlin#out Step|out]]" ] | ||
+ | both [ URL="[[Gremlin#both Step|both]]" ] | ||
+ | |||
+ | inE [ URL="[[Gremlin#inE Step|inE]]" ] | ||
+ | outE [ URL="[[Gremlin#outE Step|outE]]" ] | ||
+ | bothE [ URL="[[Gremlin#bothE Step|bothE]]" ] | ||
+ | |||
+ | inV [ URL="[[Gremlin#inV Step|inV]]" ] | ||
+ | outV [ URL="[[Gremlin#outV Step|outV]]" ] | ||
+ | bothV [ URL="[[Gremlin#bothV Step|bothV]]" ] | ||
+ | |||
+ | in -> flatMap | ||
+ | out -> flatMap | ||
+ | both -> flatMap | ||
+ | |||
+ | inE -> flatMap | ||
+ | outE -> flatMap | ||
+ | bothE -> flatMap | ||
+ | |||
+ | inV -> flatMap | ||
+ | outV -> flatMap | ||
+ | bothV -> flatMap | ||
+ | |||
+ | coalesce -> flatMap | ||
+ | } | ||
− | + | subgraph cluster_filter{ | |
− | + | label="filter steps"; | |
− | + | graph[style=dotted]; | |
− | + | and [ URL="[[Gremlin#and Step|and]]" ] | |
− | label-> | + | coin [ URL="[[Gremlin#coin Step|coin]]" ] |
− | + | has [ URL="[[Gremlin#has Step|has]]" ] | |
− | + | is [ URL="[[Gremlin#is Step|is]]" ] | |
− | + | limit [ URL="[[Gremlin#limit Step|limit]]" ] | |
− | + | or [ URL="[[Gremlin#or Step|or]]" ] | |
+ | range [ URL="[[Gremlin#range Step|range]]" ] | ||
+ | tail [ URL="[[Gremlin#tail Step|tail]]" ] | ||
+ | where [ URL="[[Gremlin#where Step|where]]" ] | ||
+ | |||
+ | and -> filter | ||
+ | coin -> filter | ||
+ | has -> filter | ||
+ | is -> filter | ||
+ | limit -> filter | ||
+ | or -> filter | ||
+ | range -> filter | ||
+ | tail -> filter | ||
+ | where -> filter | ||
+ | } | ||
+ | |||
+ | subgraph cluster_sideEffect { | ||
+ | label="sideEffect steps"; | ||
+ | graph[style=dotted]; | ||
+ | |||
+ | addE [ URL="[[Gremlin#addE Step|addE]]" ] | ||
+ | addV [ URL="[[Gremlin#addV Step|addV]]" ] | ||
+ | property [ URL="[[Gremlin#property Step|property]]" ] | ||
+ | aggregate [ URL="[[Gremlin#aggregate Step|aggregate]]" ] | ||
+ | |||
+ | addE -> sideEffect | ||
+ | addV -> sideEffect | ||
+ | property -> sideEffect | ||
+ | aggregate -> sideEffect | ||
+ | inject -> sideEffect | ||
+ | profile -> sideEffect | ||
+ | property -> sideEffect | ||
+ | sg [ label="subgraph" ] | ||
+ | sg -> sideEffect | ||
+ | } | ||
+ | subgraph cluster_branch { | ||
+ | label="branch steps"; | ||
+ | graph[style=dotted]; | ||
+ | choose [ URL="[[Gremlin#choose Step|choose]]" ] | ||
+ | repeat [ URL="[[Gremlin#repeat Step|repeat]]" ] | ||
+ | union [ URL="[[Gremlin#union Step|union]]" ] | ||
− | + | choose -> branch | |
− | + | repeat -> branch | |
− | + | union -> branch | |
− | + | } | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
} | } | ||
</graphviz> | </graphviz> | ||
+ | |||
+ | == terminal Steps == | ||
+ | {{Step|name=hasNext|kind=terminal|reference=terminal-steps|level=2|text=determines whether there are available results|test=<source lang='java'> @Test | ||
+ | public void testHasNext() { | ||
+ | assertTrue(g().V(1).hasNext()); | ||
+ | assertFalse(g().V(7).hasNext()); | ||
+ | }</source> | ||
+ | }} | ||
+ | {{Step|name=next|kind=terminal|reference=terminal-steps|level=2|text=will return the next result.next(n) will return the next n results in a list|test=<source lang='java'> @Test | ||
+ | public void testNext() { | ||
+ | assertEquals("v[1]",g().V().next().toString()); | ||
+ | assertEquals("v[1]",g().V(1).next().toString()); | ||
+ | assertEquals("[v[1], v[2]]",g().V(1,2,3).next(2).toString()); | ||
+ | } | ||
+ | </source> | ||
+ | }} | ||
+ | {{Step|name=tryNext|kind=terminal|reference=terminal-steps|level=2|text=will return an Optional and thus, is a composite of hasNext()/next()|test=<source lang='java'> @Test | ||
+ | public void testTryNext() { | ||
+ | assertTrue(g().V(1).tryNext().isPresent()); | ||
+ | assertFalse(g().V(7).tryNext().isPresent()); | ||
+ | } | ||
+ | </source> | ||
+ | }} | ||
+ | {{Step|name=toList|kind=terminal|reference=terminal-steps|level=2|text=will return all results in a list|test=<source lang='java'> @Test | ||
+ | public void testToList() { | ||
+ | List<Vertex> vlist = g().V().toList(); | ||
+ | assertEquals("[v[1], v[2], v[3], v[4], v[5], v[6]]", vlist.toString()); | ||
+ | List<Edge> elist = g().E(7,8,9).toList(); | ||
+ | assertEquals( | ||
+ | "[e[7][1-knows->2], e[8][1-knows->4], e[9][1-created->3]]", | ||
+ | elist.toString()); | ||
+ | }</source>}} | ||
+ | {{Step|name=toSet|kind=terminal|reference=terminal-steps|level=2|text= will return all results in a set and thus, duplicates removed |test=<source lang='java'> @Test | ||
+ | public void testToSet() { | ||
+ | Set<Vertex> vset = g().V(1,2,2,3,4).toSet(); | ||
+ | assertEquals("[v[1], v[2], v[3], v[4]]", vset.toString()); | ||
+ | Set<Edge> set = g().E(7,8,9,7,8,9).toSet(); | ||
+ | assertEquals( | ||
+ | "[e[7][1-knows->2], e[8][1-knows->4], e[9][1-created->3]]", | ||
+ | set.toString()); | ||
+ | }</source>}} | ||
+ | {{Step|name=toBulkSet|kind=terminal|reference=terminal-steps|level=2|text=will return all results in a weighted set and thus, duplicates preserved via weighting|test=<source lang='java'> @Test | ||
+ | public void testToBulkSet() { | ||
+ | BulkSet<Vertex> vset = g().V(1,2,2,3,4).toBulkSet(); | ||
+ | assertEquals(2,vset.asBulk().get(g().V(2).next()).longValue()); | ||
+ | } | ||
+ | </source>}} | ||
+ | {{Step|name=fill|kind=terminal|reference=terminal-steps|level=2|text=fill(collection) will put all results in the provided collection and return the collection when complete.|test=<source lang='java'> @Test | ||
+ | public void testFill() { | ||
+ | List<Vertex> vlist=new LinkedList<Vertex>(); | ||
+ | List<Vertex> rvlist = g().V().fill(vlist); | ||
+ | assertEquals(vlist,rvlist); | ||
+ | assertEquals("[v[1], v[2], v[3], v[4], v[5], v[6]]", vlist.toString()); | ||
+ | } | ||
+ | </source>}} | ||
+ | {{Step|name=iterate|kind=terminal|reference=terminal-steps|level=2|text=Iterates the traversal presumably for the generation of side-effects. See https://stackoverflow.com/questions/47403296/iterate-step-is-used-in-the-end-of-the-command-when-creating-nodes-and-edges-t|test=<source lang='java'> @Test | ||
+ | public void testIterate() throws IOException { | ||
+ | // read and write without iterate doesn't have an effect | ||
+ | File kryoFile=File.createTempFile("modern", ".kryo"); | ||
+ | g().io(kryoFile.getPath()).write(); | ||
+ | GraphTraversalSource newg = TinkerGraph.open().traversal(); | ||
+ | newg.io(kryoFile.getPath()).read(); | ||
+ | assertEquals(0,newg.V().count().next().longValue()); | ||
+ | |||
+ | // read and write with iterate does really write and read | ||
+ | g().io(kryoFile.getPath()).write().iterate(); | ||
+ | newg = TinkerGraph.open().traversal(); | ||
+ | newg.io(kryoFile.getPath()).read().iterate(); | ||
+ | assertEquals(6,newg.V().count().next().longValue()); | ||
+ | }</source>}} | ||
+ | {{Step|name=promise|kind=terminal|reference=terminal-steps|level=2|text=can only be used with remote traversals to Gremlin Server or RGPs. It starts a promise to execute a function on the current Traversal that will be completed in the future.|test=<source lang='java'> @Test | ||
+ | public void testPromise() { | ||
+ | try { | ||
+ | CompletableFuture<Object> cf = g().V().promise(t -> t.next()); | ||
+ | cf.join(); | ||
+ | assertTrue(cf.isDone()); | ||
+ | } catch (Exception e) { | ||
+ | assertEquals( | ||
+ | "Only traversals created using withRemote() can be used in an async way", | ||
+ | e.getMessage()); | ||
+ | } | ||
+ | } | ||
+ | </source> | ||
+ | }} | ||
+ | {{Step|name=explain|kind=terminal|reference=terminal-steps|level=2|text=will return a TraversalExplanation. A traversal explanation details how the traversal (prior to explain()) will be compiled given the registered traversal strategies. A TraversalExplanation has a toString() representation with 3-columns. The first column is the traversal strategy being applied. The second column is the traversal strategy category: [D]ecoration, [O]ptimization, [P]rovider optimization, [F]inalization, and [V]erification. Finally, the third column is the state of the traversal post strategy application. The final traversal is the resultant execution plan.|test=<source lang='java'>@Test | ||
+ | public void testExplain() { | ||
+ | TraversalExplanation te = g().V().explain(); | ||
+ | assertEquals("Traversal Explanation\n" | ||
+ | + "===============================================================\n" | ||
+ | + "Original Traversal [GraphStep(vertex,[])]\n" + "\n" | ||
+ | + "ConnectiveStrategy [D] [GraphStep(vertex,[])]\n" | ||
+ | + "CountStrategy [O] [GraphStep(vertex,[])]\n" | ||
+ | + "IncidentToAdjacentStrategy [O] [GraphStep(vertex,[])]\n" | ||
+ | + "RepeatUnrollStrategy [O] [GraphStep(vertex,[])]\n" | ||
+ | + "MatchPredicateStrategy [O] [GraphStep(vertex,[])]\n" | ||
+ | + "PathRetractionStrategy [O] [GraphStep(vertex,[])]\n" | ||
+ | + "FilterRankingStrategy [O] [GraphStep(vertex,[])]\n" | ||
+ | + "InlineFilterStrategy [O] [GraphStep(vertex,[])]\n" | ||
+ | + "AdjacentToIncidentStrategy [O] [GraphStep(vertex,[])]\n" | ||
+ | + "LazyBarrierStrategy [O] [GraphStep(vertex,[])]\n" | ||
+ | + "TinkerGraphCountStrategy [P] [GraphStep(vertex,[])]\n" | ||
+ | + "TinkerGraphStepStrategy [P] [TinkerGraphStep(vertex,[])]\n" | ||
+ | + "ProfileStrategy [F] [TinkerGraphStep(vertex,[])]\n" | ||
+ | + "StandardVerificationStrategy [V] [TinkerGraphStep(vertex,[])]\n" | ||
+ | + "\n" | ||
+ | + "Final Traversal [TinkerGraphStep(vertex,[])]", | ||
+ | te.toString()); | ||
+ | }</source>}} | ||
+ | |||
+ | == filter Steps == | ||
+ | {{Step|name=and|reference=and-step|kind=filter|javadoc=and-org.apache.tinkerpop.gremlin.process.traversal.Traversal...-|level=2|text=ensures that all provided traversals yield a result|test=<source lang='java'> @Test | ||
+ | public void testAnd() { | ||
+ | assertEquals("[marko]",g().V().and( | ||
+ | outE("knows"), | ||
+ | values("age").is(lt(30))). | ||
+ | values("name").toList().toString()); | ||
+ | }</source>}} | ||
+ | {{Step|name=coin|reference=coin-step|kind=filter|javadoc=coin-double-|level=2|text=randomly filters out traversers with the given probability|test=<source lang='java'>@Test | ||
+ | public void testCoin() { | ||
+ | // 0% chance | ||
+ | assertEquals("[]", g().V().coin(0.0).toList().toString()); | ||
+ | // 100% chance | ||
+ | assertEquals("[v[1], v[2], v[3], v[4], v[5], v[6]]", | ||
+ | g().V().coin(1.0).toList().toString()); | ||
+ | // 50 % chance | ||
+ | int tosses = 1000; | ||
+ | double sixsigma=0.33; // 1 out of a million chance that the average will deviate more than this | ||
+ | int sum = 0; | ||
+ | for (int i = 1; i <= tosses; i++) | ||
+ | sum += g().V().coin(0.5).toList().size(); | ||
+ | double avg = sum * 1.0 / tosses; | ||
+ | assertTrue(avg<3.0+sixsigma); | ||
+ | assertTrue(avg>3.0-sixsigma); | ||
+ | }</source> | ||
+ | }} | ||
+ | {{Step|name=has|reference=has-step|kind=filter|javadoc=has-java.lang.String-|level=2|text=filters vertices, edges, and vertex properties based on their properties. This step has quite a few variations.|test=<source lang='java'> @Test | ||
+ | public void testHas() { | ||
+ | assertEquals(6, g().V().has("name").count().next().longValue()); | ||
+ | assertEquals("[29, 27]", | ||
+ | (g().V().has("age", inside(20, 30)).values("age").toList().toString())); | ||
+ | assertEquals("[32, 35]", (g().V().has("age", outside(20, 30)).values("age") | ||
+ | .toList().toString())); | ||
+ | assertEquals("[{name=[marko], age=[29]}, {name=[josh], age=[32]}]", (g().V() | ||
+ | .has("name", within("josh", "marko")).valueMap().toList().toString())); | ||
+ | assertEquals("[lop, ripple]",g().V().hasNot("age").values("name").toList().toString()); | ||
+ | }</source> | ||
+ | }} | ||
+ | {{Step|name=is|reference=is-step|kind=filter|javadoc=is-org.apache.tinkerpop.gremlin.process.traversal.P-|level=2|text=filters elements that fullfill the given predicate. Variant: Filters elements that are equal to the given Object.|test=<source lang='java'> @Test | ||
+ | public void testIs() { | ||
+ | assertEquals("[32]", g().V().values("age").is(32).toList().toString()); | ||
+ | assertEquals("[29, 27]", | ||
+ | g().V().values("age").is(lte(30)).toList().toString()); | ||
+ | assertEquals("[32, 35]", | ||
+ | g().V().values("age").is(inside(30, 40)).toList().toString()); | ||
+ | assertEquals("[ripple]", g().V().where(in("created").count().is(1)) | ||
+ | .values("name").toList().toString()); | ||
+ | assertEquals("[lop]", g().V().where(in("created").count().is(gte(2))) | ||
+ | .values("name").toList().toString()); | ||
+ | assertEquals("[lop, ripple]", | ||
+ | g().V().where(in("created").values("age").mean().is(inside(30d, 35d))) | ||
+ | .values("name").toList().toString()); | ||
+ | }</source> | ||
+ | }} | ||
+ | {{Step|name=or|reference=or-step|kind=filter|javadoc=or-org.apache.tinkerpop.gremlin.process.traversal.Traversal...-|level=2|text=ensures that at least one of the provided traversals yield a result.|test=<source lang='java'> @Test | ||
+ | public void testOr() { | ||
+ | assertEquals("[marko, lop, josh, peter]", | ||
+ | g().V().or(outE("created"), inE("created").count().is(gt(1))) | ||
+ | .values("name").toList().toString()); | ||
+ | assertEquals("[vadas, peter]", | ||
+ | g().V().or(values("age").is(gt(33)), values("age").is(lt(29))) | ||
+ | .values("name").toList().toString()); | ||
+ | }</source> | ||
+ | }} | ||
+ | {{Step|name=limit|reference=limit-step|kind=filter|javadoc=|text=|test=}} | ||
+ | {{Step|name=range|reference=range-step|kind=filter|javadoc=|text=|test=}} | ||
+ | {{Step|name=tail|reference=tail-step|kind=filter|javadoc=|text=|test=}} | ||
+ | {{Step|name=where|reference=where-step|kind=filter|javadoc=|level=2|text=filters the current object based on either the object itself (Scope.local) or the path history of the object (Scope.global) (filter). This step is typically used in conjunction with either {{Link|target=#match Step|titel=match()-step}} or {{Link|target=#select Step|title=select()-step}}, but can be used in isolation.|test=<source lang='java'> @Test | ||
+ | public void testWhere() { | ||
+ | assertEquals("[v[4], v[6]]", g().V(1).as("a").out("created").in("created") | ||
+ | .where(neq("a")).toList().toString()); | ||
+ | String names[] = { "josh", "peter" }; | ||
+ | assertEquals("[josh, peter]", | ||
+ | g().withSideEffect("a", Arrays.asList(names)).V(1).out("created") | ||
+ | .in("created").values("name").where(within("a")).toList() | ||
+ | .toString()); | ||
+ | assertEquals("[josh]", | ||
+ | g().V(1).out("created").in("created") | ||
+ | .where(out("created").count().is(gt(1))).values("name").toList() | ||
+ | .toString()); | ||
+ | }</source> | ||
+ | }} | ||
+ | |||
+ | == Step Modulators == | ||
+ | {{Step|name=as|reference=as-step|kind=modulator|javadoc=as-java.lang.String-java.lang.String...-|level=2|text=is not a real step, but a "step modulator" similar to by() and option(). With as(), it is possible to provide a label to the step that can later be accessed by steps and data structures that make use of such labels — e.g., select(), match(), and path|test=<source lang='java'> @Test | ||
+ | public void testAs() { | ||
+ | assertEquals( | ||
+ | "[{a=v[1], b=v[3]}, {a=v[4], b=v[5]}, {a=v[4], b=v[3]}, {a=v[6], b=v[3]}]", | ||
+ | g().V().as("a").out("created").as("b").select("a", "b").toList() | ||
+ | .toString()); | ||
+ | assertEquals( | ||
+ | "[{a=marko, b=lop}, {a=josh, b=ripple}, {a=josh, b=lop}, {a=peter, b=lop}]", | ||
+ | g().V().as("a").out("created").as("b").select("a", "b").by("name") | ||
+ | .toList().toString()); | ||
+ | }</source>}} | ||
+ | {{Step|name=by|reference=by-step|kind=modulator|javadoc=by--|level=2|text=is not an actual step, but instead is a "step-modulator" similar to as() and option(). If a step is able to accept traversals, functions, comparators, etc. then by() is the means by which they are added. The general pattern is step().by()…by(). Some steps can only accept one by() while others can take an arbitrary amount.|test=<source lang='java'> @Test | ||
+ | public void testBy() { | ||
+ | assertEquals("[{1=[v[2], v[5], v[6]], 3=[v[1], v[3], v[4]]}]", | ||
+ | g().V().group().by(bothE().count()).toList().toString()); | ||
+ | assertEquals("[{1=[vadas, ripple, peter], 3=[marko, lop, josh]}]", | ||
+ | g().V().group().by(bothE().count()).by("name").toList().toString()); | ||
+ | assertEquals("[{1=3, 3=3}]", | ||
+ | g().V().group().by(bothE().count()).by(count()).toList().toString()); | ||
+ | }</source>}} | ||
+ | {{Step|name=emit|reference=emit-step|kind=modulator|javadoc=emit--|level=2|text=is not an actual step, but is instead a step modulator for {{Link|target=#repeat Step|title=repeat()}} (find more documentation on the emit() there).|test=<source lang='java'>@Test | ||
+ | public void testEmit() { | ||
+ | assertEquals( | ||
+ | "[path[marko, lop], path[marko, vadas], path[marko, josh], path[marko, josh, ripple], path[marko, josh, lop]]", | ||
+ | g().V(1).repeat(out()).times(2).emit().path().by("name").toList() | ||
+ | .toString()); | ||
+ | assertEquals( | ||
+ | "[path[marko], path[marko, lop], path[marko, vadas], path[marko, josh], path[marko, josh, ripple], path[marko, josh, lop]]", | ||
+ | g().V(1).emit().repeat(out()).times(2).path().by("name").toList() | ||
+ | .toString()); | ||
+ | assertEquals("[path[marko, lop], path[marko, josh, ripple], path[marko, josh, lop]]",g().V(1).repeat(out()).times(2).emit(has("lang")).path() | ||
+ | .by("name").toList().toString()); | ||
+ | assertEquals("[path[marko, lop], path[marko, vadas], path[marko, josh], path[marko, josh, ripple], path[marko, josh, lop]]",g().V(1).repeat(out()).times(2).emit().path().by("name") | ||
+ | .toList().toString()); | ||
+ | }</source> | ||
+ | }} | ||
+ | {{Step|name=option|reference=option-step|kind=modulator|javadoc=|level=2|text=An option to a {{Link|target=#branch Step|title=branch()}} or {{Link|target=#choose Step|title=choose()}}|test=<source lang='java'></source>}} | ||
== map Steps == | == map Steps == | ||
− | = | + | {{Step|name=id|reference=id-step|kind=map|level=2|text=maps the traversal to the ids of the current elements. |test=<source lang='java'> |
− | |||
− | <source lang='java'> | ||
@Test | @Test | ||
public void testId() { | public void testId() { | ||
Line 174: | Line 536: | ||
} | } | ||
</source> | </source> | ||
+ | }} | ||
− | = | + | {{Step|name=label|reference=label-step|kind=map|level=2|text=maps the traversal to the labels of the current elements.|test= |
− | |||
<source lang='java'> | <source lang='java'> | ||
@Test | @Test | ||
Line 188: | Line 550: | ||
} | } | ||
</source> | </source> | ||
+ | }} | ||
+ | {{Step|name=match|reference=match-step|kind=map|level=2|text=see https://stackoverflow.com/questions/55609832/is-threre-a-document-about-how-gremlin-match-works|test=}} | ||
+ | {{Step|name=path|reference=path-step|kind=map|level=2|text=|test=}} | ||
+ | {{Step|name=select|reference=select-step|kind=map|level=2|text=|test=}} | ||
+ | {{Step|name=order|reference=order-step|javadoc=order--,order-org.apache.tinkerpop.gremlin.process.traversal.Scope-|kind=map|level=2|text=orders the traversal elements|test=<source lang='java'> @Test | ||
+ | public void testOrder() { | ||
+ | assertEquals("[josh, lop, marko, peter, ripple, vadas]", | ||
+ | g().V().values("name").order().toList().toString()); | ||
+ | assertEquals("[vadas, ripple, peter, marko, lop, josh]", | ||
+ | g().V().values("name").order().by(Order.desc).toList().toString()); | ||
+ | assertEquals("[vadas, marko, josh, peter]", g().V().hasLabel("person") | ||
+ | .order().by("age", Order.asc).values("name").toList().toString()); | ||
+ | }</source> | ||
+ | }} | ||
+ | === barrier Steps === | ||
+ | {{Step|name=cap|reference=cap-step|javadoc=cap-java.lang.String-java.lang.String...-|kind=barrier|level=3|text=Iterates the traversal up to the itself and emits the side-effect referenced by the key. If multiple keys are supplied then the side-effects are emitted as a Map.|test=<source lang='java'> @Test | ||
+ | public void testCap() { | ||
+ | assertEquals("[{software=2, person=4}]", | ||
+ | g().V().groupCount("a").by(label()).cap("a").toList().toString()); | ||
+ | assertEquals("[{a={software=2, person=4}, b={0=3, 1=1, 2=1, 3=1}}]",g().V().groupCount("a").by(label()).groupCount("b") | ||
+ | .by(outE().count()).cap("a", "b").toList().toString()); | ||
+ | } | ||
+ | </source> | ||
+ | }} | ||
+ | {{Step|name=count|reference=count-step|javadoc=count--|kind=reducing barrier|level=3|text=counts the total number of represented traversers in the streams (i.e. the bulk count).|test=<source lang='java'>@Test | ||
+ | public void testCount() { | ||
+ | assertEquals(6,g().V().count().next().longValue()); | ||
+ | assertEquals(4,g().V().hasLabel("person").count().next().intValue()); | ||
+ | assertEquals(2,g().V().hasLabel("software").count().next().intValue()); | ||
+ | assertEquals(4,g().E().hasLabel("created").count().next().intValue()); | ||
+ | assertEquals(2,g().E().hasLabel("knows").count().next().intValue()); | ||
+ | }</source> | ||
+ | }} | ||
+ | {{Step|name=min|reference=min-step|kind=reducing barrier|level=3|text=operates on a stream of comparable objects and determines which is the first object according to its natural order in the stream.|test=<source lang='java'>@Test | ||
+ | public void testMin() { | ||
+ | assertEquals(27,g().V().values("age").min().next()); | ||
+ | assertEquals(0.2,g().E().values("weight").min().next()); | ||
+ | assertEquals("josh",g().V().values("name").min().next()); | ||
+ | }</source> | ||
+ | }} | ||
+ | {{Step|name=max|reference=max-step|kind=reducing barrier|level=3|text=operates on a stream of comparable objects and determines which is the last object according to its natural order in the stream.|test=<source lang='java'>@Test | ||
+ | public void testMax() { | ||
+ | assertEquals(35,g().V().values("age").max().next()); | ||
+ | assertEquals(1.0,g().E().values("weight").max().next()); | ||
+ | assertEquals("vadas",g().V().values("name").max().next()); | ||
+ | }</source> | ||
+ | }} | ||
+ | {{Step|name=mean|reference=mean-step|kind=reducing barrier|level=3|text=operates on a stream of numbers and determines the average of those numbers.|test=<source lang='java'>@Test | ||
+ | public void testMean() { | ||
+ | assertEquals(30.75, g().V().values("age").mean().next()); | ||
+ | assertEquals(0.583, g().E().values("weight").mean().next().doubleValue(), | ||
+ | 0.001); | ||
+ | try { | ||
+ | assertEquals("josh", g().V().values("name").mean().next()); | ||
+ | } catch (Exception e) { | ||
+ | assertEquals("java.lang.String cannot be cast to java.lang.Number",e.getMessage()); | ||
+ | } | ||
+ | }</source> | ||
+ | }} | ||
+ | {{Step|name=sum|reference=sum-step|kind=reducing barrier|level=3|text=operates on a stream of numbers and sums the numbers together to yield a result|test=<source lang='java'>@Test | ||
+ | public void testSum() { | ||
+ | assertEquals(123, g().V().values("age").sum().next().intValue()); | ||
+ | assertEquals(3.5, g().E().values("weight").sum().next().doubleValue(),0.01); | ||
+ | }</source> | ||
+ | }} | ||
+ | {{Step|name=fold|reference=fold-step|kind=reducing barrier|level=3|text=There are situations when the traversal stream needs a "barrier" to aggregate all the objects and emit a computation that is a function of the aggregate. The fold()-step (map) is one particular instance of this. Please see unfold()-step for the inverse functionality.|test=<source lang='java'>@Test | ||
+ | public void testFold() { | ||
+ | List<Object> knowsList1 = g().V(1).out("knows").values("name").fold().next(); | ||
+ | assertEquals("[vadas, josh]",knowsList1.toString()); | ||
+ | }</source> | ||
+ | }} | ||
− | === | + | == flatMap Steps == |
− | + | {{Step|name=in|kind=flatMap|level=2|text=maps the current elements to the vertices at the end of the ingoing edges.|test= | |
+ | <source lang='java'> | ||
+ | @Test | ||
+ | public void testIn() { | ||
+ | assertEquals("[v[1], v[1], v[4], v[6], v[1], v[4]]", | ||
+ | g().V().in().toList().toString()); | ||
+ | assertEquals("[v[1], v[4], v[6], v[4]]", | ||
+ | g().V().in("created").toList().toString()); | ||
+ | assertEquals("[v[1], v[1]]", g().V().in("knows").toList().toString()); | ||
+ | assertEquals("[v[1], v[1], v[4], v[6], v[1], v[4]]", | ||
+ | g().V().in("created","knows").toList().toString()); | ||
+ | } | ||
+ | </source> | ||
+ | }} | ||
+ | {{Step|name=out|kind=flatMap|level=2|text=maps the current elements to the vertices at the end of the outgoing edges.|test= | ||
+ | <source lang='java'> | ||
+ | @Test | ||
+ | public void testOut() { | ||
+ | assertEquals("[v[3], v[2], v[4], v[5], v[3], v[3]]", | ||
+ | g().V().out().toList().toString()); | ||
+ | assertEquals("[v[3], v[5], v[3], v[3]]", | ||
+ | g().V().out("created").toList().toString()); | ||
+ | assertEquals("[v[2], v[4]]", g().V().out("knows").toList().toString()); | ||
+ | assertEquals("[v[3], v[2], v[4], v[5], v[3], v[3]]", | ||
+ | g().V().out("created","knows").toList().toString()); | ||
+ | } | ||
+ | </source> | ||
+ | }} | ||
+ | {{Step|name=both|kind=flatMap|level=2|text=maps the current elements to the vertices at the boths ends of the edges.|test= | ||
+ | <source lang='java'> | ||
+ | @Test | ||
+ | public void testBoth() { | ||
+ | assertEquals("[v[5], v[3], v[1]]", | ||
+ | g().V(4).both().toList().toString()); | ||
+ | assertEquals("[v[5], v[3]]", | ||
+ | g().V(4).both("created").toList().toString()); | ||
+ | assertEquals("[v[1]]", g().V(4).both("knows").toList().toString()); | ||
+ | assertEquals("[v[5], v[3], v[1]]", | ||
+ | g().V(4).both("created","knows").toList().toString()); | ||
+ | } | ||
+ | </source> | ||
+ | }} | ||
+ | {{Step|name=inE|reference=|kind=flatMap|level=2|text=maps the current elements to the the ingoing edges.|test= | ||
+ | <source lang='java'> | ||
+ | @Test | ||
+ | public void testInE() { | ||
+ | assertEquals( | ||
+ | "[e[7][1-knows->2], e[9][1-created->3], e[11][4-created->3], e[12][6-created->3], e[8][1-knows->4], e[10][4-created->5]]", | ||
+ | g().V().inE().toList().toString()); | ||
+ | assertEquals( | ||
+ | "[e[9][1-created->3], e[11][4-created->3], e[12][6-created->3], e[10][4-created->5]]", | ||
+ | g().V().inE("created").toList().toString()); | ||
+ | assertEquals("[e[7][1-knows->2], e[8][1-knows->4]]", | ||
+ | g().V().inE("knows").toList().toString()); | ||
+ | assertEquals( | ||
+ | "[e[7][1-knows->2], e[9][1-created->3], e[11][4-created->3], e[12][6-created->3], e[8][1-knows->4], e[10][4-created->5]]", | ||
+ | g().V().inE("created", "knows").toList().toString()); | ||
+ | } | ||
+ | </source> | ||
+ | }} | ||
+ | {{Step|name=outE|reference=|kind=flatMap|level=2|text=maps the current elements to the the outgoing edges.|test= | ||
+ | <source lang='java'> | ||
+ | @Test | ||
+ | public void testOutE() { | ||
+ | assertEquals( | ||
+ | "[e[9][1-created->3], e[7][1-knows->2], e[8][1-knows->4]]", | ||
+ | g().V(1).outE().toList().toString()); | ||
+ | assertEquals( | ||
+ | "[e[9][1-created->3]]", | ||
+ | g().V(1).outE("created").toList().toString()); | ||
+ | assertEquals("[e[7][1-knows->2], e[8][1-knows->4]]", | ||
+ | g().V(1).outE("knows").toList().toString()); | ||
+ | assertEquals( | ||
+ | "[e[9][1-created->3], e[7][1-knows->2], e[8][1-knows->4]]", | ||
+ | g().V(1).outE("created", "knows").toList().toString()); | ||
+ | } | ||
+ | </source> | ||
+ | }} | ||
+ | {{Step|name=bothE|reference=|kind=flatMap|level=2|text=maps the current elements to both the in and outgoing edges.|test= | ||
+ | <source lang='java'> | ||
+ | @Test | ||
+ | public void testBothE() { | ||
+ | assertEquals("[e[10][4-created->5], e[11][4-created->3], e[8][1-knows->4]]", | ||
+ | g().V(4).bothE().toList().toString()); | ||
+ | assertEquals("[e[10][4-created->5], e[11][4-created->3]]", | ||
+ | g().V(4).bothE("created").toList().toString()); | ||
+ | assertEquals("[e[8][1-knows->4]]", | ||
+ | g().V(4).bothE("knows").toList().toString()); | ||
+ | assertEquals("[e[10][4-created->5], e[11][4-created->3], e[8][1-knows->4]]", | ||
+ | g().V(4).bothE("created", "knows").toList().toString()); | ||
+ | } | ||
+ | </source> | ||
+ | }} | ||
+ | {{Step|name=inV|reference=|kind=flatMap|level=2|text=maps the current edges to the the ingoing Vertices.|test= | ||
+ | <source lang='java'> | ||
+ | @Test | ||
+ | public void testInV() { | ||
+ | assertEquals("[v[2], v[4], v[3], v[5], v[3], v[3]]", | ||
+ | g().E().inV().toList().toString()); | ||
+ | assertEquals("[v[3]]", g().E(9).inV().toList().toString()); | ||
+ | } | ||
+ | </source> | ||
+ | }} | ||
+ | {{Step|name=outV|reference=|kind=flatMap|level=2|text=The outV step maps the current edges to the outgoing Vertices.|test= | ||
+ | <source lang='java'> | ||
+ | @Test | ||
+ | public void testOutV() { | ||
+ | assertEquals("[v[1], v[1], v[1], v[4], v[4], v[6]]", | ||
+ | g().E().outV().toList().toString()); | ||
+ | assertEquals("[v[1]]", g().E(9).outV().toList().toString()); | ||
+ | } | ||
+ | </source> | ||
+ | }} | ||
+ | {{Step|name=bothV|reference=|kind=flatMap|level=2|text=maps the current edges to both the ingoing and outgoing Vertices.|test= | ||
+ | <source lang='java'> | ||
+ | @Test | ||
+ | public void testBothV() { | ||
+ | assertEquals("[v[4], v[3]]", | ||
+ | g().E(11).bothV().toList().toString()); | ||
+ | assertEquals("[v[1], v[3]]", g().E(9).bothV().toList().toString()); | ||
+ | } | ||
+ | </source>}} | ||
+ | {{Step|name=coalesce|reference=coalesce-step|kind=flatMap|level=2|text=The coalesce()-step evaluates the provided traversals in order and returns the first traversal that emits at least one element. | ||
+ | }} | ||
− | === | + | == Side Effect Steps == |
− | === | + | {{Step|name=addE|kind=sideEffect|reference=addedge-step|javadoc=addE-java.lang.String-|level=2|text=is used to add edges to the graph|test=<source lang='java'> @Test |
− | === | + | public void testAddE() { |
+ | Vertex marko = g().V().has("name","marko").next(); | ||
+ | Vertex peter = g().V().has("name","peter").next(); | ||
+ | assertEquals("e[13][1-knows->6]",g().V(marko).addE("knows").to(peter).next().toString()); | ||
+ | assertEquals("e[13][1-knows->6]",g().addE("knows").from(marko).to(peter).next().toString()); | ||
+ | }</source>}} | ||
+ | {{Step|name=addV|kind=sideEffect|reference=addvertex-step|javadoc=addV-java.lang.String-|level=2|text=is used to add vertices to the graph|test=<source lang='java'> @Test | ||
+ | public void testAddV() { | ||
+ | assertEquals("[marko, vadas, lop, josh, ripple, peter, stephen]", | ||
+ | g().addV("person").property("name", "stephen").V().values("name").toList() | ||
+ | .toString()); | ||
+ | }</source>}} | ||
+ | {{Step|name=property|kind=sideEffect|reference=addproperty-step|javadoc=property-java.lang.Object-java.lang.Object-java.lang.Object...-|level=2|text=is used to add properties to the elements of the graph|test=<source lang='java'> @Test | ||
+ | public void testProperty() { | ||
+ | assertEquals("[v[3]]",g().V().has("name","lop").property("version","1.0").V().has("version").toList().toString()); | ||
+ | } | ||
+ | </source>}} | ||
+ | {{Step|name=aggregate|kind=sideEffect|reference=aggregate-step|javadoc=aggregate-java.lang.String-|level=2|text=is used to aggregate all the objects at a particular point of traversal into a Collection|test=<source lang='java'> @Test | ||
+ | public void testAggregate() { | ||
+ | assertEquals("[ripple]",g().V(1).out("created").aggregate("x").in("created").out("created"). | ||
+ | where(without("x")).values("name").toList().toString()); | ||
+ | assertEquals("[{vadas=1, josh=1}]",g().V().out("knows").aggregate("x").by("name").cap("x").toList().toString()); | ||
+ | } | ||
+ | </source>}} | ||
+ | |||
+ | == Branch Steps == | ||
+ | {{Step|name=choose|kind=branch|reference=choose-step|javadoc=choose-java.util.function.Function-,choose-java.util.function.Predicate-org.apache.tinkerpop.gremlin.process.traversal.Traversal-|level=2|text=routes the current traverser to a particular traversal branch option. With choose(), it is possible to implement if/then/else-semantics as well as more complicated selections.|test=<source lang='java'> @Test | ||
+ | public void testChoose() { | ||
+ | assertEquals("[marko, ripple, lop, lop]", | ||
+ | g().V().hasLabel("person") | ||
+ | .choose(values("age").is(lte(30)), in(), out()).values("name") | ||
+ | .toList().toString()); | ||
+ | assertEquals("[marko, ripple, lop]", | ||
+ | g().V().hasLabel("person").choose(values("age")).option(27, in()) | ||
+ | .option(32, out()).values("name").toList().toString()); | ||
+ | }</source>}} | ||
+ | {{Step|name=repeat|kind=branch|reference=repeat-step|javadoc=repeat-org.apache.tinkerpop.gremlin.process.traversal.Traversal-,repeat-java.lang.String-org.apache.tinkerpop.gremlin.process.traversal.Traversal-|level=2|text=is used for looping over a traversal given some break predicate|test=<source lang='java'> @Test | ||
+ | public void testRepeat() { | ||
+ | assertEquals("[path[marko, josh, ripple], path[marko, josh, lop]]", | ||
+ | g().V(1).repeat(out()).times(2).path().by("name").toList().toString()); | ||
+ | |||
+ | assertEquals("[path[marko, josh, ripple], path[josh, ripple], path[ripple]]",g().V().until(has("name", "ripple")).repeat(out()).path() | ||
+ | .by("name").toList().toString()); | ||
+ | }</source>}} | ||
+ | {{Step|name=union|kind=branch|reference=repeat-step|javadoc=|level=2|text=|test=<source lang='java'></source>}} | ||
== General Steps == | == General Steps == | ||
− | |||
− | |||
− | <source lang='java'> | + | {{Step|name=filter|kind=general|reference=general-steps|level=2|text=Continues processing based on the given filter condition.|test=<source lang='java'> |
@Test | @Test | ||
public void testFilter() { | public void testFilter() { | ||
Line 211: | Line 809: | ||
There are 3 vertices having outgoing edges and 4 vertices having incoming edges in the modern example graph. | There are 3 vertices having outgoing edges and 4 vertices having incoming edges in the modern example graph. | ||
There are 4 edges having a weight>=0.4; | There are 4 edges having a weight>=0.4; | ||
+ | }} | ||
− | === | + | {{Step|name=map|kind=general|reference=general-steps|level=2|text= |
− | + | transforms the current step element to a new element (which may be empty). | |
− | see also https://stackoverflow.com/questions/51015636/in-gremlin-how-does-map-really-work | + | see also https://stackoverflow.com/questions/51015636/in-gremlin-how-does-map-really-work|test=<source lang='java'> @Test |
− | |||
− | <source lang='java'> | ||
− | |||
public void testMap() { | public void testMap() { | ||
assertEquals(6,g().V().map(values("name")).count().next().longValue()); | assertEquals(6,g().V().map(values("name")).count().next().longValue()); | ||
Line 234: | Line 830: | ||
There are 2 vertices with the lang property having the value "java".There are 3 vertices having out edges. The toList() call returns a list of Edges. | There are 2 vertices with the lang property having the value "java".There are 3 vertices having out edges. The toList() call returns a list of Edges. | ||
There are 2 edges having a weight of 0.4. The map step toList() returns a list of the edges for this last example (which are returned as generic objects). | There are 2 edges having a weight of 0.4. The map step toList() returns a list of the edges for this last example (which are returned as generic objects). | ||
− | + | }} | |
− | === | + | {{Step|name=flatMap|kind=general|reference=general-steps|level=2|text= |
− | + | transforms the current step in a one to many fashion.|test=<source lang='java'> | |
− | |||
− | <source lang='java'> | ||
@Test | @Test | ||
public void testflatMap() { | public void testflatMap() { | ||
Line 254: | Line 848: | ||
</source> | </source> | ||
Note the difference to the testMap step. Only the outE() parameter behaves different. In the map() case only the first Edge is considered - in the flatMap case all edges are considered. | Note the difference to the testMap step. Only the outE() parameter behaves different. In the map() case only the first Edge is considered - in the flatMap case all edges are considered. | ||
− | + | }} | |
− | === | + | {{Step|name=sideEffect|kind=general|reference=general-steps|level=2|text=performs some operation on the traverser and passes it to the next step.|test=<source lang='java'> |
− | |||
− | |||
− | = | ||
− | <source lang='java'> | ||
@Test | @Test | ||
public void testSideEffect() { | public void testSideEffect() { | ||
Line 267: | Line 857: | ||
</source> | </source> | ||
The sideffect in this example JUnit test case adds edges "on the fly". | The sideffect in this example JUnit test case adds edges "on the fly". | ||
− | + | }} | |
− | === | + | {{Step|name=branch|kind=general|reference=general-steps|level=2|text= |
− | + | Splits the traverser|test=<source lang='java'> | |
− | |||
− | <source lang='java'> | ||
@Test | @Test | ||
public void testBranch() { | public void testBranch() { | ||
Line 277: | Line 865: | ||
} | } | ||
</source> | </source> | ||
+ | }} | ||
+ | Author: {{Link|target=Wolfgang Fahl}} | ||
= What links here = | = What links here = | ||
Line 299: | Line 889: | ||
= Traversing Graphs with Gremlin = | = Traversing Graphs with Gremlin = | ||
<youtube>mZmVnEzsDnY</youtube> | <youtube>mZmVnEzsDnY</youtube> | ||
+ | [[Category:Gremlin]][[Category:DBIS-VL]] |
Latest revision as of 14:40, 18 February 2023
Gremlin is the graph traversal language of Apache TinkerPop. Gremlin is a functional, data-flow language that enables users to succinctly express complex traversals on (or queries of) their application's property graph. Every Gremlin traversal is composed of a sequence of (potentially nested) steps. A step performs an atomic operation on the data stream. Every step is either a map-step (transforming the objects in the stream), a filter-step (removing objects from the stream), or a sideEffect-step (computing statistics about the stream). The Gremlin step library extends on these 3-fundamental operations to provide users a rich collection of steps that they can compose in order to ask any conceivable question they may have of their data for Gremlin is Turing Complete.
Explaining Gremlin
There are different levels on which gremlin can be explained:
- Mathematical background as explained in Marko Rodriguez's paper The Gremlin Graph Traversal Machine and Language
- Generic API as explained in the Tinkerpop documentation
- Specific API (Java) as explained in the Javadocs page
- Specific "modern" Example mostly used for tests and explanations regarding Gremlin
On this page the goal is to cover all 4 levels with a focus on Java being applied to the modern example. The source code TestSteps.java is available on github.
Graph
A Graph G= (V, E) consist of a finite set of vertices V and a finite set of edges E ⊆ V×V.
An Element of a Graph is either a vertice or an edge.
A Propertygraph allows all elements (vertice or edge) of a graph to have properties. Each property is a name/value pair.
The Modern example
The "modern" graph is shipped with gremlin as a standard example.
The graph has 6 edges and 6 vertices.
It consists of :
- vertice person (name: marko, age:29)
- vertice person (name: vadas, age:27)
- vertice software (name: lop, lang: java)
- vertice person (name: josh, age:32)
- vertice software (name: ripple, lang: java)
- vertice person (name: peter, age:35)
- edge knows 1->2 (weight: 0.5)
- edge knows 1->4 (weight: 1.0)
- edge created 1->3 (weight: 0.4)
- edge created 4->5 (weight: 1.0)
- edge created 4->3 (weight: 0.4)
- edge created 6->3 (weight: 0.2)
In Gremlin edges and vertices have a set of properties. Each property is a name/value pair. One important property is the id of a vertice or edge. E.g. the vertice for peter has the id 6 and a property with the name "age" and the value 35 and another property with the name "name" and the value "peter".
GraphTraversal
One of the core concepts of tinkerpop/gremlin is the GraphTraversal It's interface has a generic definition as:
public interface GraphTraversal<S,E> extends Traversal<S,E>
and at https://markorodriguez.com/ the Author Marko Rodriguez explains the ideas behind using an generic approach vor handling Graphs. The Java implementation is available on github.
S is a generic Start class, and E is a generic End class as explained in the Apache Tinkerpop documentation.
GraphTraversalSource
A Graph Traversal Source is the starting point for working with a graph. The convention is to name this starting point
g
or
g()
In our tests we'll use a GraphTraversalSource for the modern example
/**
* common access to GraphTraversalSource
* @return - the graph traversal
*/
public GraphTraversalSource g() {
Graph graph = TinkerFactory.createModern();
GraphTraversalSource g = graph.traversal();
return g;
}
JUnit Testcase
@Test
public void testTraversal() {
assertEquals(6,g().E().count().next().longValue());
assertEquals(6,g().V().count().next().longValue());
}
E() gives you access to the edges of a graph traversal. V() gives you access to the vertices of a graph traversal. In the above example we simply count the edges and vertices and check our assumption that there are 6 edges and 6 vertices in the modern example graph.
Steps
As explained in Gremlin_Basics: "The Gremlin graph traversal language defines approximately 30 steps which can be understood as the instruction set of the Gremlin traversal machine.
A regular computer has a CPU with an Instruction Pointer which tells the machine to take the instruction at that memory address and execute it next. There are also instructions that can manipulate the instruction pointer with the effect of the return from a function or a goto to a different part of the program.
Gremlin instead works on a sequence of steps and each step the "graph traversal machine" will take it's current state and execute the step to reach a new state of affairs.
The gremlin steps are useful in practice, with typically only 10 or so of them being applied in the majority of cases. Each of the provided steps can be understood as being a specification of one of the 5 general types enumerated below".
Alphabetical table of Steps
There are 58 Steps described on this page
name | kind | reference | javadoc | text |
---|---|---|---|---|
addE | sideEffect | addedge-step | addE | is used to add edges to the graph |
addV | sideEffect | addvertex-step | addV | is used to add vertices to the graph |
aggregate | sideEffect | aggregate-step | aggregate | is used to aggregate all the objects at a particular point of traversal into a Collection |
and | filter | and-step | and | ensures that all provided traversals yield a result |
as | modulator | as-step | as | is not a real step, but a "step modulator" similar to by() and option(). With as(), it is possible to provide a label to the step that can later be accessed by steps and data structures that make use of such labels — e.g., select(), match(), and path |
both | flatMap | maps the current elements to the vertices at the boths ends of the edges. | ||
bothE | flatMap | maps the current elements to both the in and outgoing edges. | ||
bothV | flatMap | maps the current edges to both the ingoing and outgoing Vertices. | ||
branch | general | general-steps | Splits the traverser | |
by | modulator | by-step | by | is not an actual step, but instead is a "step-modulator" similar to as() and option(). If a step is able to accept traversals, functions, comparators, etc. then by() is the means by which they are added. The general pattern is step().by()…by(). Some steps can only accept one by() while others can take an arbitrary amount. |
cap | barrier | cap-step | cap | Iterates the traversal up to the itself and emits the side-effect referenced by the key. If multiple keys are supplied then the side-effects are emitted as a Map. |
choose | branch | choose-step | choose | routes the current traverser to a particular traversal branch option. With choose(), it is possible to implement if/then/else-semantics as well as more complicated selections. |
coalesce | flatMap | coalesce-step | The coalesce()-step evaluates the provided traversals in order and returns the first traversal that emits at least one element. | |
coin | filter | coin-step | coin | randomly filters out traversers with the given probability |
count | reducing barrier | count-step | count | counts the total number of represented traversers in the streams (i.e. the bulk count). |
emit | modulator | emit-step | emit | is not an actual step, but is instead a step modulator for repeat() (find more documentation on the emit() there). |
explain | terminal | terminal-steps | will return a TraversalExplanation. A traversal explanation details how the traversal (prior to explain()) will be compiled given the registered traversal strategies. A TraversalExplanation has a toString() representation with 3-columns. The first column is the traversal strategy being applied. The second column is the traversal strategy category: [D]ecoration, [O]ptimization, [P]rovider optimization, [F]inalization, and [V]erification. Finally, the third column is the state of the traversal post strategy application. The final traversal is the resultant execution plan. | |
fill | terminal | terminal-steps | fill(collection) will put all results in the provided collection and return the collection when complete. | |
filter | general | general-steps | Continues processing based on the given filter condition. | |
flatMap | general | general-steps | transforms the current step in a one to many fashion. | |
fold | reducing barrier | fold-step | There are situations when the traversal stream needs a "barrier" to aggregate all the objects and emit a computation that is a function of the aggregate. The fold()-step (map) is one particular instance of this. Please see unfold()-step for the inverse functionality. | |
has | filter | has-step | has | filters vertices, edges, and vertex properties based on their properties. This step has quite a few variations. |
hasNext | terminal | terminal-steps | determines whether there are available results | |
id | map | id-step | maps the traversal to the ids of the current elements. | |
in | flatMap | maps the current elements to the vertices at the end of the ingoing edges. | ||
inE | flatMap | maps the current elements to the the ingoing edges. | ||
inV | flatMap | maps the current edges to the the ingoing Vertices. | ||
is | filter | is-step | is | filters elements that fullfill the given predicate. Variant: Filters elements that are equal to the given Object. |
iterate | terminal | terminal-steps | Iterates the traversal presumably for the generation of side-effects. See https://stackoverflow.com/questions/47403296/iterate-step-is-used-in-the-end-of-the-command-when-creating-nodes-and-edges-t | |
label | map | label-step | maps the traversal to the labels of the current elements. | |
limit | filter | limit-step | ||
map | general | general-steps | transforms the current step element to a new element (which may be empty).
see also https://stackoverflow.com/questions/51015636/in-gremlin-how-does-map-really-work | |
match | map | match-step | see https://stackoverflow.com/questions/55609832/is-threre-a-document-about-how-gremlin-match-works | |
max | reducing barrier | max-step | operates on a stream of comparable objects and determines which is the last object according to its natural order in the stream. | |
mean | reducing barrier | mean-step | operates on a stream of numbers and determines the average of those numbers. | |
min | reducing barrier | min-step | operates on a stream of comparable objects and determines which is the first object according to its natural order in the stream. | |
next | terminal | terminal-steps | will return the next result.next(n) will return the next n results in a list | |
option | modulator | option-step | An option to a branch() or choose() | |
or | filter | or-step | or | ensures that at least one of the provided traversals yield a result. |
order | map | order-step | order | orders the traversal elements |
out | flatMap | maps the current elements to the vertices at the end of the outgoing edges. | ||
outE | flatMap | maps the current elements to the the outgoing edges. | ||
outV | flatMap | The outV step maps the current edges to the outgoing Vertices. | ||
path | map | path-step | ||
promise | terminal | terminal-steps | can only be used with remote traversals to Gremlin Server or RGPs. It starts a promise to execute a function on the current Traversal that will be completed in the future. | |
property | sideEffect | addproperty-step | property | is used to add properties to the elements of the graph |
range | filter | range-step | ||
repeat | branch | repeat-step | repeat | is used for looping over a traversal given some break predicate |
select | map | select-step | ||
sideEffect | general | general-steps | performs some operation on the traverser and passes it to the next step. | |
sum | reducing barrier | sum-step | operates on a stream of numbers and sums the numbers together to yield a result | |
tail | filter | tail-step | ||
toBulkSet | terminal | terminal-steps | will return all results in a weighted set and thus, duplicates preserved via weighting | |
toList | terminal | terminal-steps | will return all results in a list | |
toSet | terminal | terminal-steps | will return all results in a set and thus, duplicates removed | |
tryNext | terminal | terminal-steps | will return an Optional and thus, is a composite of hasNext()/next() | |
union | branch | repeat-step | ||
where | filter | where-step | filters the current object based on either the object itself (Scope.local) or the path history of the object (Scope.global) (filter). This step is typically used in conjunction with either #match Step or select()-step, but can be used in isolation. |
Stephierarchy
All steps are based on five general steps. Click on any of the steps below to see the explanation for the step
terminal Steps
hasNext Step
The hasNext step determines whether there are available results
@Test
public void testHasNext() {
assertTrue(g().V(1).hasNext());
assertFalse(g().V(7).hasNext());
}
next Step
The next step will return the next result.next(n) will return the next n results in a list
@Test
public void testNext() {
assertEquals("v[1]",g().V().next().toString());
assertEquals("v[1]",g().V(1).next().toString());
assertEquals("[v[1], v[2]]",g().V(1,2,3).next(2).toString());
}
tryNext Step
The tryNext step will return an Optional and thus, is a composite of hasNext()/next()
@Test
public void testTryNext() {
assertTrue(g().V(1).tryNext().isPresent());
assertFalse(g().V(7).tryNext().isPresent());
}
toList Step
The toList step will return all results in a list
@Test
public void testToList() {
List<Vertex> vlist = g().V().toList();
assertEquals("[v[1], v[2], v[3], v[4], v[5], v[6]]", vlist.toString());
List<Edge> elist = g().E(7,8,9).toList();
assertEquals(
"[e[7][1-knows->2], e[8][1-knows->4], e[9][1-created->3]]",
elist.toString());
}
toSet Step
The toSet step will return all results in a set and thus, duplicates removed
@Test
public void testToSet() {
Set<Vertex> vset = g().V(1,2,2,3,4).toSet();
assertEquals("[v[1], v[2], v[3], v[4]]", vset.toString());
Set<Edge> set = g().E(7,8,9,7,8,9).toSet();
assertEquals(
"[e[7][1-knows->2], e[8][1-knows->4], e[9][1-created->3]]",
set.toString());
}
toBulkSet Step
The toBulkSet step will return all results in a weighted set and thus, duplicates preserved via weighting
@Test
public void testToBulkSet() {
BulkSet<Vertex> vset = g().V(1,2,2,3,4).toBulkSet();
assertEquals(2,vset.asBulk().get(g().V(2).next()).longValue());
}
fill Step
The fill step fill(collection) will put all results in the provided collection and return the collection when complete.
@Test
public void testFill() {
List<Vertex> vlist=new LinkedList<Vertex>();
List<Vertex> rvlist = g().V().fill(vlist);
assertEquals(vlist,rvlist);
assertEquals("[v[1], v[2], v[3], v[4], v[5], v[6]]", vlist.toString());
}
iterate Step
The iterate step Iterates the traversal presumably for the generation of side-effects. See https://stackoverflow.com/questions/47403296/iterate-step-is-used-in-the-end-of-the-command-when-creating-nodes-and-edges-t
@Test
public void testIterate() throws IOException {
// read and write without iterate doesn't have an effect
File kryoFile=File.createTempFile("modern", ".kryo");
g().io(kryoFile.getPath()).write();
GraphTraversalSource newg = TinkerGraph.open().traversal();
newg.io(kryoFile.getPath()).read();
assertEquals(0,newg.V().count().next().longValue());
// read and write with iterate does really write and read
g().io(kryoFile.getPath()).write().iterate();
newg = TinkerGraph.open().traversal();
newg.io(kryoFile.getPath()).read().iterate();
assertEquals(6,newg.V().count().next().longValue());
}
promise Step
The promise step can only be used with remote traversals to Gremlin Server or RGPs. It starts a promise to execute a function on the current Traversal that will be completed in the future.
@Test
public void testPromise() {
try {
CompletableFuture<Object> cf = g().V().promise(t -> t.next());
cf.join();
assertTrue(cf.isDone());
} catch (Exception e) {
assertEquals(
"Only traversals created using withRemote() can be used in an async way",
e.getMessage());
}
}
explain Step
The explain step will return a TraversalExplanation. A traversal explanation details how the traversal (prior to explain()) will be compiled given the registered traversal strategies. A TraversalExplanation has a toString() representation with 3-columns. The first column is the traversal strategy being applied. The second column is the traversal strategy category: [D]ecoration, [O]ptimization, [P]rovider optimization, [F]inalization, and [V]erification. Finally, the third column is the state of the traversal post strategy application. The final traversal is the resultant execution plan.
@Test
public void testExplain() {
TraversalExplanation te = g().V().explain();
assertEquals("Traversal Explanation\n"
+ "===============================================================\n"
+ "Original Traversal [GraphStep(vertex,[])]\n" + "\n"
+ "ConnectiveStrategy [D] [GraphStep(vertex,[])]\n"
+ "CountStrategy [O] [GraphStep(vertex,[])]\n"
+ "IncidentToAdjacentStrategy [O] [GraphStep(vertex,[])]\n"
+ "RepeatUnrollStrategy [O] [GraphStep(vertex,[])]\n"
+ "MatchPredicateStrategy [O] [GraphStep(vertex,[])]\n"
+ "PathRetractionStrategy [O] [GraphStep(vertex,[])]\n"
+ "FilterRankingStrategy [O] [GraphStep(vertex,[])]\n"
+ "InlineFilterStrategy [O] [GraphStep(vertex,[])]\n"
+ "AdjacentToIncidentStrategy [O] [GraphStep(vertex,[])]\n"
+ "LazyBarrierStrategy [O] [GraphStep(vertex,[])]\n"
+ "TinkerGraphCountStrategy [P] [GraphStep(vertex,[])]\n"
+ "TinkerGraphStepStrategy [P] [TinkerGraphStep(vertex,[])]\n"
+ "ProfileStrategy [F] [TinkerGraphStep(vertex,[])]\n"
+ "StandardVerificationStrategy [V] [TinkerGraphStep(vertex,[])]\n"
+ "\n"
+ "Final Traversal [TinkerGraphStep(vertex,[])]",
te.toString());
}
filter Steps
and Step
The and step (javadoc)ensures that all provided traversals yield a result
@Test
public void testAnd() {
assertEquals("[marko]",g().V().and(
outE("knows"),
values("age").is(lt(30))).
values("name").toList().toString());
}
coin Step
The coin step (javadoc)randomly filters out traversers with the given probability
@Test
public void testCoin() {
// 0% chance
assertEquals("[]", g().V().coin(0.0).toList().toString());
// 100% chance
assertEquals("[v[1], v[2], v[3], v[4], v[5], v[6]]",
g().V().coin(1.0).toList().toString());
// 50 % chance
int tosses = 1000;
double sixsigma=0.33; // 1 out of a million chance that the average will deviate more than this
int sum = 0;
for (int i = 1; i <= tosses; i++)
sum += g().V().coin(0.5).toList().size();
double avg = sum * 1.0 / tosses;
assertTrue(avg<3.0+sixsigma);
assertTrue(avg>3.0-sixsigma);
}
has Step
The has step (javadoc)filters vertices, edges, and vertex properties based on their properties. This step has quite a few variations.
@Test
public void testHas() {
assertEquals(6, g().V().has("name").count().next().longValue());
assertEquals("[29, 27]",
(g().V().has("age", inside(20, 30)).values("age").toList().toString()));
assertEquals("[32, 35]", (g().V().has("age", outside(20, 30)).values("age")
.toList().toString()));
assertEquals("[{name=[marko], age=[29]}, {name=[josh], age=[32]}]", (g().V()
.has("name", within("josh", "marko")).valueMap().toList().toString()));
assertEquals("[lop, ripple]",g().V().hasNot("age").values("name").toList().toString());
}
is Step
The is step (javadoc)filters elements that fullfill the given predicate. Variant: Filters elements that are equal to the given Object.
@Test
public void testIs() {
assertEquals("[32]", g().V().values("age").is(32).toList().toString());
assertEquals("[29, 27]",
g().V().values("age").is(lte(30)).toList().toString());
assertEquals("[32, 35]",
g().V().values("age").is(inside(30, 40)).toList().toString());
assertEquals("[ripple]", g().V().where(in("created").count().is(1))
.values("name").toList().toString());
assertEquals("[lop]", g().V().where(in("created").count().is(gte(2)))
.values("name").toList().toString());
assertEquals("[lop, ripple]",
g().V().where(in("created").values("age").mean().is(inside(30d, 35d)))
.values("name").toList().toString());
}
or Step
The or step (javadoc)ensures that at least one of the provided traversals yield a result.
@Test
public void testOr() {
assertEquals("[marko, lop, josh, peter]",
g().V().or(outE("created"), inE("created").count().is(gt(1)))
.values("name").toList().toString());
assertEquals("[vadas, peter]",
g().V().or(values("age").is(gt(33)), values("age").is(lt(29)))
.values("name").toList().toString());
}
limit Step
The limit step
range Step
The range step
tail Step
The tail step
where Step
The where step filters the current object based on either the object itself (Scope.local) or the path history of the object (Scope.global) (filter). This step is typically used in conjunction with either #match Step or select()-step, but can be used in isolation.
@Test
public void testWhere() {
assertEquals("[v[4], v[6]]", g().V(1).as("a").out("created").in("created")
.where(neq("a")).toList().toString());
String names[] = { "josh", "peter" };
assertEquals("[josh, peter]",
g().withSideEffect("a", Arrays.asList(names)).V(1).out("created")
.in("created").values("name").where(within("a")).toList()
.toString());
assertEquals("[josh]",
g().V(1).out("created").in("created")
.where(out("created").count().is(gt(1))).values("name").toList()
.toString());
}
Step Modulators
as Step
The as step (javadoc)is not a real step, but a "step modulator" similar to by() and option(). With as(), it is possible to provide a label to the step that can later be accessed by steps and data structures that make use of such labels — e.g., select(), match(), and path
@Test
public void testAs() {
assertEquals(
"[{a=v[1], b=v[3]}, {a=v[4], b=v[5]}, {a=v[4], b=v[3]}, {a=v[6], b=v[3]}]",
g().V().as("a").out("created").as("b").select("a", "b").toList()
.toString());
assertEquals(
"[{a=marko, b=lop}, {a=josh, b=ripple}, {a=josh, b=lop}, {a=peter, b=lop}]",
g().V().as("a").out("created").as("b").select("a", "b").by("name")
.toList().toString());
}
by Step
The by step (javadoc)is not an actual step, but instead is a "step-modulator" similar to as() and option(). If a step is able to accept traversals, functions, comparators, etc. then by() is the means by which they are added. The general pattern is step().by()…by(). Some steps can only accept one by() while others can take an arbitrary amount.
@Test
public void testBy() {
assertEquals("[{1=[v[2], v[5], v[6]], 3=[v[1], v[3], v[4]]}]",
g().V().group().by(bothE().count()).toList().toString());
assertEquals("[{1=[vadas, ripple, peter], 3=[marko, lop, josh]}]",
g().V().group().by(bothE().count()).by("name").toList().toString());
assertEquals("[{1=3, 3=3}]",
g().V().group().by(bothE().count()).by(count()).toList().toString());
}
emit Step
The emit step (javadoc)is not an actual step, but is instead a step modulator for repeat() (find more documentation on the emit() there).
@Test
public void testEmit() {
assertEquals(
"[path[marko, lop], path[marko, vadas], path[marko, josh], path[marko, josh, ripple], path[marko, josh, lop]]",
g().V(1).repeat(out()).times(2).emit().path().by("name").toList()
.toString());
assertEquals(
"[path[marko], path[marko, lop], path[marko, vadas], path[marko, josh], path[marko, josh, ripple], path[marko, josh, lop]]",
g().V(1).emit().repeat(out()).times(2).path().by("name").toList()
.toString());
assertEquals("[path[marko, lop], path[marko, josh, ripple], path[marko, josh, lop]]",g().V(1).repeat(out()).times(2).emit(has("lang")).path()
.by("name").toList().toString());
assertEquals("[path[marko, lop], path[marko, vadas], path[marko, josh], path[marko, josh, ripple], path[marko, josh, lop]]",g().V(1).repeat(out()).times(2).emit().path().by("name")
.toList().toString());
}
option Step
The option step An option to a branch() or choose()
map Steps
id Step
The id step maps the traversal to the ids of the current elements.
@Test
public void testId() {
List<Object> vids = g().V().id().toList();
assertEquals(6,vids.size());
assertEquals("[1, 2, 3, 4, 5, 6]",vids.toString());
List<Object> eids = g().E().id().toList();
assertEquals(6,eids.size());
assertEquals("[7, 8, 9, 10, 11, 12]",eids.toString());
}
label Step
The label step maps the traversal to the labels of the current elements.
@Test
public void testLabel() {
List<String> vlabels = g().V().label().toList();
assertEquals(6,vlabels.size());
assertEquals("[person, person, software, person, software, person]",vlabels.toString());
List<String> elabels = g().E().label().toList();
assertEquals(6,elabels.size());
assertEquals("[knows, knows, created, created, created, created]",elabels.toString());
}
match Step
The match step see https://stackoverflow.com/questions/55609832/is-threre-a-document-about-how-gremlin-match-works
path Step
The path step
select Step
The select step
order Step
The order step (javadoc), (javadoc)orders the traversal elements
@Test
public void testOrder() {
assertEquals("[josh, lop, marko, peter, ripple, vadas]",
g().V().values("name").order().toList().toString());
assertEquals("[vadas, ripple, peter, marko, lop, josh]",
g().V().values("name").order().by(Order.desc).toList().toString());
assertEquals("[vadas, marko, josh, peter]", g().V().hasLabel("person")
.order().by("age", Order.asc).values("name").toList().toString());
}
barrier Steps
cap Step
The cap step (javadoc)Iterates the traversal up to the itself and emits the side-effect referenced by the key. If multiple keys are supplied then the side-effects are emitted as a Map.
@Test
public void testCap() {
assertEquals("[{software=2, person=4}]",
g().V().groupCount("a").by(label()).cap("a").toList().toString());
assertEquals("[{a={software=2, person=4}, b={0=3, 1=1, 2=1, 3=1}}]",g().V().groupCount("a").by(label()).groupCount("b")
.by(outE().count()).cap("a", "b").toList().toString());
}
count Step
The count step (javadoc)counts the total number of represented traversers in the streams (i.e. the bulk count).
@Test
public void testCount() {
assertEquals(6,g().V().count().next().longValue());
assertEquals(4,g().V().hasLabel("person").count().next().intValue());
assertEquals(2,g().V().hasLabel("software").count().next().intValue());
assertEquals(4,g().E().hasLabel("created").count().next().intValue());
assertEquals(2,g().E().hasLabel("knows").count().next().intValue());
}
min Step
The min step operates on a stream of comparable objects and determines which is the first object according to its natural order in the stream.
@Test
public void testMin() {
assertEquals(27,g().V().values("age").min().next());
assertEquals(0.2,g().E().values("weight").min().next());
assertEquals("josh",g().V().values("name").min().next());
}
max Step
The max step operates on a stream of comparable objects and determines which is the last object according to its natural order in the stream.
@Test
public void testMax() {
assertEquals(35,g().V().values("age").max().next());
assertEquals(1.0,g().E().values("weight").max().next());
assertEquals("vadas",g().V().values("name").max().next());
}
mean Step
The mean step operates on a stream of numbers and determines the average of those numbers.
@Test
public void testMean() {
assertEquals(30.75, g().V().values("age").mean().next());
assertEquals(0.583, g().E().values("weight").mean().next().doubleValue(),
0.001);
try {
assertEquals("josh", g().V().values("name").mean().next());
} catch (Exception e) {
assertEquals("java.lang.String cannot be cast to java.lang.Number",e.getMessage());
}
}
sum Step
The sum step operates on a stream of numbers and sums the numbers together to yield a result
@Test
public void testSum() {
assertEquals(123, g().V().values("age").sum().next().intValue());
assertEquals(3.5, g().E().values("weight").sum().next().doubleValue(),0.01);
}
fold Step
The fold step There are situations when the traversal stream needs a "barrier" to aggregate all the objects and emit a computation that is a function of the aggregate. The fold()-step (map) is one particular instance of this. Please see unfold()-step for the inverse functionality.
@Test
public void testFold() {
List<Object> knowsList1 = g().V(1).out("knows").values("name").fold().next();
assertEquals("[vadas, josh]",knowsList1.toString());
}
flatMap Steps
in Step
The in step maps the current elements to the vertices at the end of the ingoing edges.
@Test
public void testIn() {
assertEquals("[v[1], v[1], v[4], v[6], v[1], v[4]]",
g().V().in().toList().toString());
assertEquals("[v[1], v[4], v[6], v[4]]",
g().V().in("created").toList().toString());
assertEquals("[v[1], v[1]]", g().V().in("knows").toList().toString());
assertEquals("[v[1], v[1], v[4], v[6], v[1], v[4]]",
g().V().in("created","knows").toList().toString());
}
out Step
The out step maps the current elements to the vertices at the end of the outgoing edges.
@Test
public void testOut() {
assertEquals("[v[3], v[2], v[4], v[5], v[3], v[3]]",
g().V().out().toList().toString());
assertEquals("[v[3], v[5], v[3], v[3]]",
g().V().out("created").toList().toString());
assertEquals("[v[2], v[4]]", g().V().out("knows").toList().toString());
assertEquals("[v[3], v[2], v[4], v[5], v[3], v[3]]",
g().V().out("created","knows").toList().toString());
}
both Step
The both step maps the current elements to the vertices at the boths ends of the edges.
@Test
public void testBoth() {
assertEquals("[v[5], v[3], v[1]]",
g().V(4).both().toList().toString());
assertEquals("[v[5], v[3]]",
g().V(4).both("created").toList().toString());
assertEquals("[v[1]]", g().V(4).both("knows").toList().toString());
assertEquals("[v[5], v[3], v[1]]",
g().V(4).both("created","knows").toList().toString());
}
inE Step
The inE step maps the current elements to the the ingoing edges.
@Test
public void testInE() {
assertEquals(
"[e[7][1-knows->2], e[9][1-created->3], e[11][4-created->3], e[12][6-created->3], e[8][1-knows->4], e[10][4-created->5]]",
g().V().inE().toList().toString());
assertEquals(
"[e[9][1-created->3], e[11][4-created->3], e[12][6-created->3], e[10][4-created->5]]",
g().V().inE("created").toList().toString());
assertEquals("[e[7][1-knows->2], e[8][1-knows->4]]",
g().V().inE("knows").toList().toString());
assertEquals(
"[e[7][1-knows->2], e[9][1-created->3], e[11][4-created->3], e[12][6-created->3], e[8][1-knows->4], e[10][4-created->5]]",
g().V().inE("created", "knows").toList().toString());
}
outE Step
The outE step maps the current elements to the the outgoing edges.
@Test
public void testOutE() {
assertEquals(
"[e[9][1-created->3], e[7][1-knows->2], e[8][1-knows->4]]",
g().V(1).outE().toList().toString());
assertEquals(
"[e[9][1-created->3]]",
g().V(1).outE("created").toList().toString());
assertEquals("[e[7][1-knows->2], e[8][1-knows->4]]",
g().V(1).outE("knows").toList().toString());
assertEquals(
"[e[9][1-created->3], e[7][1-knows->2], e[8][1-knows->4]]",
g().V(1).outE("created", "knows").toList().toString());
}
bothE Step
The bothE step maps the current elements to both the in and outgoing edges.
@Test
public void testBothE() {
assertEquals("[e[10][4-created->5], e[11][4-created->3], e[8][1-knows->4]]",
g().V(4).bothE().toList().toString());
assertEquals("[e[10][4-created->5], e[11][4-created->3]]",
g().V(4).bothE("created").toList().toString());
assertEquals("[e[8][1-knows->4]]",
g().V(4).bothE("knows").toList().toString());
assertEquals("[e[10][4-created->5], e[11][4-created->3], e[8][1-knows->4]]",
g().V(4).bothE("created", "knows").toList().toString());
}
inV Step
The inV step maps the current edges to the the ingoing Vertices.
@Test
public void testInV() {
assertEquals("[v[2], v[4], v[3], v[5], v[3], v[3]]",
g().E().inV().toList().toString());
assertEquals("[v[3]]", g().E(9).inV().toList().toString());
}
outV Step
The outV step The outV step maps the current edges to the outgoing Vertices.
@Test
public void testOutV() {
assertEquals("[v[1], v[1], v[1], v[4], v[4], v[6]]",
g().E().outV().toList().toString());
assertEquals("[v[1]]", g().E(9).outV().toList().toString());
}
bothV Step
The bothV step maps the current edges to both the ingoing and outgoing Vertices.
@Test
public void testBothV() {
assertEquals("[v[4], v[3]]",
g().E(11).bothV().toList().toString());
assertEquals("[v[1], v[3]]", g().E(9).bothV().toList().toString());
}
coalesce Step
The coalesce step The coalesce()-step evaluates the provided traversals in order and returns the first traversal that emits at least one element.
Side Effect Steps
addE Step
The addE step (javadoc)is used to add edges to the graph
@Test
public void testAddE() {
Vertex marko = g().V().has("name","marko").next();
Vertex peter = g().V().has("name","peter").next();
assertEquals("e[13][1-knows->6]",g().V(marko).addE("knows").to(peter).next().toString());
assertEquals("e[13][1-knows->6]",g().addE("knows").from(marko).to(peter).next().toString());
}
addV Step
The addV step (javadoc)is used to add vertices to the graph
@Test
public void testAddV() {
assertEquals("[marko, vadas, lop, josh, ripple, peter, stephen]",
g().addV("person").property("name", "stephen").V().values("name").toList()
.toString());
}
property Step
The property step (javadoc)is used to add properties to the elements of the graph
@Test
public void testProperty() {
assertEquals("[v[3]]",g().V().has("name","lop").property("version","1.0").V().has("version").toList().toString());
}
aggregate Step
The aggregate step (javadoc)is used to aggregate all the objects at a particular point of traversal into a Collection
@Test
public void testAggregate() {
assertEquals("[ripple]",g().V(1).out("created").aggregate("x").in("created").out("created").
where(without("x")).values("name").toList().toString());
assertEquals("[{vadas=1, josh=1}]",g().V().out("knows").aggregate("x").by("name").cap("x").toList().toString());
}
Branch Steps
choose Step
The choose step (javadoc), (javadoc)routes the current traverser to a particular traversal branch option. With choose(), it is possible to implement if/then/else-semantics as well as more complicated selections.
@Test
public void testChoose() {
assertEquals("[marko, ripple, lop, lop]",
g().V().hasLabel("person")
.choose(values("age").is(lte(30)), in(), out()).values("name")
.toList().toString());
assertEquals("[marko, ripple, lop]",
g().V().hasLabel("person").choose(values("age")).option(27, in())
.option(32, out()).values("name").toList().toString());
}
repeat Step
The repeat step (javadoc), (javadoc)is used for looping over a traversal given some break predicate
@Test
public void testRepeat() {
assertEquals("[path[marko, josh, ripple], path[marko, josh, lop]]",
g().V(1).repeat(out()).times(2).path().by("name").toList().toString());
assertEquals("[path[marko, josh, ripple], path[josh, ripple], path[ripple]]",g().V().until(has("name", "ripple")).repeat(out()).path()
.by("name").toList().toString());
}
union Step
The union step
General Steps
filter Step
The filter step Continues processing based on the given filter condition.
@Test
public void testFilter() {
assertEquals(3,g().V().filter(out()).count().next().longValue());
assertEquals(4,g().V().filter(in()).count().next().longValue());
assertEquals(5,g().E().filter(values("weight").
is(P.gte(0.4))).count().next().longValue());
}
There are 3 vertices having outgoing edges and 4 vertices having incoming edges in the modern example graph. There are 4 edges having a weight>=0.4;
map Step
The map step transforms the current step element to a new element (which may be empty). see also https://stackoverflow.com/questions/51015636/in-gremlin-how-does-map-really-work
@Test
public void testMap() {
assertEquals(6,g().V().map(values("name")).count().next().longValue());
assertEquals(4,g().V().map(hasLabel("person")).count().next().longValue());
assertEquals(2,g().V().map(has("lang","java")).count().next().longValue());
List<Edge> outEdges = g().V().map(outE()).toList();
assertEquals(3,outEdges.size());
List<Object> edges = g().E().map(has("weight",0.4)).toList();
assertEquals(2,edges.size());
for (Object edge:edges) {
assertTrue(edge instanceof Edge);
}
}
There are 6 vertices having a name property. There are 4 vertices with a "person" label. There are 2 vertices with the lang property having the value "java".There are 3 vertices having out edges. The toList() call returns a list of Edges. There are 2 edges having a weight of 0.4. The map step toList() returns a list of the edges for this last example (which are returned as generic objects).
flatMap Step
The flatMap step transforms the current step in a one to many fashion.
@Test
public void testflatMap() {
assertEquals(6,g().V().flatMap(values("name")).count().next().longValue());
assertEquals(4,g().V().flatMap(hasLabel("person")).count().next().longValue());
assertEquals(2,g().V().flatMap(has("lang","java")).count().next().longValue());
List<Edge> outEdges = g().V().flatMap(outE()).toList();
assertEquals(6,outEdges.size());
List<Object> edges = g().E().flatMap(has("weight",0.4)).toList();
assertEquals(2,edges.size());
for (Object edge:edges) {
assertTrue(edge instanceof Edge);
}
}
Note the difference to the testMap step. Only the outE() parameter behaves different. In the map() case only the first Edge is considered - in the flatMap case all edges are considered.
sideEffect Step
The sideEffect step performs some operation on the traverser and passes it to the next step.
@Test
public void testSideEffect() {
assertEquals(6,g().V().sideEffect(addE("sideedge")).outE().
hasLabel("sideedge").count().next().longValue());
}
The sideffect in this example JUnit test case adds edges "on the fly".
branch Step
The branch step Splits the traverser
@Test
public void testBranch() {
}
Author: Wolfgang Fahl
What links here
Links
- That Conf - Graph Database - What, Why, How - Presentation by Andrew Glassmann
- Practical Gremlin: An Apache TinkerPop Tutorial by Kelvin Lawrence see also https://github.com/krlawrence/graph
- https://github.com/bechbd/gremlin-ide
- Tinkerpop
Stackoverflow Questions
Recipes
Practical Gremlin: An Apache TinkerPop Tutorial by Kelvin Lawrence
Traversing Graphs with Gremlin