Authorization · tinkerpop.apache.org

Gremlin steps are chained together to produce the actual traversal and are triggered by way of start steps on the GraphTraversalSource.

Important

More details about the Gremlin language can be found in the Provider Documentation within the Gremlin Semantics Section.

General Steps

There are five general steps, each having a traversal and a lambda representation, by which all other specific steps described later extend.

Step Description

map(Traversal<S, E>) map(Function<Traverser<S>, E>)

map the traverser to some object of type E for the next step to process.

flatMap(Traversal<S, E>) flatMap(Function<Traverser<S>, Iterator<E>>)

map the traverser to an iterator of E objects that are streamed to the next step.

filter(Traversal<?, ?>) filter(Predicate<Traverser<S>>)

map the traverser to either true or false, where false will not pass the traverser to the next step.

sideEffect(Traversal<S, S>) sideEffect(Consumer<Traverser<S>>)

perform some operation on the traverser and pass it to the next step.

branch(Traversal<S, M>) branch(Function<Traverser<S>,M>)

split the traverser to all the traversals indexed by the M token.

Warning

Lambda steps are presented for educational purposes as they represent the foundational constructs of the Gremlin language. In practice, lambda steps should be avoided in favor of their traversals representation and traversal verification strategies exist to disallow their use unless explicitly "turned off." For more information on the problems with lambdas, please read A Note on Lambdas.

The Traverser<S> object provides access to:

  1. The current traversed S object — Traverser.get().

  2. The current path traversed by the traverser — Traverser.path().

    1. A helper shorthand to get a particular path-history object — Traverser.path(String) == Traverser.path().get(String).

  3. The number of times the traverser has gone through the current loop — Traverser.loops().

  4. The number of objects represented by this traverser — Traverser.bulk().

  5. The local data structure associated with this traverser — Traverser.sack().

  6. The side-effects associated with the traversal — Traverser.sideEffects().

    1. A helper shorthand to get a particular side-effect — Traverser.sideEffect(String) == Traverser.sideEffects().get(String).

map lambda

console (groovy) groovy
gremlin> g.V(1).out().values('name') //// (1)
==>lop
==>vadas
==>josh
gremlin> g.V(1).out().map {it.get().value('name')} //// (2)
==>lop
==>vadas
==>josh
gremlin> g.V(1).out().map(values('name')) //// (3)
==>lop
==>vadas
==>josh
g.V(1).out().values('name') //// (1)
g.V(1).out().map {it.get().value('name')} //// (2)
g.V(1).out().map(values('name')) //3
  1. An outgoing traversal from vertex 1 to the name values of the adjacent vertices.

  2. The same operation, but using a lambda to access the name property values.

  3. Again the same operation, but using the traversal representation of map().

filter lambda

console (groovy) groovy
gremlin> g.V().filter {it.get().label() == 'person'} //// (1)
==>v[1]
==>v[2]
==>v[4]
==>v[6]
gremlin> g.V().filter(label().is('person')) //// (2)
==>v[1]
==>v[2]
==>v[4]
==>v[6]
gremlin> g.V().hasLabel('person') //// (3)
==>v[1]
==>v[2]
==>v[4]
==>v[6]
g.V().filter {it.get().label() == 'person'} //// (1)
g.V().filter(label().is('person')) //// (2)
g.V().hasLabel('person') //3
  1. A filter that only allows the vertex to pass if it has the "person" label

  2. The same operation, but using the traversal representation of filter().

  3. The more specific has()-step is implemented as a filter() with respective predicate.

side effect lambda

console (groovy) groovy
gremlin> g.V().hasLabel('person').sideEffect(System.out.&println) //// (1)
v[1]
==>v[1]
v[2]
==>v[2]
v[4]
==>v[4]
v[6]
==>v[6]
gremlin> g.V().sideEffect(outE().count().local(aggregate("o"))).
               sideEffect(inE().count().local(aggregate("i"))).cap("o","i") //// (2)
==>[i:[0,0,1,1,1,3],o:[3,0,0,0,2,1]]
g.V().hasLabel('person').sideEffect(System.out.&println) //// (1)
g.V().sideEffect(outE().count().local(aggregate("o"))).
      sideEffect(inE().count().local(aggregate("i"))).cap("o","i") //2
  1. Whatever enters sideEffect() is passed to the next step, but some intervening process can occur.

  2. Compute the out- and in-degree for each vertex. Both sideEffect() are fed with the same vertex.

branch lambda

console (groovy) groovy
gremlin> g.V().branch {it.get().value('name')}.
               option('marko', values('age')).
               option(none, values('name')) //// (1)
==>29
==>vadas
==>lop
==>josh
==>ripple
==>peter
gremlin> g.V().branch(values('name')).
               option('marko', values('age')).
               option(none, values('name')) //// (2)
==>29
==>vadas
==>lop
==>josh
==>ripple
==>peter
gremlin> g.V().choose(has('name','marko'),
                      values('age'),
                      values('name')) //// (3)
==>29
==>vadas
==>lop
==>josh
==>ripple
==>peter
g.V().branch {it.get().value('name')}.
      option('marko', values('age')).
      option(none, values('name')) //// (1)
g.V().branch(values('name')).
      option('marko', values('age')).
      option(none, values('name')) //// (2)
g.V().choose(has('name','marko'),
             values('age'),
             values('name')) //3
  1. If the vertex is "marko", get his age, else get the name of the vertex.

  2. The same operation, but using the traversal representing of branch().

  3. The more specific boolean-based choose()-step is implemented as a branch().

Terminal Steps

Typically, when a step is concatenated to a traversal a traversal is returned. In this way, a traversal is built up in a fluent, monadic fashion. However, some steps do not return a traversal, but instead, execute the traversal and return a result. These steps are known as terminal steps (terminal) and they are explained via the examples below.

console (groovy) groovy
gremlin> g.V().out('created').hasNext() //// (1)
==>true
gremlin> g.V().out('created').next() //// (2)
==>v[3]
gremlin> g.V().out('created').next(2) //// (3)
==>v[3]
==>v[5]
gremlin> g.V().out('nothing').tryNext() //// (4)
==>Optional.empty
gremlin> g.V().out('created').toList() //// (5)
==>v[3]
==>v[5]
==>v[3]
==>v[3]
gremlin> g.V().out('created').toSet() //// (6)
==>v[3]
==>v[5]
gremlin> g.V().out('created').toBulkSet() //// (7)
==>v[3]
==>v[3]
==>v[3]
==>v[5]
gremlin> results = ['blah',3]
==>blah
==>3
gremlin> g.V().out('created').fill(results) //// (8)
==>blah
==>3
==>v[3]
==>v[5]
==>v[3]
==>v[3]
gremlin> g.addV('person').iterate() //// (9)
g.V().out('created').hasNext() //// (1)
g.V().out('created').next() //// (2)
g.V().out('created').next(2) //// (3)
g.V().out('nothing').tryNext() //// (4)
g.V().out('created').toList() //// (5)
g.V().out('created').toSet() //// (6)
g.V().out('created').toBulkSet() //// (7)
results = ['blah',3]
g.V().out('created').fill(results) //// (8)
g.addV('person').iterate() //9
  1. hasNext() determines whether there are available results (not supported in gremlin-javascript).

  2. next() will return the next result.

  3. next(n) will return the next n results in a list (not supported in gremlin-javascript or Gremlin.NET).

  4. tryNext() will return an Optional and thus, is a composite of hasNext()/next() (only supported for JVM languages).

  5. toList() will return all results in a list.

  6. toSet() will return all results in a set and thus, duplicates removed (not supported in gremlin-javascript).

  7. toBulkSet() will return all results in a weighted set and thus, duplicates preserved via weighting (only supported for JVM languages).

  8. fill(collection) will put all results in the provided collection and return the collection when complete (only supported for JVM languages).

  9. iterate() does not exactly fit the definition of a terminal step in that it doesn’t return a result, but still returns a traversal - it does however behave as a terminal step in that it iterates the traversal and generates side effects without returning the actual result.

There is also the promise() terminator step, which 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.

Finally, explain()-step is also a terminal step and is described in its own section.

AddE Step

Reasoning is the process of making explicit what is implicit in the data. What is explicit in a graph are the objects of the graph — i.e. vertices and edges. What is implicit in the graph is the traversal. In other words, traversals expose meaning where the meaning is determined by the traversal definition. For example, take the concept of a "co-developer." Two people are co-developers if they have worked on the same project together. This concept can be represented as a traversal and thus, the concept of "co-developers" can be derived. Moreover, what was once implicit can be made explicit via the addE()-step (map/sideEffect).

addedge step

console (groovy) groovy
gremlin> g.V(1).as('a').out('created').in('created').where(neq('a')).
           addE('co-developer').from('a').property('year',2009) //// (1)
==>e[0][1-co-developer->4]
==>e[13][1-co-developer->6]
gremlin> g.V(3,4,5).aggregate('x').has('name','josh').as('a').
           select('x').unfold().hasLabel('software').addE('createdBy').to('a') //// (2)
==>e[14][3-createdBy->4]
==>e[15][5-createdBy->4]
gremlin> g.V().as('a').out('created').addE('createdBy').to('a').property('acl','public') //// (3)
==>e[16][3-createdBy->1]
==>e[17][5-createdBy->4]
==>e[18][3-createdBy->4]
==>e[19][3-createdBy->6]
gremlin> g.V(1).as('a').out('knows').
           addE('livesNear').from('a').property('year',2009).
           inV().inE('livesNear').values('year') //// (4)
==>2009
==>2009
gremlin> g.V().match(
                 __.as('a').out('knows').as('b'),
                 __.as('a').out('created').as('c'),
                 __.as('b').out('created').as('c')).
               addE('friendlyCollaborator').from('a').to('b').
                 property(id,23).property('project',select('c').values('name')) //// (5)
==>e[23][1-friendlyCollaborator->4]
gremlin> g.E(23).valueMap()
==>[project:lop]
gremlin> vMarko = g.V().has('name','marko').next()
==>v[1]
gremlin> vPeter = g.V().has('name','peter').next()
==>v[6]
gremlin> g.V(vMarko).addE('knows').to(vPeter) //// (6)
==>e[22][1-knows->6]
gremlin> g.addE('knows').from(vMarko).to(vPeter) //// (7)
==>e[24][1-knows->6]
gremlin> g.addE('knows').from(__.V(1)).to(__.constant(6)) //// (8)
==>e[25][1-knows->6]
g.V(1).as('a').out('created').in('created').where(neq('a')).
  addE('co-developer').from('a').property('year',2009) //// (1)
g.V(3,4,5).aggregate('x').has('name','josh').as('a').
  select('x').unfold().hasLabel('software').addE('createdBy').to('a') //// (2)
g.V().as('a').out('created').addE('createdBy').to('a').property('acl','public') //// (3)
g.V(1).as('a').out('knows').
  addE('livesNear').from('a').property('year',2009).
  inV().inE('livesNear').values('year') //// (4)
g.V().match(
        __.as('a').out('knows').as('b'),
        __.as('a').out('created').as('c'),
        __.as('b').out('created').as('c')).
      addE('friendlyCollaborator').from('a').to('b').
        property(id,23).property('project',select('c').values('name')) //// (5)
g.E(23).valueMap()
vMarko = g.V().has('name','marko').next()
vPeter = g.V().has('name','peter').next()
g.V(vMarko).addE('knows').to(vPeter) //// (6)
g.addE('knows').from(vMarko).to(vPeter) //// (7)
g.addE('knows').from(__.V(1)).to(__.constant(6)) //8
  1. Add a co-developer edge with a year-property between marko and his collaborators.

  2. Add incoming createdBy edges from the josh-vertex to the lop- and ripple-vertices.

  3. Add an inverse createdBy edge for all created edges.

  4. The newly created edge is a traversable object.

  5. Two arbitrary bindings in a traversal can be joined from()to(), where id can be provided for graphs that supports user provided ids.

  6. Add an edge between marko and peter given the directed (detached) vertex references.

  7. Add an edge between marko and peter given the directed (detached) vertex references.

  8. Use child traversals producing either a vertex, or vertex id to add an edge between marko and peter.

Additional References

AddV Step

The addV()-step is used to add vertices to the graph (map/sideEffect). For every incoming object, a vertex is created. Moreover, GraphTraversalSource maintains an addV() method.

console (groovy) groovy
gremlin> g.addV('person').property('name','stephen')
==>v[0]
gremlin> g.V().values('name')
==>stephen
==>marko
==>vadas
==>lop
==>josh
==>ripple
==>peter
gremlin> g.V().outE('knows').addV().property('name','nothing')
==>v[13]
==>v[15]
gremlin> g.V().has('name','nothing')
==>v[13]
==>v[15]
gremlin> g.V().has('name','nothing').bothE()
g.addV('person').property('name','stephen')
g.V().values('name')
g.V().outE('knows').addV().property('name','nothing')
g.V().has('name','nothing')
g.V().has('name','nothing').bothE()

Additional References

Aggregate Step

aggregate step

The aggregate()-step (sideEffect) is used to aggregate all the objects at a particular point of traversal into a Collection. By default, the step will use eager evaluation in that no objects continue on until all previous objects have been fully aggregated. The eager evaluation model is crucial in situations where everything at a particular point is required for future computation.

console (groovy) groovy
gremlin> g.V(1).out('created') //// (1)
==>v[3]
gremlin> g.V(1).out('created').aggregate('x') //// (2)
==>v[3]
gremlin> g.V(1).out('created').aggregate('x').in('created') //// (3)
==>v[1]
==>v[4]
==>v[6]
gremlin> g.V(1).out('created').aggregate('x').in('created').out('created') //// (4)
==>v[3]
==>v[5]
==>v[3]
==>v[3]
gremlin> g.V(1).out('created').aggregate('x').in('created').out('created').
                where(without('x')).values('name') //// (5)
==>ripple
g.V(1).out('created') //// (1)
g.V(1).out('created').aggregate('x') //// (2)
g.V(1).out('created').aggregate('x').in('created') //// (3)
g.V(1).out('created').aggregate('x').in('created').out('created') //// (4)
g.V(1).out('created').aggregate('x').in('created').out('created').
       where(without('x')).values('name') //5
  1. What has marko created?

  2. Aggregate all his creations.

  3. Who are marko’s collaborators?

  4. What have marko’s collaborators created?

  5. What have marko’s collaborators created that he hasn’t created?

"What has userA liked? Who else has liked those things? What have they liked that userA hasn't already liked?"

Finally, aggregate()-step can be modulated via by()-projection.

console (groovy) groovy
gremlin> g.V().out('knows').aggregate('x').cap('x')
==>[v[2],v[4]]
gremlin> g.V().out('knows').aggregate('x').by('name').cap('x')
==>[vadas,josh]
gremlin> g.V().out('knows').aggregate('x').by('age').cap('x') //// (1)
==>[27,32]
g.V().out('knows').aggregate('x').cap('x')
g.V().out('knows').aggregate('x').by('name').cap('x')
g.V().out('knows').aggregate('x').by('age').cap('x')  //1
  1. The "age" property is not productive for all vertices and therefore those values are not included in the aggregation.

Aggregation can be controlled to occur in a lazy fashion by using the step inside local().

console (groovy) groovy groovy
gremlin> g.V().aggregate('x').limit(1).cap('x')
==>[v[1],v[2],v[3],v[4],v[5],v[6]]
gremlin> g.V().local(aggregate('x')).limit(1).cap('x')
==>[v[1],v[2]]
g.V().aggregate('x').limit(1).cap('x')
g.V().local(aggregate('x')).limit(1).cap('x')
g.E().local(aggregate('x')).by('weight').cap('x')

Additional References

All Step

It is possible to filter list traversers using all()-step (filter). Every item in the list will be tested against the supplied predicate and if all of the items pass then the traverser is passed along the stream, otherwise it is filtered. Empty lists are passed along but null or non-iterable traversers are filtered out.

Python

The term all is a reserved word in Python, and therefore must be referred to in Gremlin with all_().

console (groovy) groovy
gremlin> g.V().values('age').fold().all(gt(25)) //// (1)
==>[29,27,32,35]
g.V().values('age').fold().all(gt(25)) //1
  1. Return the list of ages only if everyone’s age is greater than 25.

Additional References

And Step

The and()-step ensures that all provided traversals yield a result (filter). Please see or() for or-semantics.

Python

The term and is a reserved word in Python, and therefore must be referred to in Gremlin with and_().

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

The and()-step can take an arbitrary number of traversals. All traversals must produce at least one output for the original traverser to pass to the next step.

console (groovy) groovy
gremlin> g.V().where(outE('created').and().outE('knows')).values('name')
==>marko
g.V().where(outE('created').and().outE('knows')).values('name')

Additional References

Any Step

It is possible to filter list traversers using any()-step (filter). All items in the list will be tested against the supplied predicate and if any of the items pass then the traverser is passed along the stream, otherwise it is filtered. Empty lists, null traversers, and non-iterable traversers are filtered out as well.

Python

The term any is a reserved word in Python, and therefore must be referred to in Gremlin with any_().

console (groovy) groovy
gremlin> g.V().values('age').fold().any(gt(25)) //// (1)
==>[29,27,32,35]
g.V().values('age').fold().any(gt(25)) //1
  1. Return the list of ages if anyone’s age is greater than 25.

Additional References

As Step

The as()-step 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.

Groovy

The term as is a reserved word in Groovy, and when therefore used as part of an anonymous traversal must be referred to in Gremlin with the double underscore __.as().

Python

The term as is a reserved word in Python, and therefore must be referred to in Gremlin with as_().

console (groovy) groovy
gremlin> g.V().as('a').out('created').as('b').select('a','b') //// (1)
==>[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().as('a').out('created').as('b').select('a','b').by('name') //// (2)
==>[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') //// (1)
g.V().as('a').out('created').as('b').select('a','b').by('name') //2
  1. Select the objects labeled "a" and "b" from the path.

  2. Select the objects labeled "a" and "b" from the path and, for each object, project its name value.

A step can have any number of labels associated with it. This is useful for referencing the same step multiple times in a future step.

console (groovy) groovy
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]]
g.V().hasLabel('software').as('a','b','c').
   select('a','b','c').
     by('name').
     by('lang').
     by(__.in('created').values('name').fold())

Additional References

AsString Step

The asString()-step (map) returns the value of incoming traverser as strings. Any null value will cause an IllegalArgumentException.

console (groovy) groovy
gremlin> g.V().hasLabel('person').values('age').asString() //// (1)
==>29
==>27
==>32
==>35
gremlin> g.V().hasLabel('person').values('age').asString().concat(' years old') //// (2)
==>29 years old
==>27 years old
==>32 years old
==>35 years old
gremlin> g.V().hasLabel('person').values('age').fold().asString(local) //// (3)
==>[29,27,32,35]
g.V().hasLabel('person').values('age').asString() //// (1)
g.V().hasLabel('person').values('age').asString().concat(' years old') //// (2)
g.V().hasLabel('person').values('age').fold().asString(local) //3
  1. Return ages as string.

  2. Return ages as string and use concat to generate phrases.

  3. Use Scope.local to operate on individual string elements inside incoming list, which will return a list.

Additional References

AsBool Step

The asBool()-step (map) converts the incoming traverser to a boolean value. If the traverser is already a boolean value, it is passed as-is. Numbers evaluate to true if non-zero, and to false if zero or NaN. Strings are only accepted when equal to "true" or "false" (case-insensitive), otherwise an IllegalArgumentException is thrown. All other types (including null) will throw an IllegalArgumentException.

console (groovy) groovy
gremlin> g.inject(1).asBool() //// (1)
==>true
gremlin> g.inject("false").asBool() //// (2)
==>false
g.inject(1).asBool() //// (1)
g.inject("false").asBool() //2
  1. Convert number to boolean

  2. Convert string to boolean

Additional References

AsDate Step

The asDate()-step (map) converts string or numeric input to Date.

For string input only ISO-8601 format is supported. For numbers, the value is considered as the number of the milliseconds since "the epoch" (January 1, 1970, 00:00:00 GMT). Date input is passed without changes.

If the incoming traverser is not a string, number, Date or OffsetDateTime then an IllegalArgumentException will be thrown.

console (groovy) groovy
gremlin> g.inject(1690934400000).asDate() //// (1)
==>2023-08-02T00:00Z
gremlin> g.inject("2023-08-02T00:00:00Z").asDate() //// (2)
==>2023-08-02T00:00Z
gremlin> g.inject(datetime("2023-08-24T00:00:00Z")).asDate() //// (3)
==>2023-08-24T00:00Z
g.inject(1690934400000).asDate() //// (1)
g.inject("2023-08-02T00:00:00Z").asDate() //// (2)
g.inject(datetime("2023-08-24T00:00:00Z")).asDate() //3
  1. Convert number to Date

  2. Convert ISO-8601 string to Date

  3. Pass Date without modification

Additional References

AsNumber Step

The asNumber()-step (map) converts the incoming traverser to the nearest parsable type if no argument is provided, or to the desired numerical type, based on the type token (GType) provided. If a type token entered isn’t a numerical type, an IllegalArgumentException will be thrown.

Numerical input will pass through unless a type is specified by the number token. ArithmeticException will be thrown for any overflow during narrowing of types.

String inputs are parsed into numeric values. By default, the value will be parsed as an integer if it represents a whole number, or as a double if it contains a decimal point. A NumberFormatException will be thrown if the string cannot be parsed into a valid number format.

Date inputs are converted to milliseconds since epoch (January 1, 1970, 00:00:00 GMT).

All other input types will result in IllegalArgumentException.

console (groovy) groovy
gremlin> g.inject(1234).asNumber() //// (1)
==>1234
gremlin> g.inject(1.76d).asNumber() //// (2)
==>1.76
gremlin> g.inject(1.76d).asNumber(GType.INT) //// (3)
==>1
gremlin> g.inject("2023-08-02T00:00:00Z").asDate().asNumber() //// (4)
==>1690934400000
g.inject(1234).asNumber() //// (1)
g.inject(1.76d).asNumber() //// (2)
g.inject(1.76d).asNumber(GType.INT) //// (3)
g.inject("2023-08-02T00:00:00Z").asDate().asNumber() //4
  1. An int will be passed through.

  2. A double will be passed through.

  3. A double is converted into an int.

  4. A date is converted into milliseconds since epoch.

Java

The enums values byte, short, int, long, float, double are reserved word in Java, and therefore must be referred to in Gremlin with an underscore appended as a suffix: byte_, short_, int_, long_, float_, double_.

Groovy & Gremlin Console

The enums values byte, short, int, long, float, double are reserved word in Groovy, therefore as the Gremlin Console is Groovy-based, they must be referred to in Gremlin with an underscore appended as a suffix: byte_, short_, int_, long_, float_, double_.

JavaScript

The enums values byte, short, int, long, float, double are reserved word in Javascript, and therefore must be referred to in Gremlin with an underscore appended as a suffix: byte_, short_, int_, long_, float_, double_.

Additional References

Barrier Step

The barrier()-step (barrier) turns the lazy traversal pipeline into a bulk-synchronous pipeline. This step is useful in the following situations:

  • When everything prior to barrier() needs to be executed before moving onto the steps after the barrier() (i.e. ordering).

  • When "stalling" the traversal may lead to a "bulking optimization" in traversals that repeatedly touch many of the same elements (i.e. optimizing).

console (groovy) groovy
gremlin> g.V().sideEffect{println "first: ${it}"}.sideEffect{println "second: ${it}"}.iterate()
first: v[1]
second: v[1]
first: v[2]
second: v[2]
first: v[3]
second: v[3]
first: v[4]
second: v[4]
first: v[5]
second: v[5]
first: v[6]
second: v[6]
gremlin> g.V().sideEffect{println "first: ${it}"}.barrier().sideEffect{println "second: ${it}"}.iterate()
first: v[1]
first: v[2]
first: v[3]
first: v[4]
first: v[5]
first: v[6]
second: v[1]
second: v[2]
second: v[3]
second: v[4]
second: v[5]
second: v[6]
g.V().sideEffect{println "first: ${it}"}.sideEffect{println "second: ${it}"}.iterate()
g.V().sideEffect{println "first: ${it}"}.barrier().sideEffect{println "second: ${it}"}.iterate()

The theory behind a "bulking optimization" is simple. If there are one million traversers at vertex 1, then there is no need to calculate one million both()-computations. Instead, represent those one million traversers as a single traverser with a Traverser.bulk() equal to one million and execute both() once. A bulking optimization example is made more salient on a larger graph. Therefore, the example below leverages the Grateful Dead graph.

console (groovy) groovy
gremlin> graph = TinkerGraph.open()
==>tinkergraph[vertices:0 edges:0]
gremlin> g = traversal().with(graph)
==>graphtraversalsource[tinkergraph[vertices:0 edges:0], standard]
gremlin> g.io('data/grateful-dead.xml').read().iterate()
gremlin> g = traversal().with(graph).withoutStrategies(LazyBarrierStrategy) //// (1)
==>graphtraversalsource[tinkergraph[vertices:808 edges:8049], standard]
gremlin> clockWithResult(1){g.V().both().both().both().count().next()} //// (2)
==>4458.2642909999995
==>126653966
gremlin> clockWithResult(1){g.V().repeat(both()).times(3).count().next()} //// (3)
==>4960.452041
==>126653966
gremlin> clockWithResult(1){g.V().both().barrier().both().barrier().both().barrier().count().next()} //// (4)
==>5.427665999999999
==>126653966
graph = TinkerGraph.open()
g = traversal().with(graph)
g.io('data/grateful-dead.xml').read().iterate()
g = traversal().with(graph).withoutStrategies(LazyBarrierStrategy) //// (1)
clockWithResult(1){g.V().both().both().both().count().next()} //// (2)
clockWithResult(1){g.V().repeat(both()).times(3).count().next()} //// (3)
clockWithResult(1){g.V().both().barrier().both().barrier().both().barrier().count().next()} //4
  1. Explicitly remove LazyBarrierStrategy which yields a bulking optimization.

  2. A non-bulking traversal where each traverser is processed.

  3. Each traverser entering repeat() has its recursion bulked.

  4. A bulking traversal where implicit traversers are not processed.

If barrier() is provided an integer argument, then the barrier will only hold n-number of unique traversers in its barrier before draining the aggregated traversers to the next step. This is useful in the aforementioned bulking optimization scenario with the added benefit of reducing the risk of an out-of-memory exception.

LazyBarrierStrategy inserts barrier()-steps into a traversal where appropriate in order to gain the "bulking optimization."

console (groovy) groovy
gremlin> graph = TinkerGraph.open()
==>tinkergraph[vertices:0 edges:0]
gremlin> g = traversal().with(graph) //// (1)
==>graphtraversalsource[tinkergraph[vertices:0 edges:0], standard]
gremlin> g.io('data/grateful-dead.xml').read().iterate()
gremlin> clockWithResult(1){g.V().both().both().both().count().next()}
==>4.155125
==>126653966
gremlin> g.V().both().both().both().count().iterate().toString() //// (2)
==>[TinkerGraphStep(vertex,[]), VertexStep(BOTH,vertex), NoOpBarrierStep(2500), VertexStep(BOTH,vertex), NoOpBarrierStep(2500), VertexStep(BOTH,edge), CountGlobalStep, DiscardStep]
graph = TinkerGraph.open()
g = traversal().with(graph) //// (1)
g.io('data/grateful-dead.xml').read().iterate()
clockWithResult(1){g.V().both().both().both().count().next()}
g.V().both().both().both().count().iterate().toString()  //2
  1. LazyBarrierStrategy is a default strategy and thus, does not need to be explicitly activated.

  2. With LazyBarrierStrategy activated, barrier()-steps are automatically inserted where appropriate.

Additional References

Branch Step

The branch() step splits the traverser to all the child traversals provided to it. Please see the General Steps section for more information, but also consider that branch() is the basis for more robust steps like choose() and union().

Additional References

By Step

The by()-step 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.

console (groovy) groovy
gremlin> g.V().group().by(bothE().count()) //// (1)
==>[1:[v[2],v[5],v[6]],3:[v[1],v[3],v[4]]]
gremlin> g.V().group().by(bothE().count()).by('name') //// (2)
==>[1:[vadas,ripple,peter],3:[marko,lop,josh]]
gremlin> g.V().group().by(bothE().count()).by(count()) //// (3)
==>[1:3,3:3]
g.V().group().by(bothE().count()) //// (1)
g.V().group().by(bothE().count()).by('name') //// (2)
g.V().group().by(bothE().count()).by(count())  //3
  1. by(outE().count()) will group the elements by their edge count (traversal).

  2. by('name') will process the grouped elements by their name (element property projection).

  3. by(count()) will count the number of elements in each group (traversal).

When a by() modulator does not produce a result, it is deemed "unproductive". An "unproductive" modulator will lead to the filtering of the traverser it is currently working with. The filtering will manifest in various ways depending on the step.

console (groovy) groovy
gremlin> g.V().sample(1).by('age') //// (1)
==>v[4]
g.V().sample(1).by('age') //1
  1. The "age" property key is not present for all vertices, therefore sample() will ignore (i.e. filter) such vertices for consideration in the sampling.

The following steps all support by()-modulation. Note that the semantics of such modulation should be understood on a step-by-step level and thus, as discussed in their respective section of the documentation.

  • aggregate(): aggregate all objects into a set but only store their by()-modulated values.

  • cyclicPath(): filter if the traverser’s path is cyclic given by()-modulation.

  • dedup(): dedup on the results of a by()-modulation.

  • format(): transform a traverser provided to the step by way of the by() modulator before it is processed by it.

  • group(): create group keys and values according to by()-modulation.

  • groupCount(): count those groups where the group keys are the result of by()-modulation.

  • math(): transform a traverser provided to the step by way of the by() modulator before it is processed by it.

  • order(): order the objects by the results of a by()-modulation.

  • path(): get the path of the traverser where each path element is by()-modulated.

  • project(): project a map of results given various by()-modulations off the current object.

  • propertyMap(): transform the result of the values in the resulting Map using the by() modulator.

  • sack(): provides the transformation for a traverser to a value to be stored in the sack.

  • sample(): sample using the value returned by by()-modulation.

  • select(): select path elements and transform them via by()-modulation.

  • simplePath(): filter if the traverser’s path is simple given by()-modulation.

  • tree(): get a tree of traversers objects where the objects have been by()-modulated.

  • valueMap(): transform the result of the values in the resulting Map using the by() modulator.

  • where(): determine the predicate given the testing of the results of by()-modulation.

Additional References

Call Step

The call() step allows for custom, provider-specific service calls either at the start of a traversal or mid-traversal. This allows Graph providers to expose operations not natively built into the Gremlin language, such as full text search, custom analytics, notification triggers, etc.

When called with no arguments, call() will produce a list of callable services available for the graph in use. This no-argument version is equivalent to call('--list'). This "directory service" is also capable of producing more verbose output describing all the services or an individual service:

console (groovy) groovy
gremlin> g.call() //// (1)
gremlin> g.call('--list') //// (1)
gremlin> g.call().with('verbose') //// (2)
gremlin> g.call().with('verbose').with('service', 'xyz-service') //// (3)
g.call() //// (1)
g.call('--list') //// (1)
g.call().with('verbose') //// (2)
g.call().with('verbose').with('service', 'xyz-service') //3
  1. List available services by name

  2. Produce a Map of detailed service information by name

  3. Produce the detailed service information for the 'xyz-service'

The first argument to call() is always the name of the service call. Additionally, service calls can accept both static and dynamically produced parameters. Static parameters can be passed as a Map to the call() as the second argument. Individual static parameters can also be added using the .with() modulator. Dynamic parameters can be passed as a Map-producing Traversal as the second argument (no static parameters) or third argument (static + dynamic parameters). Additional individual dynamic parameters can be added using the .with() modulator.

g.call('xyz-service') //1
g.call('xyz-service', ['a':'b']) //2
g.call('xyz-service').with('a', 'b') //2
g.call('xyz-service', __.inject(['a':'b'])) //3
g.call('xyz-service').with('a', __.inject('b')) //3
g.call('xyz-service', ['a':'b'], __.inject(['c':'d'])) //4
  1. Call the 'xyz-service' with no parameters

  2. Examples of static parameters (constants known before execution)

  3. Examples of dynamic parameters (these will be computed at execution time)

  4. Example of static + dynamic parameters (these will be computed and merged into one set of parameters at execution time)

Additional References

GraphTraversalSource:

GraphTraversal:

Cap Step

The cap()-step (barrier) iterates the traversal up to itself and emits the sideEffect referenced by the provided key. If multiple keys are provided, then a Map<String,Object> of sideEffects is emitted.

console (groovy) groovy
gremlin> g.V().groupCount('a').by(label).cap('a') //// (1)
==>[software:2,person:4]
gremlin> g.V().groupCount('a').by(label).groupCount('b').by(outE().count()).cap('a','b') //// (2)
==>[a:[software:2,person:4],b:[0:3,1:1,2:1,3:1]]
g.V().groupCount('a').by(label).cap('a') //// (1)
g.V().groupCount('a').by(label).groupCount('b').by(outE().count()).cap('a','b')   //2
  1. Group and count vertices by their label. Emit the side effect labeled 'a', which is the group count by label.

  2. Same as statement 1, but also emit the side effect labeled 'b' which groups vertices by the number of out edges.

Additional References

Choose Step

choose step

The choose()-step (branch) routes the current traverser to a particular traversal branch option. With choose(), it is possible to implement two different types of semantics: if-then-else (conditional branching) and switch (value-based selection).

If-Then-Else

The if-the-else semantics of choose() evaluate a predicate traversal and route the traverser to either the "true" branch or the "false" branch based on the result.

console (groovy) groovy
gremlin> g.V().hasLabel('person').
               choose(values('age').is(lte(30)),
                      __.in(),
                      __.out()).values('name') //// (1)
==>marko
==>ripple
==>lop
==>lop
gremlin> g.V().hasLabel('person').
               choose(outE('knows').count().is(gt(0)),
                      __.out('knows'),
                      __.identity()).values('name') //// (2)
==>vadas
==>josh
==>vadas
==>josh
==>peter
g.V().hasLabel('person').
      choose(values('age').is(lte(30)),
             __.in(),
             __.out()).values('name') //// (1)
g.V().hasLabel('person').
      choose(outE('knows').count().is(gt(0)),
             __.out('knows'),
             __.identity()).values('name') //2
  1. If the person’s age is less than or equal to 30, then traverse to incoming vertices, else traverse to outgoing vertices.

  2. If the person has outgoing "knows" edges, then traverse to those known vertices, else return the person vertex itself.

If the "false"-branch is not provided, then simple if-then-semantics are implemented, where traversers that don’t match the condition are passed through unchanged.

console (groovy) groovy
gremlin> g.V().choose(hasLabel('person'), out('created')).values('name') //// (1)
==>lop
==>lop
==>ripple
==>lop
==>ripple
==>lop
gremlin> g.V().choose(hasLabel('person'), out('created'), identity()).values('name') //// (2)
==>lop
==>lop
==>ripple
==>lop
==>ripple
==>lop
g.V().choose(hasLabel('person'), out('created')).values('name') //// (1)
g.V().choose(hasLabel('person'), out('created'), identity()).values('name') //2
  1. If the vertex is a person, emit the vertices they created, else emit the vertex.

  2. if-the-else with an identity() on the false-branch is equivalent to if-then with no false-branch.

Switch

The switch semantics of choose() use the result of a traversal as a key to select from multiple traversal options. This allows for more complex branching logic beyond simple true/false conditions.

console (groovy) groovy
gremlin> g.V().hasLabel('person').
               choose(values('name')).
                 option('marko', values('age')).
                 option('josh', values('name')).
                 option('vadas', elementMap()).
                 option('peter', label()) //// (1)
==>29
==>[id:2,label:person,name:vadas,age:27]
==>josh
==>person
gremlin> g.V().hasLabel('person').
               choose(values('age')).
                 option(27, __.in().values('name')).
                 option(32, __.out().values('name')) //// (2)
==>v[1]
==>marko
==>ripple
==>lop
==>v[6]
g.V().hasLabel('person').
      choose(values('name')).
        option('marko', values('age')).
        option('josh', values('name')).
        option('vadas', elementMap()).
        option('peter', label()) //// (1)
g.V().hasLabel('person').
      choose(values('age')).
        option(27, __.in().values('name')).
        option(32, __.out().values('name')) //2
  1. Use the person’s name to select which property or operation to return.

  2. Use the person’s age value to select which traversal to apply, noting that traversers matching no age values simply pass through.

The choose()-step can use predicates with options to match ranges of values or other conditions.

console (groovy) groovy
gremlin> g.V().hasLabel('person').
               choose(values('age')).
                 option(P.between(26, 30), constant('younger')).
                 option(P.gt(30), constant('older')).
                 option(Pick.none, constant('unknown')) //// (1)
==>younger
==>younger
==>older
==>older
g.V().hasLabel('person').
      choose(values('age')).
        option(P.between(26, 30), constant('younger')).
        option(P.gt(30), constant('older')).
        option(Pick.none, constant('unknown')) //1
  1. If the person’s age is between 26 and 30, classify them as 'younger', if greater than 30, classify as 'older', otherwise 'unknown'.

The token T.label can be used as shorthand for __.label() when selecting options based on element labels.

console (groovy) groovy
gremlin> g.V().choose(T.label).
                 option('person', out('created')).
                 option('software', in('created')).
                 values('name') //// (1)
==>lop
==>marko
==>josh
==>peter
==>ripple
==>lop
==>josh
==>lop
g.V().choose(T.label).
        option('person', out('created')).
        option('software', in('created')).
        values('name') //1
  1. For person vertices, traverse to the software they created; for software vertices, traverse to the people who created them.

The Pick enum was introduced in an example earlier to handle non-matching scenarios. The following Pick options may be used with choose():

  • Pick.none - Matches when no other options match

  • Pick.unproductive - Matches when the choice in choose() produces no results

console (groovy) groovy
gremlin> g.V().choose(values('age')).
                 option(P.between(26, 30), values('name')).
                 option(Pick.none, values('name')).
                 option(Pick.unproductive, label()) //// (1)
==>marko
==>vadas
==>software
==>josh
==>software
==>peter
gremlin> g.V().hasLabel('person').
               choose(out('knows').count()).
                 option(0, constant('noFriends')).
                 option(Pick.none, constant('hasFriends')) //// (2)
==>hasFriends
==>noFriends
==>noFriends
==>noFriends
gremlin> g.V().choose(values('age')).
                 option(27, __.in().values('name')).
                 option(32, __.out().values('name')).
                 option(Pick.unproductive, discard()).
                 option(Pick.none, discard()) //// (3)
==>marko
==>ripple
==>lop
g.V().choose(values('age')).
        option(P.between(26, 30), values('name')).
        option(Pick.none, values('name')).
        option(Pick.unproductive, label()) //// (1)
g.V().hasLabel('person').
      choose(out('knows').count()).
        option(0, constant('noFriends')).
        option(Pick.none, constant('hasFriends')) //// (2)
g.V().choose(values('age')).
        option(27, __.in().values('name')).
        option(32, __.out().values('name')).
        option(Pick.unproductive, discard()).
        option(Pick.none, discard()) //3
  1. For vertices with age between 26-30, return the name. For vertices with age outside that range, return the name. For vertices without an age property, return the label.

  2. For people with no outgoing "knows" edges, return 'noFriends', otherwise return 'hasFriends'.

  3. Use none() step in combination with Pick.none and Pick.unproductive to filter unproductive traversals and unmatched values.

Important

It is important to think of choose() as a branching step and not a filter. The if-then semantics can intuitively lead to thinking the latter, where no match would mean to remove the traverser from the stream. As shown in the examples, this is not what happens.

The choose()-step can be used within a map() step to apply the branching logic to each element in a collection.

console (groovy) groovy
gremlin> g.V().hasLabel('person').
               map(choose(values('age')).
                     option(P.between(26, 30), values('name').fold()).
                     option(Pick.none, values('name').fold())) //// (1)
==>[marko]
==>[vadas]
==>[josh]
==>[peter]
g.V().hasLabel('person').
      map(choose(values('age')).
            option(P.between(26, 30), values('name').fold()).
            option(Pick.none, values('name').fold())) //1
  1. For each person, create a list containing their name, using the same traversal regardless of age.

Additional References

Coalesce Step

The coalesce()-step evaluates the provided traversals in order and returns the first traversal that emits at least one element.

console (groovy) groovy
gremlin> g.V(1).coalesce(outE('knows'), outE('created')).inV().path().by('name').by(label)
==>[marko,knows,vadas]
==>[marko,knows,josh]
gremlin> g.V(1).coalesce(outE('created'), outE('knows')).inV().path().by('name').by(label)
==>[marko,created,lop]
gremlin> g.V(1).property('nickname', 'okram')
==>v[1]
gremlin> g.V().hasLabel('person').coalesce(values('nickname'), values('name'))
==>okram
==>vadas
==>josh
==>peter
g.V(1).coalesce(outE('knows'), outE('created')).inV().path().by('name').by(label)
g.V(1).coalesce(outE('created'), outE('knows')).inV().path().by('name').by(label)
g.V(1).property('nickname', 'okram')
g.V().hasLabel('person').coalesce(values('nickname'), values('name'))

Be aware that the current traverser behavior where the traverser appears to be unaffected by state modifying steps or account as a single bulk to side effects inside the coalesce() traversal is subject to change. The following are examples of some traversals on the "modern" graph whose output may change:

gremlin> g.V(1, 1).barrier().coalesce(aggregate("x"), groupCount("x")).cap("x")
==>[v[1]]
gremlin> g.withSack(1.0f).V(1).barrier().coalesce(sack(mult).by("age"), constant(2)).sack()
==>1.0

Additional References

Coin Step

To randomly filter out a traverser, use the coin()-step (filter). The provided double argument biases the "coin toss."

console (groovy) groovy
gremlin> g.V().coin(0.5)
==>v[1]
==>v[3]
==>v[4]
==>v[5]
gremlin> g.V().coin(0.0)
gremlin> g.V().coin(1.0)
==>v[1]
==>v[2]
==>v[3]
==>v[4]
==>v[5]
==>v[6]
g.V().coin(0.5)
g.V().coin(0.0)
g.V().coin(1.0)

Additional References

Combine Step

The combine()-step (map) combines the elements of the incoming list traverser and the provided list argument into one list. This is also known as appending or concatenating. This step only expects list data (array or Iterable) and will throw an IllegalArgumentException if any other type is encountered (including null). This differs from the merge()-step in that it allows duplicates to exist.

console (groovy) groovy
gremlin> g.V().values("name").fold().combine(["james","jen","marko","vadas"])
==>[marko,vadas,lop,josh,ripple,peter,james,jen,marko,vadas]
gremlin> g.V().values("name").fold().combine(__.constant("stephen").fold())
==>[marko,vadas,lop,josh,ripple,peter,stephen]
g.V().values("name").fold().combine(["james","jen","marko","vadas"])
g.V().values("name").fold().combine(__.constant("stephen").fold())

Additional References

Concat Step

The concat()-step (map) concatenates one or more String values together to the incoming String traverser. This step can take either String varargs or Traversal varargs. Any null String values will be skipped when concatenated with non-null String values. If two null value are concatenated, the null value will be propagated and returned. If the incoming traverser is a non-String value then an IllegalArgumentException will be thrown.

console (groovy) groovy
gremlin> g.addV(constant('prefix_').concat(__.V(1).label())).property(id, 10) //// (1)
==>v[10]
gremlin> g.V(10).label()
==>prefix_person
gremlin> g.V().hasLabel('person').values('name').as('a').
             constant('Mr.').concat(__.select('a')) //// (2)
==>Mr.marko
==>Mr.vadas
==>Mr.josh
==>Mr.peter
gremlin> g.V().hasLabel('software').as('a').values('name').
             concat(' uses ').
             concat(select('a').values('lang')) //// (3)
==>lop uses java
==>ripple uses java
gremlin> g.V(1).outE().as('a').V(1).values('name').
             concat(' ').
             concat(select('a').label()).
             concat(' ').
             concat(select("a").inV().values('name')) //// (4)
==>marko created lop
==>marko knows vadas
==>marko knows josh
gremlin> g.V(1).outE().as('a').V(1).values('name').
             concat(constant(' '),
                 select("a").label(),
                 constant(' '),
                 select('a').inV().values('name')) //// (5)
==>marko created lop
==>marko knows vadas
==>marko knows josh
gremlin> g.inject('hello', 'hi').concat(__.V().values('name')) //// (6)
==>hellomarko
==>himarko
gremlin> g.inject('This').concat(' ').concat('is a ', 'gremlin.') //// (7)
==>This is a gremlin.
g.addV(constant('prefix_').concat(__.V(1).label())).property(id, 10) //// (1)
g.V(10).label()
g.V().hasLabel('person').values('name').as('a').
    constant('Mr.').concat(__.select('a')) //// (2)
g.V().hasLabel('software').as('a').values('name').
    concat(' uses ').
    concat(select('a').values('lang')) //// (3)
g.V(1).outE().as('a').V(1).values('name').
    concat(' ').
    concat(select('a').label()).
    concat(' ').
    concat(select("a").inV().values('name')) //// (4)
g.V(1).outE().as('a').V(1).values('name').
    concat(constant(' '),
        select("a").label(),
        constant(' '),
        select('a').inV().values('name')) //// (5)
g.inject('hello', 'hi').concat(__.V().values('name')) //// (6)
g.inject('This').concat(' ').concat('is a ', 'gremlin.') //7
  1. Add a new vertex with id 10 which should be labeled like an existing vertex but with some prefix attached

  2. Attach the prefix "Mr." to all the names using the constant()-step

  3. Generate a string of software names and the language they use

  4. Generate a string description for each of marko’s outgoing edges

  5. Alternative way to generate the string description by using traversal varargs. Use the constant() step to add desired strings between arguments.

  6. The concat() step will append the first result from the child traversal to the incoming traverser

  7. A generic use of concat() to join strings together

Additional References

Conjoin Step

The conjoin()-step (map) joins together the elements in the incoming list traverser together with the provided argument as a delimiter. The resulting String is added to the Traversal Stream. This step only expects list data (array or Iterable) in the incoming traverser and will throw an IllegalArgumentException if any other type is encountered (including null). Null values are skipped and not included in the result.

console (groovy) groovy
gremlin> g.V().values("name").fold().conjoin("+")
==>marko+vadas+lop+josh+ripple+peter
g.V().values("name").fold().conjoin("+")

Additional References

ConnectedComponent Step

The connectedComponent() step performs a computation to identify Connected Component instances in a graph. When this step completes, the vertices will be labelled with a component identifier to denote the component to which they are associated.

Important

The connectedComponent()-step is a VertexComputing-step and as such, can only be used against a graph that supports GraphComputer (OLAP).
console (groovy) groovy
gremlin> g = traversal().with(graph).withComputer()
==>graphtraversalsource[tinkergraph[vertices:6 edges:6], graphcomputer]
gremlin> g.V().
           connectedComponent().
             with(ConnectedComponent.propertyName, 'component').
           project('name','component').
             by('name').
             by('component')
==>[name:vadas,component:1]
==>[name:josh,component:1]
==>[name:ripple,component:1]
==>[name:marko,component:1]
==>[name:peter,component:1]
==>[name:lop,component:1]
gremlin> g.V().hasLabel('person').
           connectedComponent().
             with(ConnectedComponent.propertyName, 'component').
             with(ConnectedComponent.edges, outE('knows')).
           project('name','component').
             by('name').
             by('component')
==>[name:vadas,component:1]
==>[name:marko,component:1]
==>[name:peter,component:6]
==>[name:josh,component:1]
g = traversal().with(graph).withComputer()
g.V().
  connectedComponent().
    with(ConnectedComponent.propertyName, 'component').
  project('name','component').
    by('name').
    by('component')
g.V().hasLabel('person').
  connectedComponent().
    with(ConnectedComponent.propertyName, 'component').
    with(ConnectedComponent.edges, outE('knows')).
  project('name','component').
    by('name').
    by('component')

Note the use of the with() modulating step which provides configuration options to the algorithm. It takes configuration keys from the ConnectedComponent class and is automatically imported to the Gremlin Console.

Additional References

Constant Step

To specify a constant value for a traverser, use the constant()-step (map). This is often useful with conditional steps like choose()-step or coalesce()-step.

console (groovy) groovy
gremlin> g.V().choose(hasLabel('person'),
             values('name'),
             constant('inhuman')) //// (1)
==>marko
==>vadas
==>inhuman
==>josh
==>inhuman
==>peter
gremlin> g.V().coalesce(
             hasLabel('person').values('name'),
             constant('inhuman')) //// (2)
==>marko
==>vadas
==>inhuman
==>josh
==>inhuman
==>peter
g.V().choose(hasLabel('person'),
    values('name'),
    constant('inhuman')) //// (1)
g.V().coalesce(
    hasLabel('person').values('name'),
    constant('inhuman')) //2
  1. Show the names of people, but show "inhuman" for other vertices.

  2. Same as statement 1 (unless there is a person vertex with no name).

Additional References

Count Step

count step

The count()-step (map) counts the total number of represented traversers in the streams (i.e. the bulk count).

console (groovy) groovy
gremlin> g.V().count()
==>6
gremlin> g.V().hasLabel('person').count()
==>4
gremlin> g.V().hasLabel('person').outE('created').count().path() //// (1)
==>[4]
gremlin> g.V().hasLabel('person').outE('created').count().map {it.get() * 10}.path() //// (2)
==>[4,40]
g.V().count()
g.V().hasLabel('person').count()
g.V().hasLabel('person').outE('created').count().path() //// (1)
g.V().hasLabel('person').outE('created').count().map {it.get() * 10}.path() //2
  1. count()-step is a reducing barrier step meaning that all of the previous traversers are folded into a new traverser.

  2. The path of the traverser emanating from count() starts at count().

Important

count(local) counts the current, local object (not the objects in the traversal stream). This works for Collection- and Map-type objects. For any other object, a count of 1 is returned.

Additional References

CyclicPath Step

cyclicpath step

Each traverser maintains its history through the traversal over the graph — i.e. its path. If it is important that the traverser repeat its course, then cyclic()-path should be used (filter). The step analyzes the path of the traverser thus far and if there are any repeats, the traverser is filtered out over the traversal computation. If non-cyclic behavior is desired, see simplePath().

console (groovy) groovy
gremlin> g.V(1).both().both()
==>v[1]
==>v[4]
==>v[6]
==>v[1]
==>v[5]
==>v[3]
==>v[1]
gremlin> g.V(1).both().both().cyclicPath()
==>v[1]
==>v[1]
==>v[1]
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().cyclicPath().by('age').path() //// (1)
==>[v[1],v[2],v[1]]
==>[v[1],v[4],v[1]]
gremlin> g.V(1).as('a').out('created').as('b').
           in('created').as('c').
           cyclicPath().
           path()
==>[v[1],v[3],v[1]]
gremlin> g.V(1).as('a').out('created').as('b').
           in('created').as('c').
           cyclicPath().from('a').to('b').
           path()
g.V(1).both().both()
g.V(1).both().both().cyclicPath()
g.V(1).both().both().cyclicPath().path()
g.V(1).both().both().cyclicPath().by('age').path() //// (1)
g.V(1).as('a').out('created').as('b').
  in('created').as('c').
  cyclicPath().
  path()
g.V(1).as('a').out('created').as('b').
  in('created').as('c').
  cyclicPath().from('a').to('b').
  path()
  1. The "age" property is not productive for all vertices and therefore those traversers are filtered.

Additional References

DateAdd Step

The dateAdd()-step (map) returns the value with the addition of the value number of units as specified by the DateToken. If the incoming traverser is not a Date or OffsetDateTime, then an IllegalArgumentException will be thrown.

console (groovy) groovy
gremlin> g.inject("2023-08-02T00:00:00Z").asDate().dateAdd(DT.day, 7) //// (1)
==>2023-08-09T00:00Z
gremlin> g.inject(["2023-08-02T00:00:00Z", "2023-08-03T00:00:00Z"]).unfold().asDate().dateAdd(DT.minute, 1) //// (2)
==>2023-08-02T00:01Z
==>2023-08-03T00:01Z
g.inject("2023-08-02T00:00:00Z").asDate().dateAdd(DT.day, 7) //// (1)
g.inject(["2023-08-02T00:00:00Z", "2023-08-03T00:00:00Z"]).unfold().asDate().dateAdd(DT.minute, 1) //2
  1. Add 7 days to Date

  2. Add 1 minute to incoming dates

Additional References

DateDiff Step

The dateDiff()-step (map) returns the difference between two Dates in epoch time in milliseconds. If the incoming traverser is not a Date or OffsetDateTime, then an IllegalArgumentException will be thrown.

console (groovy) groovy
gremlin> g.inject("2023-08-02T00:00:00Z").asDate().dateDiff(constant("2023-08-03T00:00:00Z").asDate()) //// (1)
==>-86400000
g.inject("2023-08-02T00:00:00Z").asDate().dateDiff(constant("2023-08-03T00:00:00Z").asDate()) //1
  1. Find difference between two dates in milliseconds

Additional References

Dedup Step

With dedup()-step (filter), repeatedly seen objects are removed from the traversal stream. Note that if a traverser’s bulk is greater than 1, then it is set to 1 before being emitted.

console (groovy) groovy
gremlin> g.V().values('lang')
==>java
==>java
gremlin> g.V().values('lang').dedup()
==>java
gremlin> g.V(1).repeat(bothE('created').dedup().otherV()).emit().path() //// (1)
==>[v[1],e[9][1-created->3],v[3]]
==>[v[1],e[9][1-created->3],v[3],e[11][4-created->3],v[4]]
==>[v[1],e[9][1-created->3],v[3],e[12][6-created->3],v[6]]
==>[v[1],e[9][1-created->3],v[3],e[11][4-created->3],v[4],e[10][4-created->5],v[5]]
gremlin> g.V().bothE().properties().dedup() //// (2)
==>p[weight->0.4]
==>p[weight->0.5]
==>p[weight->1.0]
==>p[weight->0.2]
g.V().values('lang')
g.V().values('lang').dedup()
g.V(1).repeat(bothE('created').dedup().otherV()).emit().path() //// (1)
g.V().bothE().properties().dedup() //2
  1. Traverse all created edges, but don’t touch any edge twice.

  2. Note that Property instances will compare on key and value, whereas a VertexProperty will also include its element as it is a first-class citizen.

If a by-step modulation is provided to dedup(), then the object is processed accordingly prior to determining if it has been seen or not.

console (groovy) groovy
gremlin> g.V().elementMap('name')
==>[id:1,label:person,name:marko]
==>[id:2,label:person,name:vadas]
==>[id:3,label:software,name:lop]
==>[id:4,label:person,name:josh]
==>[id:5,label:software,name:ripple]
==>[id:6,label:person,name:peter]
gremlin> g.V().dedup().by(label).values('name')
==>marko
==>lop
g.V().elementMap('name')
g.V().dedup().by(label).values('name')

If dedup() is provided an array of strings, then it will ensure that the de-duplication is not with respect to the current traverser object, but to the path history of the traverser.

console (groovy) groovy
gremlin> g.V().as('a').out('created').as('b').in('created').as('c').select('a','b','c')
==>[a:v[1],b:v[3],c:v[1]]
==>[a:v[1],b:v[3],c:v[4]]
==>[a:v[1],b:v[3],c:v[6]]
==>[a:v[4],b:v[5],c:v[4]]
==>[a:v[4],b:v[3],c:v[1]]
==>[a:v[4],b:v[3],c:v[4]]
==>[a:v[4],b:v[3],c:v[6]]
==>[a:v[6],b:v[3],c:v[1]]
==>[a:v[6],b:v[3],c:v[4]]
==>[a:v[6],b:v[3],c:v[6]]
gremlin> g.V().as('a').out('created').as('b').in('created').as('c').dedup('a','b').select('a','b','c') //// (1)
==>[a:v[1],b:v[3],c:v[1]]
==>[a:v[4],b:v[5],c:v[4]]
==>[a:v[4],b:v[3],c:v[1]]
==>[a:v[6],b:v[3],c:v[1]]
gremlin> g.V().as('a').both().as('b').both().as('c').
           dedup('a','b').by('age'). //// (2)
           select('a','b','c').by('name')
==>[a:marko,b:vadas,c:marko]
==>[a:marko,b:josh,c:ripple]
==>[a:vadas,b:marko,c:lop]
==>[a:josh,b:marko,c:lop]
g.V().as('a').out('created').as('b').in('created').as('c').select('a','b','c')
g.V().as('a').out('created').as('b').in('created').as('c').dedup('a','b').select('a','b','c') //// (1)
g.V().as('a').both().as('b').both().as('c').
  dedup('a','b').by('age'). //// (2)
  select('a','b','c').by('name')
  1. If the current a and b combination has been seen previously, then filter the traverser.

  2. The "age" property is not productive for all vertices and therefore those values are filtered.

The dedup() step can work on many different types of objects. One object in particular can need a bit of explanation. If you use dedup() on a Path object there is a chance that you may get some unexpected results. Consider the following example which forcibly generates duplicate path results in the first traversal and in the second applies dedup() to remove them:

console (groovy) groovy
gremlin> g.V().union(out().path(), out().path())
==>[v[1],v[3]]
==>[v[1],v[2]]
==>[v[1],v[4]]
==>[v[1],v[3]]
==>[v[1],v[2]]
==>[v[1],v[4]]
==>[v[4],v[5]]
==>[v[4],v[3]]
==>[v[4],v[5]]
==>[v[4],v[3]]
==>[v[6],v[3]]
==>[v[6],v[3]]
gremlin> g.V().union(out().path(), out().path()).dedup()
==>[v[1],v[3]]
==>[v[1],v[2]]
==>[v[1],v[4]]
==>[v[4],v[5]]
==>[v[4],v[3]]
==>[v[6],v[3]]
g.V().union(out().path(), out().path())
g.V().union(out().path(), out().path()).dedup()

The dedup() step checks the equality of the paths by examining the equality of the objects on the Path (in this case vertices), but also on any path labels. In the prior example, there weren’t any path labels so dedup() behaved as expected. In the next example, note the difference in the results if a label is added for one Path but not the other:

console (groovy) groovy
gremlin> g.V().union(out().as('x').path(), out().path())
==>[v[1],v[3]]
==>[v[1],v[2]]
==>[v[1],v[4]]
==>[v[1],v[3]]
==>[v[1],v[2]]
==>[v[1],v[4]]
==>[v[4],v[5]]
==>[v[4],v[3]]
==>[v[4],v[5]]
==>[v[4],v[3]]
==>[v[6],v[3]]
==>[v[6],v[3]]
gremlin> g.V().union(out().as('x').path(), out().path()).dedup()
==>[v[1],v[3]]
==>[v[1],v[2]]
==>[v[1],v[4]]
==>[v[1],v[3]]
==>[v[1],v[2]]
==>[v[1],v[4]]
==>[v[4],v[5]]
==>[v[4],v[3]]
==>[v[4],v[5]]
==>[v[4],v[3]]
==>[v[6],v[3]]
==>[v[6],v[3]]
g.V().union(out().as('x').path(), out().path())
g.V().union(out().as('x').path(), out().path()).dedup()

The prior example shows how dedup() does not have the same effect when a path label is in place. In this contrived example the answer is simple: remove the as('x'). If in the real world, it is not possible to remove the label, the workaround is to deconstruct the Path into a List to drop the label. In this way, dedup() is just comparing List objects and the objects in the Path.

console (groovy) groovy
gremlin> g.V().union(out().as('x').path(), out().path()).map(unfold().fold()).dedup()
==>[v[1],v[3]]
==>[v[1],v[2]]
==>[v[1],v[4]]
==>[v[4],v[5]]
==>[v[4],v[3]]
==>[v[6],v[3]]
g.V().union(out().as('x').path(), out().path()).map(unfold().fold()).dedup()

Additional References

Difference Step

The difference()-step (map) calculates the difference between the incoming list traverser and the provided list argument. More specifically, this provides the set operation A-B where A is the traverser and B is the argument. This step only expects list data (array or Iterable) and will throw an IllegalArgumentException if any other type is encountered (including null).

console (groovy) groovy
gremlin> g.V().values("name").fold().difference(["lop","ripple"])
==>[peter,vadas,josh,marko]
gremlin> g.V().values("name").fold().difference(__.V().limit(2).values("name").fold())
==>[ripple,peter,josh,lop]
g.V().values("name").fold().difference(["lop","ripple"])
g.V().values("name").fold().difference(__.V().limit(2).values("name").fold())

Additional References

Discard Step

The discard()-step (filter) filters all objects from a traversal stream. It is helpful with Branch Step types of steps where a particular branch of code should "throw away" traversers. In the following example, traversers that don’t match are filtered out of the traversal stream.

console (groovy) groovy
gremlin> g.V().choose(T.label).
                 option("person", __.out("knows").values("name")).
                 option("bleep", __.out("created").values("name")).
                 option(none, discard())
==>vadas
==>josh
g.V().choose(T.label).
        option("person", __.out("knows").values("name")).
        option("bleep", __.out("created").values("name")).
        option(none, discard())

It is also useful for traversals that are executed remotely where returning results is not useful and the traversal is only meant to generate side-effects. Choosing not to return results saves in serialization and network costs as the objects are filtered on the remote end and not returned to the client side. Typically, this step does not need to be used directly and is quietly used by the iterate() terminal step which appends discard() to the traversal before actually cycling through results.

Additional References

Disjunct Step

The disjunct()-step (map) calculates the disjunct set between the incoming list traverser and the provided list argument. This step only expects list data (array or Iterable) and will throw an IllegalArgumentException if any other type is encountered (including null).

console (groovy) groovy
gremlin> g.V().values("name").fold().disjunct(["lop","peter","sam"]) //// (1)
==>[ripple,vadas,josh,sam,marko]
gremlin> g.V().values("name").fold().disjunct(__.V().limit(3).values("name").fold())
==>[ripple,peter,josh]
g.V().values("name").fold().disjunct(["lop","peter","sam"]) //// (1)
g.V().values("name").fold().disjunct(__.V().limit(3).values("name").fold())
  1. Find the unique names between two group of names

Additional References

Drop Step

The drop()-step (filter/sideEffect) is used to remove element and properties from the graph (i.e. remove). It is a filter step because the traversal yields no outgoing objects.

console (groovy) groovy
gremlin> g.V().outE().drop()
gremlin> g.E()
gremlin> g.V().properties('name').drop()
gremlin> g.V().elementMap()
==>[id:1,label:person,age:29]
==>[id:2,label:person,age:27]
==>[id:3,label:software,lang:java]
==>[id:4,label:person,age:32]
==>[id:5,label:software,lang:java]
==>[id:6,label:person,age:35]
gremlin> g.V().drop()
gremlin> g.V()
g.V().outE().drop()
g.E()
g.V().properties('name').drop()
g.V().elementMap()
g.V().drop()
g.V()

Additional References

E Step

The E()-step is meant to read edges from the graph and is usually used to start a GraphTraversal, but can also be used mid-traversal.

console (groovy) groovy
gremlin> g.E(11) //// (1)
==>e[11][4-created->3]
gremlin> g.E().hasLabel('knows').has('weight', gt(0.75))
==>e[8][1-knows->4]
gremlin> g.inject(1).coalesce(E().hasLabel("knows"), addE("knows").from(V().has("name","josh")).to(V().has("name","vadas"))) //// (2)
==>e[7][1-knows->2]
==>e[8][1-knows->4]
g.E(11) //// (1)
g.E().hasLabel('knows').has('weight', gt(0.75))
g.inject(1).coalesce(E().hasLabel("knows"), addE("knows").from(V().has("name","josh")).to(V().has("name","vadas"))) //2
  1. Find the edge by its unique identifier (i.e. T.id) - not all graphs will use a numeric value for their identifier.

  2. Get edges with label knows, if there is none then add new one between josh and vadas.

Additional References

Element Step

The element() step is a no-argument step that traverses from a Property to the Element that owns it.

console (groovy) groovy
gremlin> g.V().properties().element() //// (1)
==>v[1]
==>v[1]
==>v[1]
==>v[1]
==>v[1]
==>v[7]
==>v[7]
==>v[7]
==>v[7]
==>v[8]
==>v[8]
==>v[8]
==>v[8]
==>v[8]
==>v[9]
==>v[9]
==>v[9]
==>v[9]
==>v[10]
==>v[11]
gremlin> g.E().properties().element() //// (2)
==>e[13][1-develops->10]
==>e[14][1-develops->11]
==>e[15][1-uses->10]
==>e[16][1-uses->11]
==>e[17][7-develops->10]
==>e[18][7-develops->11]
==>e[19][7-uses->10]
==>e[20][7-uses->11]
==>e[21][8-develops->10]
==>e[22][8-uses->10]
==>e[23][8-uses->11]
==>e[24][9-uses->10]
==>e[25][9-uses->11]
gremlin> g.V().properties().properties().element() //// (3)
==>vp[location->san diego]
==>vp[location->san diego]
==>vp[location->santa cruz]
==>vp[location->santa cruz]
==>vp[location->brussels]
==>vp[location->brussels]
==>vp[location->santa fe]
==>vp[location->centreville]
==>vp[location->centreville]
==>vp[location->dulles]
==>vp[location->dulles]
==>vp[location->purcellville]
==>vp[location->bremen]
==>vp[location->bremen]
==>vp[location->baltimore]
==>vp[location->baltimore]
==>vp[location->oakland]
==>vp[location->oakland]
==>vp[location->seattle]
==>vp[location->spremberg]
==>vp[location->spremberg]
==>vp[location->kaiserslautern]
==>vp[location->kaiserslautern]
==>vp[location->aachen]
g.V().properties().element() //// (1)
g.E().properties().element() //// (2)
g.V().properties().properties().element() //3
  1. Traverse from VertexProperty to Vertex

  2. Traverse from Property (edge property) to Edge

  3. Traverse from Property (meta property) to VertexProperty

Additional References

ElementMap Step

The elementMap()-step yields a Map representation of the structure of an element.

console (groovy) groovy
gremlin> g.V().elementMap()
==>[id:1,label:person,name:marko,age:29]
==>[id:2,label:person,name:vadas,age:27]
==>[id:3,label:software,name:lop,lang:java]
==>[id:4,label:person,name:josh,age:32]
==>[id:5,label:software,name:ripple,lang:java]
==>[id:6,label:person,name:peter,age:35]
gremlin> g.V().elementMap('age')
==>[id:1,label:person,age:29]
==>[id:2,label:person,age:27]
==>[id:3,label:software]
==>[id:4,label:person,age:32]
==>[id:5,label:software]
==>[id:6,label:person,age:35]
gremlin> g.V().elementMap('age','blah')
==>[id:1,label:person,age:29]
==>[id:2,label:person,age:27]
==>[id:3,label:software]
==>[id:4,label:person,age:32]
==>[id:5,label:software]
==>[id:6,label:person,age:35]
gremlin> g.E().elementMap()
==>[id:7,label:knows,IN:[id:2,label:person],OUT:[id:1,label:person],weight:0.5]
==>[id:8,label:knows,IN:[id:4,label:person],OUT:[id:1,label:person],weight:1.0]
==>[id:9,label:created,IN:[id:3,label:software],OUT:[id:1,label:person],weight:0.4]
==>[id:10,label:created,IN:[id:5,label:software],OUT:[id:4,label:person],weight:1.0]
==>[id:11,label:created,IN:[id:3,label:software],OUT:[id:4,label:person],weight:0.4]
==>[id:12,label:created,IN:[id:3,label:software],OUT:[id:6,label:person],weight:0.2]
g.V().elementMap()
g.V().elementMap('age')
g.V().elementMap('age','blah')
g.E().elementMap()

It is important to note that the map of a vertex assumes that cardinality for each key is single and if it is list then only the first item encountered will be returned. As single is the more common cardinality for properties this assumption should serve the greatest number of use cases.

console (groovy) groovy
gremlin> g.V().elementMap()
==>[id:1,label:person,name:marko,location:santa fe]
==>[id:7,label:person,name:stephen,location:purcellville]
==>[id:8,label:person,name:matthias,location:seattle]
==>[id:9,label:person,name:daniel,location:aachen]
==>[id:10,label:software,name:gremlin]
==>[id:11,label:software,name:tinkergraph]
gremlin> g.V().has('name','marko').properties('location')
==>vp[location->san diego]
==>vp[location->santa cruz]
==>vp[location->brussels]
==>vp[location->santa fe]
gremlin> g.V().has('name','marko').properties('location').elementMap()
==>[id:6,key:location,value:san diego,startTime:1997,endTime:2001]
==>[id:7,key:location,value:santa cruz,startTime:2001,endTime:2004]
==>[id:8,key:location,value:brussels,startTime:2004,endTime:2005]
==>[id:9,key:location,value:santa fe,startTime:2005]
g.V().elementMap()
g.V().has('name','marko').properties('location')
g.V().has('name','marko').properties('location').elementMap()

Important

The elementMap()-step does not return the vertex labels for incident vertices when using GraphComputer as the id is the only available data to the star graph.

Additional References

Emit Step

The emit-step is not an actual step, but is instead a step modulator for repeat() (find more documentation on the emit() there).

Additional References

Explain Step

The explain()-step (terminal) 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.

console (groovy) groovy
gremlin> g.V().hasLabel('person').outE().identity().inV().count().is(gt(5)).explain()
==>Traversal Explanation
==========================================================================================================================================================================================
Original Traversal                    [GraphStep(vertex,[]), HasStep([~label.eq(person)]), VertexStep(OUT,edge), IdentityStep, EdgeVertexStep(IN), CountGlobalStep, IsStep(gt(5))]
ConnectiveStrategy              [D]   [GraphStep(vertex,[]), HasStep([~label.eq(person)]), VertexStep(OUT,edge), IdentityStep, EdgeVertexStep(IN), CountGlobalStep, IsStep(gt(5))]
IdentityRemovalStrategy         [O]   [GraphStep(vertex,[]), HasStep([~label.eq(person)]), VertexStep(OUT,edge), EdgeVertexStep(IN), CountGlobalStep, IsStep(gt(5))]
MatchPredicateStrategy          [O]   [GraphStep(vertex,[]), HasStep([~label.eq(person)]), VertexStep(OUT,edge), EdgeVertexStep(IN), CountGlobalStep, IsStep(gt(5))]
FilterRankingStrategy           [O]   [GraphStep(vertex,[]), HasStep([~label.eq(person)]), VertexStep(OUT,edge), EdgeVertexStep(IN), CountGlobalStep, IsStep(gt(5))]
CountStrategy                   [O]   [GraphStep(vertex,[]), HasStep([~label.eq(person)]), VertexStep(OUT,edge), EdgeVertexStep(IN), RangeGlobalStep(0,6), CountGlobalStep, IsStep(gt(5))]
EarlyLimitStrategy              [O]   [GraphStep(vertex,[]), HasStep([~label.eq(person)]), VertexStep(OUT,edge), EdgeVertexStep(IN), RangeGlobalStep(0,6), CountGlobalStep, IsStep(gt(5))]
IncidentToAdjacentStrategy      [O]   [GraphStep(vertex,[]), HasStep([~label.eq(person)]), VertexStepPlaceholder(OUT,vertex), RangeGlobalStep(0,6), CountGlobalStep, IsStep(gt(5))]
InlineFilterStrategy            [O]   [GraphStep(vertex,[]), HasStep([~label.eq(person)]), VertexStepPlaceholder(OUT,vertex), RangeGlobalStep(0,6), CountGlobalStep, IsStep(gt(5))]
ByModulatorOptimizationStrategy [O]   [GraphStep(vertex,[]), HasStep([~label.eq(person)]), VertexStepPlaceholder(OUT,vertex), RangeGlobalStep(0,6), CountGlobalStep, IsStep(gt(5))]
AdjacentToIncidentStrategy      [O]   [GraphStep(vertex,[]), HasStep([~label.eq(person)]), VertexStepPlaceholder(OUT,edge), RangeGlobalStep(0,6), CountGlobalStep, IsStep(gt(5))]
RepeatUnrollStrategy            [O]   [GraphStep(vertex,[]), HasStep([~label.eq(person)]), VertexStepPlaceholder(OUT,edge), RangeGlobalStep(0,6), CountGlobalStep, IsStep(gt(5))]
PathRetractionStrategy          [O]   [GraphStep(vertex,[]), HasStep([~label.eq(person)]), VertexStepPlaceholder(OUT,edge), RangeGlobalStep(0,6), CountGlobalStep, IsStep(gt(5))]
LazyBarrierStrategy             [O]   [GraphStep(vertex,[]), HasStep([~label.eq(person)]), VertexStepPlaceholder(OUT,edge), RangeGlobalStep(0,6), CountGlobalStep, IsStep(gt(5))]
GValueReductionStrategy         [O]   [GraphStep(vertex,[]), HasStep([~label.eq(person)]), VertexStep(OUT,edge), RangeGlobalStep(0,6), CountGlobalStep, IsStep(gt(5))]
TinkerGraphCountStrategy        [P]   [GraphStep(vertex,[]), HasStep([~label.eq(person)]), VertexStep(OUT,edge), RangeGlobalStep(0,6), CountGlobalStep, IsStep(gt(5))]
TinkerGraphStepStrategy         [P]   [TinkerGraphStep(vertex,[~label.eq(person)]), VertexStep(OUT,edge), RangeGlobalStep(0,6), CountGlobalStep, IsStep(gt(5))]
ProfileStrategy                 [F]   [TinkerGraphStep(vertex,[~label.eq(person)]), VertexStep(OUT,edge), RangeGlobalStep(0,6), CountGlobalStep, IsStep(gt(5))]
StandardVerificationStrategy    [V]   [TinkerGraphStep(vertex,[~label.eq(person)]), VertexStep(OUT,edge), RangeGlobalStep(0,6), CountGlobalStep, IsStep(gt(5))]
Final Traversal                       [TinkerGraphStep(vertex,[~label.eq(person)]), VertexStep(OUT,edge), RangeGlobalStep(0,6), CountGlobalStep, IsStep(gt(5))]
g.V().hasLabel('person').outE().identity().inV().count().is(gt(5)).explain()

For traversal profiling information, please see profile()-step.

Fail Step

The fail()-step provides a way to force a traversal to immediately fail with an exception. This feature is often helpful during debugging purposes and for validating certain conditions prior to continuing with traversal execution.

gremlin> g.V().has('person','name','peter').fold().
......1>   coalesce(unfold(),
......2>            fail('peter should exist')).
......3>   property('k',100)
==>v[6]
gremlin> g.V().has('person','name','stephen').fold().
......1>   coalesce(unfold(),
......2>            fail('stephen should exist')).
......3>   property('k',100)
fail() Step Triggered
===========================================================================================================================
Message > stephen should exist
Traverser> []
  Bulk   > 1
Traversal> fail()
Parent   > CoalesceStep [V().has("person","name","stephen").fold().coalesce(__.unfold(),__.fail()).property("k",(int) 100)]
Metadata > {}
===========================================================================================================================

The code example above exemplifies the latter use case where there is essentially an assertion that there is a vertex with a particular "name" value prior to updating the property "k" and explicitly failing when that vertex is not found.

The fail() step does not guarantee that mutations are not partially applied. Triggering fail() produces an exception, but it’s effect on any open transactions or the underlying graph’s behavior ends there. Generally speaking, mutations made to the point of fail() being triggered are applied and fail() itself has no influence on rolling back those changes. It is up to the application catching that exception to act in a fashion that will allow for that rollback. Moreover, the ability to rollback at all is graph provider dependent. For example, a basic TinkerGraph, configured without transaction support, will simply be left in a partially mutated state whether the action to rollback on fail() was implemented or not.

Additional References

Filter Step

The filter() step maps the traverser from the current object to either true or false where the latter will not pass the traverser to the next step in the process. Please see the General Steps section for more information.

Additional References

FlatMap Step

The flatMap() step maps the traverser from the current object to an Iterator of objects for the next step in the process. Please see the General Steps section for more information.

Be aware that the current traverser behavior where the traverser appears to be unaffected by state modifying steps or account as a single bulk to side effects inside the flatMap() traversal is subject to change. The following are examples of some traversals on the "modern" graph whose output may change:

gremlin> g.V(1, 1).barrier().flatMap(aggregate("x")).cap("x")
==>[v[1]]
gremlin> g.withSack(1.0f).V(1).barrier().flatMap(sack(mult).by("age")).sack()
==>1.0

Additional References

Format Step

This step is designed to simplify some string operations. In general, it is similar to the string formatting function available in many programming languages. Variable values can be picked up from Element properties, maps and scope variables.

console (groovy) groovy
gremlin> g.V().format("%{name} is %{age} years old") //// (1)
==>marko is 29 years old
==>vadas is 27 years old
==>josh is 32 years old
==>peter is 35 years old
gremlin> g.V().hasLabel("person").as("a").values("name").as("p1").select("a").in("knows").format("%{p1} knows %{name}") //// (2)
==>vadas knows marko
==>josh knows marko
gremlin> g.V().format("%{name} has %{_} connections").by(bothE().count()) //// (3)
==>marko has 3 connections
==>vadas has 1 connections
==>lop has 3 connections
==>josh has 3 connections
==>ripple has 1 connections
==>peter has 1 connections
gremlin> g.V().project("name","count").by(values("name")).by(bothE().count()).format("%{name} has %{count} connections") //// (4)
==>marko has 3 connections
==>vadas has 1 connections
==>lop has 3 connections
==>josh has 3 connections
==>ripple has 1 connections
==>peter has 1 connections
g.V().format("%{name} is %{age} years old") //// (1)
g.V().hasLabel("person").as("a").values("name").as("p1").select("a").in("knows").format("%{p1} knows %{name}") //// (2)
g.V().format("%{name} has %{_} connections").by(bothE().count()) //// (3)
g.V().project("name","count").by(values("name")).by(bothE().count()).format("%{name} has %{count} connections") //4
  1. A format() will use property values from incoming Element to produce String result.

  2. A format() will use scope variable p1 and property name to resolve variable values.

  3. A format() will use property name and traversal product for positional argument to resolve variable values.

  4. A format() will use map produced by project step to resolve variable values.

Additional References

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.

console (groovy) groovy
gremlin> g.V(1).out('knows').values('name')
==>vadas
==>josh
gremlin> g.V(1).out('knows').values('name').fold() //// (1)
==>[vadas,josh]
gremlin> g.V(1).out('knows').values('name').fold().next().getClass() //// (2)
==>class java.util.ArrayList
gremlin> g.V(1).out('knows').values('name').fold(0) {a,b -> a + b.length()} //// (3)
==>9
gremlin> g.V().values('age').fold(0) {a,b -> a + b} //// (4)
==>123
gremlin> g.V().values('age').fold(0, sum) //// (5)
==>123
gremlin> g.V().values('age').sum() //// (6)
==>123
gremlin> g.inject(["a":1],["b":2]).fold([], addAll) //// (7)
==>[[a:1],[b:2]]
g.V(1).out('knows').values('name')
g.V(1).out('knows').values('name').fold() //// (1)
g.V(1).out('knows').values('name').fold().next().getClass() //// (2)
g.V(1).out('knows').values('name').fold(0) {a,b -> a + b.length()} //// (3)
g.V().values('age').fold(0) {a,b -> a + b} //// (4)
g.V().values('age').fold(0, sum) //// (5)
g.V().values('age').sum() //// (6)
g.inject(["a":1],["b":2]).fold([], addAll) //7
  1. A parameterless fold() will aggregate all the objects into a list and then emit the list.

  2. A verification of the type of list returned.

  3. fold() can be provided two arguments —  a seed value and a reduce bi-function ("vadas" is 5 characters + "josh" with 4 characters).

  4. What is the total age of the people in the graph?

  5. The same as before, but using a built-in bi-function.

  6. The same as before, but using the sum()-step.

  7. A mechanism for merging Map instances. If a key occurs in more than a single Map, the later occurrence will replace the earlier.

Additional References

From Step

The from()-step is not an actual step, but instead is a "step-modulator" similar to as() and by(). If a step is able to accept traversals or strings then from() is the means by which they are added. The general pattern is step().from(). See to()-step.

The list of steps that support from()-modulation are: simplePath(), cyclicPath(), path(), and addE().

Javascript

The term from is a reserved word in Javascript, and therefore must be referred to in Gremlin with from_().

Python

The term from is a reserved word in Python, and therefore must be referred to in Gremlin with from_().

Additional References

Group Step

As traversers propagate across a graph as defined by a traversal, sideEffect computations are sometimes required. That is, the actual path taken or the current location of a traverser is not the ultimate output of the computation, but some other representation of the traversal. The group()-step (map/sideEffect) is one such sideEffect that organizes the objects according to some function of the object. Then, if required, that organization (a list) is reduced. An example is provided below.

console (groovy) groovy
gremlin> g.V().group().by(label) //// (1)
==>[software:[v[3],v[5]],person:[v[1],v[2],v[4],v[6]]]
gremlin> g.V().group().by(label).by('name') //// (2)
==>[software:[lop,ripple],person:[marko,vadas,josh,peter]]
gremlin> g.V().group().by(label).by(count()) //// (3)
==>[software:2,person:4]
g.V().group().by(label) //// (1)
g.V().group().by(label).by('name') //// (2)
g.V().group().by(label).by(count()) //3
  1. Group the vertices by their label.

  2. For each vertex in the group, get their name.

  3. For each grouping, what is its size?

The two projection parameters available to group() via by() are:

  1. Key-projection: What feature of the object to group on (a function that yields the map key)?

  2. Value-projection: What feature of the group to store in the key-list?

console (groovy) groovy
gremlin> g.V().group().by('age').by('name') //// (1)
==>[32:[josh],35:[peter],27:[vadas],29:[marko]]
gremlin> g.V().group().by('name').by('age') //// (2)
==>[ripple:[],peter:[35],vadas:[27],josh:[32],lop:[],marko:[29]]
g.V().group().by('age').by('name') //// (1)
g.V().group().by('name').by('age') //2
  1. The "age" property is not productive for all vertices and therefore those keys are filtered.

  2. The "age" property is not productive for all vertices and therefore those values are filtered.

Additional References

GroupCount Step

When it is important to know how many times a particular object has been at a particular part of a traversal, groupCount()-step (map/sideEffect) is used.

"What is the distribution of ages in the graph?"
console (groovy) groovy
gremlin> g.V().hasLabel('person').values('age').groupCount()
==>[32:1,35:1,27:1,29:1]
gremlin> g.V().hasLabel('person').groupCount().by('age') //// (1)
==>[32:1,35:1,27:1,29:1]
gremlin> g.V().groupCount().by('age') //// (2)
==>[32:1,35:1,27:1,29:1]
g.V().hasLabel('person').values('age').groupCount()
g.V().hasLabel('person').groupCount().by('age') //// (1)
g.V().groupCount().by('age') //2
  1. You can also supply a pre-group projection, where the provided by()-modulation determines what to group the incoming object by.

  2. The "age" property is not productive for all vertices and therefore those values are filtered.

There is one person that is 32, one person that is 35, one person that is 27, and one person that is 29.

"Iteratively walk the graph and count the number of times you see the second letter of each name."

groupcount step

console (groovy) groovy
gremlin> g.V().repeat(both().groupCount('m').by(label)).times(10).cap('m')
==>[software:19598,person:39196]
g.V().repeat(both().groupCount('m').by(label)).times(10).cap('m')

The above is interesting in that it demonstrates the use of referencing the internal Map<Object,Long> of groupCount() with a string variable. Given that groupCount() is a sideEffect-step, it simply passes the object it received to its output. Internal to groupCount(), the object’s count is incremented.

Additional References

Has Step

has step

It is possible to filter vertices, edges, and vertex properties based on their properties using has()-step (filter). There are numerous variations on has() including:

  • has(key,value): Remove the traverser if its element does not have the provided key/value property.

  • has(label, key, value): Remove the traverser if its element does not have the specified label and provided key/value property.

  • has(key,predicate): Remove the traverser if its element does not have a key value that satisfies the bi-predicate. For more information on predicates, please read A Note on Predicates.

  • hasLabel(labels…​): Remove the traverser if its element does not have any of the labels.

  • hasId(ids…​): Remove the traverser if its element does not have any of the ids.

  • hasKey(keys…​): Remove the Property traverser if it does not match one of the provided keys.

  • hasValue(values…​): Remove the Property traverser if it does not match one of the provided values.

  • has(key): Remove the traverser if its element does not have a value for the key.

  • hasNot(key): Remove the traverser if its element has a value for the key.

console (groovy) groovy
gremlin> g.V().hasLabel('person')
==>v[1]
==>v[2]
==>v[4]
==>v[6]
gremlin> g.V().hasLabel('person','name','marko')
==>v[1]
==>v[2]
==>v[4]
==>v[6]
gremlin> g.V().hasLabel('person').out().has('name',within('vadas','josh'))
==>v[2]
==>v[4]
gremlin> g.V().hasLabel('person').out().has('name',within('vadas','josh')).
               outE().hasLabel('created')
==>e[10][4-created->5]
==>e[11][4-created->3]
gremlin> g.V().has('age',inside(20,30)).values('age') //// (1)
==>29
==>27
gremlin> g.V().has('age',outside(20,30)).values('age') //// (2)
==>32
==>35
gremlin> g.V().has('name',within('josh','marko')).elementMap() //// (3)
==>[id:1,label:person,name:marko,age:29]
==>[id:4,label:person,name:josh,age:32]
gremlin> g.V().has('name',without('josh','marko')).elementMap() //// (4)
==>[id:2,label:person,name:vadas,age:27]
==>[id:3,label:software,name:lop,lang:java]
==>[id:5,label:software,name:ripple,lang:java]
==>[id:6,label:person,name:peter,age:35]
gremlin> g.V().has('name',not(within('josh','marko'))).elementMap() //// (5)
==>[id:2,label:person,name:vadas,age:27]
==>[id:3,label:software,name:lop,lang:java]
==>[id:5,label:software,name:ripple,lang:java]
==>[id:6,label:person,name:peter,age:35]
gremlin> g.V().properties().hasKey('age').value() //// (6)
==>29
==>27
==>32
==>35
gremlin> g.V().hasNot('age').values('name') //// (7)
==>lop
==>ripple
gremlin> g.V().has('person','name', startingWith('m')) //// (8)
==>v[1]
gremlin> g.V().has(null, 'vadas') //// (9)
gremlin> g.V().has('person', 'name', regex('r')).values('name') //// (10)
==>marko
==>peter
g.V().hasLabel('person')
g.V().hasLabel('person','name','marko')
g.V().hasLabel('person').out().has('name',within('vadas','josh'))
g.V().hasLabel('person').out().has('name',within('vadas','josh')).
      outE().hasLabel('created')
g.V().has('age',inside(20,30)).values('age') //// (1)
g.V().has('age',outside(20,30)).values('age') //// (2)
g.V().has('name',within('josh','marko')).elementMap() //// (3)
g.V().has('name',without('josh','marko')).elementMap() //// (4)
g.V().has('name',not(within('josh','marko'))).elementMap() //// (5)
g.V().properties().hasKey('age').value() //// (6)
g.V().hasNot('age').values('name') //// (7)
g.V().has('person','name', startingWith('m')) //// (8)
g.V().has(null, 'vadas') //// (9)
g.V().has('person', 'name', regex('r')).values('name') //10
  1. Find all vertices whose ages are between 20 (exclusive) and 30 (exclusive). In other words, the age must be greater than 20 and less than 30.

  2. Find all vertices whose ages are not between 20 (inclusive) and 30 (inclusive). In other words, the age must be less than 20 or greater than 30.

  3. Find all vertices whose names are exact matches to any names in the collection [josh,marko], display all the key,value pairs for those vertices.

  4. Find all vertices whose names are not in the collection [josh,marko], display all the key,value pairs for those vertices.

  5. Same as the prior example save using not on within to yield without.

  6. Find all age-properties and emit their value.

  7. Find all vertices that do not have an age-property and emit their name.

  8. Find all "person" vertices that have a name property that starts with the letter "m".

  9. Property key is always stored as String and therefore an equality check with null will produce no result.

  10. An example of using has() with regular expression predicate.

Additional References

Id Step

The id()-step (map) takes an Element and extracts its identifier from it.

console (groovy) groovy
gremlin> g.V().id()
==>1
==>2
==>3
==>4
==>5
==>6
gremlin> g.V(1).out().id().is(2)
==>2
gremlin> g.V(1).outE().id()
==>9
==>7
==>8
gremlin> g.V(1).properties().id()
==>0
==>1
g.V().id()
g.V(1).out().id().is(2)
g.V(1).outE().id()
g.V(1).properties().id()

Additional References

Identity Step

The identity()-step (map) is an identity function which maps the current object to itself.

console (groovy) groovy
gremlin> g.V().identity()
==>v[1]
==>v[2]
==>v[3]
==>v[4]
==>v[5]
==>v[6]
g.V().identity()

Additional References

Index Step

The index()-step (map) indexes each element in the current collection. If the current traverser’s value is not a collection, then it’s treated as a single-item collection. There are two indexers available, which can be chosen using the with() modulator. The list indexer (default) creates a list for each collection item, with the first item being the original element and the second element being the index. The map indexer created a linked hash map in which the index represents the key and the original item is used as the value.

console (groovy) groovy
gremlin> g.V().hasLabel("software").index() //// (1)
==>[[v[3],0]]
==>[[v[5],0]]
gremlin> g.V().hasLabel("software").values("name").fold().
           order(Scope.local).
           index().
           unfold().
           order().
             by(__.tail(Scope.local, 1)) //// (2)
==>[lop,0]
==>[ripple,1]
gremlin> g.V().hasLabel("software").values("name").fold().
           order(Scope.local).
           index().
             with(WithOptions.indexer, WithOptions.list).
           unfold().
           order().
             by(__.tail(Scope.local, 1)) //// (3)
==>[lop,0]
==>[ripple,1]
gremlin> g.V().hasLabel("person").values("name").fold().
           order(Scope.local).
           index().
             with(WithOptions.indexer, WithOptions.map) //// (4)
==>[0:josh,1:marko,2:peter,3:vadas]
g.V().hasLabel("software").index() //// (1)
g.V().hasLabel("software").values("name").fold().
  order(Scope.local).
  index().
  unfold().
  order().
    by(__.tail(Scope.local, 1)) //// (2)
g.V().hasLabel("software").values("name").fold().
  order(Scope.local).
  index().
    with(WithOptions.indexer, WithOptions.list).
  unfold().
  order().
    by(__.tail(Scope.local, 1)) //// (3)
g.V().hasLabel("person").values("name").fold().
  order(Scope.local).
  index().
    with(WithOptions.indexer, WithOptions.map)  //4
  1. Indexing non-collection items results in multiple indexed single-item collections.

  2. Index all software names in their alphabetical order.

  3. Same as statement 1, but with an explicitely specified list indexer.

  4. Index all person names in their alphabetical order and store the result in an ordered map.

Additional References

Inject Step

inject step

The concept of "injectable steps" makes it possible to insert objects arbitrarily into a traversal stream. In general, inject()-step (sideEffect) exists and a few examples are provided below.

console (groovy) groovy
gremlin> g.V(4).out().values('name').inject('daniel')
==>daniel
==>ripple
==>lop
gremlin> g.V(4).out().values('name').inject('daniel').map {it.get().length()}
==>6
==>6
==>3
gremlin> g.V(4).out().values('name').inject('daniel').map {it.get().length()}.path()
==>[daniel,6]
==>[v[4],v[5],ripple,6]
==>[v[4],v[3],lop,3]
g.V(4).out().values('name').inject('daniel')
g.V(4).out().values('name').inject('daniel').map {it.get().length()}
g.V(4).out().values('name').inject('daniel').map {it.get().length()}.path()

In the last example above, note that the path starting with daniel is only of length 2. This is because the daniel string was inserted half-way in the traversal. Finally, a typical use case is provided below — when the start of the traversal is not a graph object.

console (groovy) groovy
gremlin> inject(1,2)
==>1
==>2
gremlin> inject(1,2).map {it.get() + 1}
==>2
==>3
gremlin> inject(1,2).map {it.get() + 1}.map {g.V(it.get()).next()}.values('name')
==>vadas
==>lop
inject(1,2)
inject(1,2).map {it.get() + 1}
inject(1,2).map {it.get() + 1}.map {g.V(it.get()).next()}.values('name')

Additional References

Intersect Step

The intersect()-step (map) calculates the intersection between the incoming list traverser and the provided list argument. This step only expects list data (array or Iterable) and will throw an IllegalArgumentException if any other type is encountered (including null).

console (groovy) groovy
gremlin> g.V().values("name").fold().intersect(["marko","josh","james","jen"])
==>[josh,marko]
gremlin> g.V().values("name").fold().intersect(__.V().limit(2).values("name").fold())
==>[vadas,marko]
g.V().values("name").fold().intersect(["marko","josh","james","jen"])
g.V().values("name").fold().intersect(__.V().limit(2).values("name").fold())

Additional References

IO Step

gremlin io The task of importing and exporting the data of Graph instances is the job of the io()-step. By default, TinkerPop supports three formats for importing and exporting graph data in GraphML, GraphSON, and Gryo.

Note

Additional documentation for TinkerPop IO formats can be found in the IO Reference.

By itself the io()-step merely configures the kind of importing and exporting that is going to occur and it is the follow-on call to the read() or write() step that determines which of those actions will execute. Therefore, a typical usage of the io()-step would look like this:

g.io(someInputFile).read().iterate()
g.io(someOutputFile).write().iterate()

Important

The commands above are still traversals and therefore require iteration to be executed, hence the use of iterate() as a termination step.

By default, the io()-step will try to detect the right file format using the file name extension. To gain greater control of the format use the with() step modulator to provide further information to io(). For example:

g.io(someInputFile).
    with(IO.reader, IO.graphson).
  read().iterate()
g.io(someOutputFile).
    with(IO.writer,IO.graphml).
  write().iterate()

The IO class is a helper for the io()-step that provides expressions that can be used to help configure it and in this case it allows direct specification of the "reader" or "writer" to use. The "reader" actually refers to a GraphReader implementation and the "writer" refers to a GraphWriter implementation. The implementations of those interfaces provided by default are the standard TinkerPop implementations.

That default is an important point to consider for users. The default TinkerPop implementations are not designed with massive, complex, parallel bulk loading in mind. They are designed to do single-threaded, OLTP-style loading of data in the most generic way possible so as to accommodate the greatest number of graph databases out there. As such, from a reading perspective, they work best for small datasets (or perhaps medium datasets where memory is plentiful and time is not critical) that are loading to an empty graph - incremental loading is not supported. The story from the writing perspective is not that different in there are no parallel operations in play, however streaming the output to disk requires a single pass of the data without high memory requirements for larger datasets.

Important

Default graph formats don’t contain information about property cardinality, so it is up to the graph provider to choose the appropriate one. You will see a warning message if the chosen cardinality is SINGLE while your graph input contains multiple values for that property.

In general, TinkerPop recommends that users examine the native bulk import/export tools of the graph implementation that they choose. Those tools will often outperform the io()-step and perhaps be easier to use with a greater feature set. That said, graph providers do have the option to optimize io() to back it with their own import/export utilities and therefore the default behavior provided by TinkerPop described above might be overridden by the graph.

An excellent example of this lies in HadoopGraph with SparkGraphComputer which replaces the default single-threaded implementation with a more advanced OLAP style bulk import/export functionality internally using CloneVertexProgram. With this model, graphs of arbitrary size can be imported/exported assuming that there is a Hadoop InputFormat or OutputFormat to support it.

Important

Remote Gremlin Console users or Gremlin Language Variant (GLV) users (e.g. gremlin-python) who utilize the io()-step should recall that their read() or write() operation will occur on the server and not locally and therefore the file specified for import/export must be something accessible by the server.

GraphSON and Gryo formats are extensible allowing users and graph providers to extend supported serialization options. These extensions are exposed through IoRegistry implementations. To apply an IoRegistry use the with() option and the IO.registry key, where the value is either an actual IoRegistry instance or the fully qualified class name of one.

g.io(someInputFile).
    with(IO.reader, IO.gryo).
    with(IO.registry, TinkerIoRegistryV3d0.instance())
  read().iterate()
g.io(someOutputFile).
    with(IO.writer,IO.graphson).
    with(IO.registry, "org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerIoRegistryV3d0")
  write().iterate()

GLVs will obviously always be forced to use the latter form as they can’t explicitly create an instance of an IoRegistry to pass to the server (nor are IoRegistry instances necessarily serializable).

The version of the formats (e.g. GraphSON 2.0 or 3.0) utilized by io() is determined entirely by the IO.reader and IO.writer configurations or their defaults. The defaults will always be the latest version for the current release of TinkerPop. It is also possible for graph providers to override these defaults, so consult the documentation of the underlying graph database in use for any details on that.

Note

The io() step will try to automatically detect the appropriate GraphReader or GraphWriter to use based on the file extension. If the file has a different extension than the ones expected, use with() as shown above to set the reader or writer explicitly.

For more advanced configuration of GraphReader and GraphWriter operations (e.g. normalized output for GraphSON, disabling class registrations for Gryo, etc.) then construct the appropriate GraphReader and GraphWriter using the build() method on their implementations and use it directly. It can be passed directly to the IO.reader or IO.writer options. Obviously, these are JVM based operations and thus not available to GLVs as portable features.

GraphML

gremlin graphml The GraphML file format is a common XML-based representation of a graph. It is widely supported by graph-related tools and libraries making it a solid interchange format for TinkerPop. In other words, if the intent is to work with graph data in conjunction with applications outside of TinkerPop, GraphML may be the best choice to do that. Common use cases might be:

  • Generate a graph using NetworkX, export it with GraphML and import it to TinkerPop.

  • Produce a subgraph and export it to GraphML to be consumed by and visualized in Gephi.

  • Migrate the data of an entire graph to a different graph database not supported by TinkerPop.

Warning

GraphML is a "lossy" format in that it only supports primitive values for properties and does not have support for Graph variables. It will use toString to serialize property values outside of those primitives.

Warning

GraphML as a specification allows for <edge> and <node> elements to appear in any order. Most software that writes GraphML (including as TinkerPop’s GraphMLWriter) write <node> elements before <edge> elements. However it is important to note that GraphMLReader will read this data in order and order can matter. This is because TinkerPop does not allow the vertex label to be changed after the vertex has been created. Therefore, if an <edge> element comes before the <node>, the label on the vertex will be ignored. It is thus better to order <node> elements in the GraphML to appear before all <edge> elements if vertex labels are important to the graph.
// expects a file extension of .xml or .graphml to determine that
// a GraphML reader/writer should be used.
g.io("graph.xml").read().iterate();
g.io("graph.xml").write().iterate();

Note

If using GraphML generated from TinkerPop 2.x, read more about its incompatibilities in the Upgrade Documentation.

GraphSON

gremlin graphson GraphSON is a JSON-based format extended from earlier versions of TinkerPop. It is important to note that TinkerPop’s GraphSON is not backwards compatible with prior TinkerPop GraphSON versions. GraphSON has some support from graph-related application outside of TinkerPop, but it is generally best used in two cases:

  • A text format of the graph or its elements is desired (e.g. debugging, usage in source control, etc.)

  • The graph or its elements need to be consumed by code that is not JVM-based (e.g. JavaScript, Python, .NET, etc.)

// expects a file extension of .json to interpret that
// a GraphSON reader/writer should be used
g.io("graph.json").read().iterate();
g.io("graph.json").write().iterate();

Note

Additional documentation for GraphSON can be found in the IO Reference.

Gryo

gremlin kryo Kryo is a popular serialization package for the JVM. Gremlin-Kryo is a binary Graph serialization format for use on the JVM by JVM languages. It is designed to be space efficient, non-lossy and is promoted as the standard format to use when working with graph data inside of the TinkerPop stack. A list of common use cases is presented below:

  • Migration from one Gremlin Structure implementation to another (e.g. TinkerGraph to Neo4jGraph)

  • Serialization of individual graph elements to be sent over the network to another JVM.

  • Backups of in-memory graphs or subgraphs.

Warning

When migrating between Gremlin Structure implementations, Kryo may not lose data, but it is important to consider the features of each Graph and whether or not the data types supported in one will be supported in the other. Failure to do so, may result in errors.
// expects a file extension of .kryo to interpret that
// a GraphSON reader/writer should be used
g.io("graph.kryo").read().iterate()
g.io("graph.kryo").write().iterate()

Additional References

Is Step

It is possible to filter scalar values using is()-step (filter).

Python

The term is is a reserved word in Python, and therefore must be referred to in Gremlin with is_().

console (groovy) groovy
gremlin> g.V().values('age').is(32)
==>32
gremlin> g.V().values('age').is(lte(30))
==>29
==>27
gremlin> g.V().values('age').is(inside(30, 40))
==>32
==>35
gremlin> g.V().where(__.in('created').count().is(1)).values('name') //// (1)
==>ripple
gremlin> g.V().where(__.in('created').count().is(gte(2))).values('name') //// (2)
==>lop
gremlin> g.V().where(__.in('created').values('age').
                                    mean().is(inside(30d, 35d))).values('name') //// (3)
==>lop
==>ripple
g.V().values('age').is(32)
g.V().values('age').is(lte(30))
g.V().values('age').is(inside(30, 40))
g.V().where(__.in('created').count().is(1)).values('name') //// (1)
g.V().where(__.in('created').count().is(gte(2))).values('name') //// (2)
g.V().where(__.in('created').values('age').
                           mean().is(inside(30d, 35d))).values('name') //3
  1. Find projects having exactly one contributor.

  2. Find projects having two or more contributors.

  3. Find projects whose contributors average age is between 30 and 35.

Additional References

Key Step

The key()-step (map) takes a Property and extracts the key from it.

console (groovy) groovy
gremlin> g.V(1).properties().key()
==>name
==>location
==>location
==>location
==>location
gremlin> g.V(1).properties().properties().key()
==>startTime
==>endTime
==>startTime
==>endTime
==>startTime
==>endTime
==>startTime
g.V(1).properties().key()
g.V(1).properties().properties().key()

Additional References

Label Step

The label()-step (map) takes an Element and extracts its label from it.

console (groovy) groovy
gremlin> g.V().label()
==>person
==>person
==>software
==>person
==>software
==>person
gremlin> g.V(1).outE().label()
==>created
==>knows
==>knows
gremlin> g.V(1).properties().label()
==>name
==>age
g.V().label()
g.V(1).outE().label()
g.V(1).properties().label()

Additional References

Length Step

The length()-step (map) returns the length incoming string or list of string traverser. Null values are not processed and remain as null when returned. If the incoming traverser is a non-String value then an IllegalArgumentException will be thrown.

console (groovy) groovy
gremlin> g.V().values('name').length() //// (1)
==>5
==>5
==>3
==>4
==>6
==>5
gremlin> g.V().values('name').fold().length(local) //// (2)
==>[5,5,3,4,6,5]
g.V().values('name').length() //// (1)
g.V().values('name').fold().length(local) //2
  1. Return the string length of all vertex names.

  2. Use Scope.local to operate on individual string elements inside incoming list, which will return a list.

Additional References

Limit Step

The limit()-step is analogous to range()-step save that the lower end range is set to 0.

console (groovy) groovy
gremlin> g.V().limit(2)
==>v[1]
==>v[2]
gremlin> g.V().range(0, 2)
==>v[1]
==>v[2]
g.V().limit(2)
g.V().range(0, 2)

The limit()-step can also be applied with Scope.local, in which case it operates on the incoming collection. The examples below use the The Crew toy data set.

console (groovy) groovy
gremlin> g.V().valueMap().select('location').limit(local,2) //// (1)
==>[san diego,santa cruz]
==>[centreville,dulles]
==>[bremen,baltimore]
==>[spremberg,kaiserslautern]
gremlin> g.V().valueMap().limit(local, 1) //// (2)
==>[name:[marko]]
==>[name:[stephen]]
==>[name:[matthias]]
==>[name:[daniel]]
==>[name:[gremlin]]
==>[name:[tinkergraph]]
gremlin> g.V().valueMap().select('location').limit(local, 1) //// (3)
==>[san diego]
==>[centreville]
==>[bremen]
==>[spremberg]
gremlin> g.V().valueMap().select('location').limit(local, 1).unfold() //// (4)
==>san diego
==>centreville
==>bremen
==>spremberg
g.V().valueMap().select('location').limit(local,2) //// (1)
g.V().valueMap().limit(local, 1) //// (2)
g.V().valueMap().select('location').limit(local, 1) //// (3)
g.V().valueMap().select('location').limit(local, 1).unfold() //4
  1. List<String> for each vertex containing the first two locations.

  2. Map<String, Object> for each vertex, but containing only the first property value.

  3. List<String> for each vertex containing the first location.

  4. String for each vertex containing the first location (use unfold() to extract single elements from singleton collections).

Additional References

Local Step

local step

A GraphTraversal operates on a continuous stream of objects. In many situations, it is important to operate on a single element within that stream. To do such object-local traversal computations, local()-step exists (branch). Note that the examples below use the The Crew toy data set.

console (groovy) groovy
gremlin> g.V().as('person').
               properties('location').order().by('startTime',asc).limit(2).value().as('location').
               select('person','location').by('name').by() //// (1)
==>[person:daniel,location:spremberg]
==>[person:stephen,location:centreville]
gremlin> g.V().as('person').
               local(properties('location').order().by('startTime',asc).limit(2)).value().as('location').
               select('person','location').by('name').by() //// (2)
==>[person:marko,location:san diego]
==>[person:marko,location:santa cruz]
==>[person:stephen,location:centreville]
==>[person:stephen,location:dulles]
==>[person:matthias,location:bremen]
==>[person:matthias,location:baltimore]
==>[person:daniel,location:spremberg]
==>[person:daniel,location:kaiserslautern]
g.V().as('person').
      properties('location').order().by('startTime',asc).limit(2).value().as('location').
      select('person','location').by('name').by() //// (1)
g.V().as('person').
      local(properties('location').order().by('startTime',asc).limit(2)).value().as('location').
      select('person','location').by('name').by() //2
  1. Get the first two people and their respective location according to the most historic location start time.

  2. For every person, get their two most historic locations.

The two traversals above look nearly identical save the inclusion of local() which wraps a section of the traversal in an object-local traversal. As such, the order().by() and the limit() refer to a particular object, not to the stream as a whole.

Local Step is quite similar in functionality to Flat Map Step where it can often be confused. The primary distinction between these steps is that while local() preserves the path history of traversers as they pass through its child traversal, flatMap() does not. As another example consider:

console (groovy) groovy
gremlin> g.V().local(outE().inV()).path()
==>[v[1],e[9][1-created->3],v[3]]
==>[v[1],e[7][1-knows->2],v[2]]
==>[v[1],e[8][1-knows->4],v[4]]
==>[v[4],e[10][4-created->5],v[5]]
==>[v[4],e[11][4-created->3],v[3]]
==>[v[6],e[12][6-created->3],v[3]]
gremlin> g.V().flatMap(outE().inV()).path()
==>[v[1],v[3]]
==>[v[1],v[2]]
==>[v[1],v[4]]
==>[v[4],v[5]]
==>[v[4],v[3]]
==>[v[6],v[3]]
g.V().local(outE().inV()).path()
g.V().flatMap(outE().inV()).path()

Warning

The anonymous traversal of local() processes the current object "locally." In OLAP, where the atomic unit of computing is the vertex and its local "star graph," it is important that the anonymous traversal does not leave the confines of the vertex’s star graph. In other words, it can not traverse to an adjacent vertex’s properties or edges.

Additional References

Loops Step

The loops()-step (map) extracts the number of times the Traverser has gone through the current loop.

console (groovy) groovy
gremlin> g.V().emit(__.has("name", "marko").or().loops().is(2)).repeat(__.out()).values("name")
==>marko
==>ripple
==>lop
g.V().emit(__.has("name", "marko").or().loops().is(2)).repeat(__.out()).values("name")

Additional References

LTrim Step

The lTrim()-step (map) returns a string with leading whitespace removed. Null values are not processed and remain as null when returned. If the incoming traverser is a non-String value then an IllegalArgumentException will be thrown.

console (groovy) groovy
gremlin> g.inject("   hello   ", " world ", null).lTrim()
==>hello
==>world
==>null
gremlin> g.inject(["   hello   ", " world ", null]).lTrim(local) //// (1)
==>[hello   ,world ,null]
g.inject("   hello   ", " world ", null).lTrim()
g.inject(["   hello   ", " world ", null]).lTrim(local) //1
  1. Use Scope.local to operate on individual string elements inside incoming list, which will return a list.

Map Step

The map() step maps the traverser from the current object to the next step in the process. Please see the General Steps section for more information.

Additional References

Match Step

The match()-step (map) provides a more declarative form of graph querying based on the notion of pattern matching. With match(), the user provides a collection of "traversal fragments," called patterns, that have variables defined that must hold true throughout the duration of the match(). When a traverser is in match(), a registered MatchAlgorithm analyzes the current state of the traverser (i.e. its history based on its path data), the runtime statistics of the traversal patterns, and returns a traversal-pattern that the traverser should try next. The default MatchAlgorithm provided is called CountMatchAlgorithm and it dynamically revises the pattern execution plan by sorting the patterns according to their filtering capabilities (i.e. largest set reduction patterns execute first). For very large graphs, where the developer is uncertain of the statistics of the graph (e.g. how many knows-edges vs. worksFor-edges exist in the graph), it is advantageous to use match(), as an optimal plan will be determined automatically. Furthermore, some queries are much easier to express via match() than with single-path traversals.

"Who created a project named 'lop' that was also created by someone who is 29 years old? Return the two creators."

match step

console (groovy) groovy
gremlin> g.V().match(
                 __.as('a').out('created').as('b'),
                 __.as('b').has('name', 'lop'),
                 __.as('b').in('created').as('c'),
                 __.as('c').has('age', 29)).
               select('a','c').by('name')
==>[a:marko,c:marko]
==>[a:josh,c:marko]
==>[a:peter,c:marko]
g.V().match(
        __.as('a').out('created').as('b'),
        __.as('b').has('name', 'lop'),
        __.as('b').in('created').as('c'),
        __.as('c').has('age', 29)).
      select('a','c').by('name')

Note that the above can also be more concisely written as below which demonstrates that standard inner-traversals can be arbitrarily defined.

console (groovy) groovy
gremlin> g.V().match(
                 __.as('a').out('created').has('name', 'lop').as('b'),
                 __.as('b').in('created').has('age', 29).as('c')).
               select('a','c').by('name')
==>[a:marko,c:marko]
==>[a:josh,c:marko]
==>[a:peter,c:marko]
g.V().match(
        __.as('a').out('created').has('name', 'lop').as('b'),
        __.as('b').in('created').has('age', 29).as('c')).
      select('a','c').by('name')

In order to improve readability, as()-steps can be given meaningful labels which better reflect your domain. The previous query can thus be written in a more expressive way as shown below.

console (groovy) groovy
gremlin> g.V().match(
                 __.as('creators').out('created').has('name', 'lop').as('projects'), //// (1)
                 __.as('projects').in('created').has('age', 29).as('cocreators')). //// (2)
               select('creators','cocreators').by('name') //// (3)
==>[creators:marko,cocreators:marko]
==>[creators:josh,cocreators:marko]
==>[creators:peter,cocreators:marko]
g.V().match(
        __.as('creators').out('created').has('name', 'lop').as('projects'), //// (1)
        __.as('projects').in('created').has('age', 29).as('cocreators')). //// (2)
      select('creators','cocreators').by('name') //3
  1. Find vertices that created something and match them as 'creators', then find out what they created which is named 'lop' and match these vertices as 'projects'.

  2. Using these 'projects' vertices, find out their creators aged 29 and remember these as 'cocreators'.

  3. Return the name of both 'creators' and 'cocreators'.

grateful dead schema

Figure 4. Grateful Dead

MatchStep brings functionality similar to SPARQL to Gremlin. Like SPARQL, MatchStep conjoins a set of patterns applied to a graph. For example, the following traversal finds exactly those songs which Jerry Garcia has both sung and written (using the Grateful Dead graph distributed in the data/ directory):

console (groovy) groovy
gremlin> g = traversal().with(graph)
==>graphtraversalsource[tinkergraph[vertices:0 edges:0], standard]
gremlin> g.io('data/grateful-dead.xml').read().iterate()
gremlin> g.V().match(
                 __.as('a').has('name', 'Garcia'),
                 __.as('a').in('writtenBy').as('b'),
                 __.as('a').in('sungBy').as('b')).
               select('b').values('name')
==>CREAM PUFF WAR
==>CRYPTICAL ENVELOPMENT
g = traversal().with(graph)
g.io('data/grateful-dead.xml').read().iterate()
g.V().match(
        __.as('a').has('name', 'Garcia'),
        __.as('a').in('writtenBy').as('b'),
        __.as('a').in('sungBy').as('b')).
      select('b').values('name')

Among the features which differentiate match() from SPARQL are:

console (groovy) groovy
gremlin> g.V().match(
                 __.as('a').out('created').has('name','lop').as('b'), //// (1)
                 __.as('b').in('created').has('age', 29).as('c'),
                 __.as('c').repeat(out()).times(2)). //// (2)
               select('c').out('knows').dedup().values('name') //// (3)
==>vadas
==>josh
g.V().match(
        __.as('a').out('created').has('name','lop').as('b'), //// (1)
        __.as('b').in('created').has('age', 29).as('c'),
        __.as('c').repeat(out()).times(2)). //// (2)
      select('c').out('knows').dedup().values('name') //3
  1. Patterns of arbitrary complexity: match() is not restricted to triple patterns or property paths.

  2. Recursion support: match() supports the branch-based steps within a pattern, including repeat().

  3. Imperative/declarative hybrid: Before and after a match(), it is possible to leverage classic Gremlin traversals.

To extend point #3, it is possible to support going from imperative, to declarative, to imperative, ad infinitum.

console (groovy) groovy
gremlin> g.V().match(
                 __.as('a').out('knows').as('b'),
                 __.as('b').out('created').has('name','lop')).
               select('b').out('created').
                 match(
                   __.as('x').in('created').as('y'),
                   __.as('y').out('knows').as('z')).
               select('z').values('name')
==>vadas
==>josh
g.V().match(
        __.as('a').out('knows').as('b'),
        __.as('b').out('created').has('name','lop')).
      select('b').out('created').
        match(
          __.as('x').in('created').as('y'),
          __.as('y').out('knows').as('z')).
      select('z').values('name')

Important

The match()-step is stateless. The variable bindings of the traversal patterns are stored in the path history of the traverser. As such, the variables used over all match()-steps within a traversal are globally unique. A benefit of this is that subsequent where(), select(), match(), etc. steps can leverage the same variables in their analysis.

Like all other steps in Gremlin, match() is a function and thus, match() within match() is a natural consequence of Gremlin’s functional foundation (i.e. recursive matching).

console (groovy) groovy
gremlin> g.V().match(
                 __.as('a').out('knows').as('b'),
                 __.as('b').out('created').has('name','lop'),
                 __.as('b').match(
                              __.as('b').out('created').as('c'),
                              __.as('c').has('name','ripple')).
                            select('c').as('c')).
               select('a','c').by('name')
==>[a:marko,c:ripple]
g.V().match(
        __.as('a').out('knows').as('b'),
        __.as('b').out('created').has('name','lop'),
        __.as('b').match(
                     __.as('b').out('created').as('c'),
                     __.as('c').has('name','ripple')).
                   select('c').as('c')).
      select('a','c').by('name')

If a step-labeled traversal proceeds the match()-step and the traverser entering the match() is destined to bind to a particular variable, then the previous step should be labeled accordingly.

console (groovy) groovy
gremlin> g.V().as('a').out('knows').as('b').
           match(
             __.as('b').out('created').as('c'),
             __.not(__.as('c').in('created').as('a'))).
           select('a','b','c').by('name')
==>[a:marko,b:josh,c:ripple]
g.V().as('a').out('knows').as('b').
  match(
    __.as('b').out('created').as('c'),
    __.not(__.as('c').in('created').as('a'))).
  select('a','b','c').by('name')

There are three types of match() traversal patterns.

  1. as('a')…​as('b'): both the start and end of the traversal have a declared variable.

  2. as('a')…​: only the start of the traversal has a declared variable.

  3. …​: there are no declared variables.

If a variable is at the start of a traversal pattern it must exist as a label in the path history of the traverser else the traverser can not go down that path. If a variable is at the end of a traversal pattern then if the variable exists in the path history of the traverser, the traverser’s current location must match (i.e. equal) its historic location at that same label. However, if the variable does not exist in the path history of the traverser, then the current location is labeled as the variable and thus, becomes a bound variable for subsequent traversal patterns. If a traversal pattern does not have an end label, then the traverser must simply "survive" the pattern (i.e. not be filtered) to continue to the next pattern. If a traversal pattern does not have a start label, then the traverser can go down that path at any point, but will only go down that pattern once as a traversal pattern is executed once and only once for the history of the traverser. Typically, traversal patterns that do not have a start and end label are used in conjunction with and(), or(), and where(). Once the traverser has "survived" all the patterns (or at least one for or()), match()-step analyzes the traverser’s path history and emits a Map<String,Object> of the variable bindings to the next step in the traversal.

console (groovy) groovy
gremlin> g.V().as('a').out().as('b'). //// (1)
             match( //// (2)
               __.as('a').out().count().as('c'), //// (3)
               __.not(__.as('a').in().as('b')), //// (4)
               or( //// (5)
                 __.as('a').out('knows').as('b'),
                 __.as('b').in().count().as('c').and().as('c').is(gt(2)))). //// (6)
             dedup('a','c'). //// (7)
             select('a','b','c').by('name').by('name').by() //// (8)
==>[a:marko,b:lop,c:3]
g.V().as('a').out().as('b'). //// (1)
    match( //// (2)
      __.as('a').out().count().as('c'), //// (3)
      __.not(__.as('a').in().as('b')), //// (4)
      or( //// (5)
        __.as('a').out('knows').as('b'),
        __.as('b').in().count().as('c').and().as('c').is(gt(2)))). //// (6)
    dedup('a','c'). //// (7)
    select('a','b','c').by('name').by('name').by() //8
  1. A standard, step-labeled traversal can come prior to match().

  2. If the traverser’s path prior to entering match() has requisite label values, then those historic values are bound.

  3. It is possible to use barrier steps though they are computed locally to the pattern (as one would expect).

  4. It is possible to not() a pattern.

  5. It is possible to nest and()- and or()-steps for conjunction matching.

  6. Both infix and prefix conjunction notation is supported.

  7. It is possible to "distinct" the specified label combination.

  8. The bound values are of different types — vertex ("a"), vertex ("b"), long ("c").

Using Where with Match

Match is typically used in conjunction with both select() (demonstrated previously) and where() (presented here). A where()-step allows the user to further constrain the result set provided by match().

console (groovy) groovy
gremlin> g.V().match(
                 __.as('a').out('created').as('b'),
                 __.as('b').in('created').as('c')).
                 where('a', neq('c')).
               select('a','c').by('name')
==>[a:marko,c:josh]
==>[a:marko,c:peter]
==>[a:josh,c:marko]
==>[a:josh,c:peter]
==>[a:peter,c:marko]
==>[a:peter,c:josh]
g.V().match(
        __.as('a').out('created').as('b'),
        __.as('b').in('created').as('c')).
        where('a', neq('c')).
      select('a','c').by('name')

The where()-step can take either a P-predicate (example above) or a Traversal (example below). Using MatchPredicateStrategy, where()-clauses are automatically folded into match() and thus, subject to the query optimizer within match()-step.

console (groovy) groovy
gremlin> traversal = g.V().match(
                             __.as('a').has(label,'person'), //// (1)
                             __.as('a').out('created').as('b'),
                             __.as('b').in('created').as('c')).
                             where(__.as('a').out('knows').as('c')). //// (2)
                           select('a','c').by('name'); null //// (3)
==>null
gremlin> traversal.toString() //// (4)
==>[GraphStep(vertex,[]), MatchStep(null,AND,[[MatchStartStep(a), HasStep([~label.eq(person)]), MatchEndStep(null)], [MatchStartStep(a), VertexStep(OUT,[created],vertex), MatchEndStep(b)], [MatchStartStep(b), VertexStep(IN,[created],vertex), MatchEndStep(c)]]), WhereTraversalStep([WhereStartStep(a), VertexStep(OUT,[knows],vertex), WhereEndStep(c)]), SelectStep(last,[a, c],[value(name)])]
gremlin> traversal // // (5) (6)
==>[a:marko,c:josh]
gremlin> traversal.toString() //// (7)
==>[TinkerGraphStep(vertex,[~label.eq(person)])@[a], MatchStep(null,AND,[[MatchStartStep(a), VertexStep(OUT,[created],vertex), MatchEndStep(b)], [MatchStartStep(b), VertexStep(IN,[created],vertex), MatchEndStep(c)], [MatchStartStep(a), WhereTraversalStep([WhereStartStep(null), VertexStep(OUT,[knows],vertex), WhereEndStep(c)]), MatchEndStep(null)]]), SelectStep(last,[a, c],[value(name)])]
traversal = g.V().match(
                    __.as('a').has(label,'person'), //// (1)
                    __.as('a').out('created').as('b'),
                    __.as('b').in('created').as('c')).
                    where(__.as('a').out('knows').as('c')). //// (2)
                  select('a','c').by('name'); null //// (3)
traversal.toString() //// (4)
traversal // // (5) (6) (5)
traversal.toString() //7
  1. Any has()-step traversal patterns that start with the match-key are pulled out of match() to enable the graph system to leverage the filter for index lookups.

  2. A where()-step with a traversal containing variable bindings declared in match().

  3. A useful trick to ensure that the traversal is not iterated by Gremlin Console.

  4. The string representation of the traversal prior to its strategies being applied.

  5. The Gremlin Console will automatically iterate anything that is an iterator or is iterable.

  6. Both marko and josh are co-developers and marko knows josh.

  7. The string representation of the traversal after the strategies have been applied (and thus, where() is folded into match())

Important

A where()-step is a filter and thus, variables within a where() clause are not globally bound to the path of the traverser in match(). As such, where()-steps in match() are used for filtering, not binding.

Additional References

Math Step

The math()-step (math) enables scientific calculator functionality within Gremlin. This step deviates from the common function composition and nesting formalisms to provide an easy to read string-based math processor. Variables within the equation map to scopes in Gremlin — e.g. path labels, side-effects, or incoming map keys. This step supports by()-modulation where the by()-modulators are applied in the order in which the variables are first referenced within the equation. Note that the reserved variable _ refers to the current numeric traverser object incoming to the math()-step.

console (groovy) groovy
gremlin> g.V().as('a').out('knows').as('b').math('a + b').by('age')
==>56.0
==>61.0
gremlin> g.V().as('a').out('created').as('b').
           math('b + a').
             by(both().count().math('_ + 100')).
             by('age')
==>132.0
==>133.0
==>135.0
==>138.0
gremlin> g.withSideEffect('x',10).V().values('age').math('_ / x')
==>2.9
==>2.7
==>3.2
==>3.5
gremlin> g.withSack(1).V(1).repeat(sack(sum).by(constant(1))).times(10).emit().sack().math('sin _')
==>0.9092974268256817
==>0.1411200080598672
==>-0.7568024953079282
==>-0.9589242746631385
==>-0.27941549819892586
==>0.6569865987187891
==>0.9893582466233818
==>0.4121184852417566
==>-0.5440211108893698
==>-0.9999902065507035
gremlin> g.V().math('_+1').by('age') //// (1)
==>30.0
==>28.0
==>33.0
==>36.0
g.V().as('a').out('knows').as('b').math('a + b').by('age')
g.V().as('a').out('created').as('b').
  math('b + a').
    by(both().count().math('_ + 100')).
    by('age')
g.withSideEffect('x',10).V().values('age').math('_ / x')
g.withSack(1).V(1).repeat(sack(sum).by(constant(1))).times(10).emit().sack().math('sin _')
g.V().math('_+1').by('age') //1
  1. The "age" property is not productive for all vertices and therefore those values are filtered.

The operators supported by the calculator include: *, +, /, ^, and %. Furthermore, the following built in functions are provided:

  • abs: absolute value

  • acos: arc cosine

  • asin: arc sine

  • atan: arc tangent

  • cbrt: cubic root

  • ceil: nearest upper integer

  • cos: cosine

  • cosh: hyperbolic cosine

  • exp: euler’s number raised to the power (e^x)

  • floor: nearest lower integer

  • log: logarithmus naturalis (base e)

  • log10: logarithm (base 10)

  • log2: logarithm (base 2)

  • sin: sine

  • sinh: hyperbolic sine

  • sqrt: square root

  • tan: tangent

  • tanh: hyperbolic tangent

  • signum: signum function

Additional References

Max Step

The max()-step (map) operates on a stream of comparable objects and determines which is the last object according to its natural order in the stream.

console (groovy) groovy
gremlin> g.V().values('age').max()
==>35
gremlin> g.V().repeat(both()).times(3).values('age').max()
==>35
gremlin> g.V().values('name').max()
==>vadas
g.V().values('age').max()
g.V().repeat(both()).times(3).values('age').max()
g.V().values('name').max()

When called as max(local) it determines the maximum value of the current, local object (not the objects in the traversal stream). This works for Collection and Comparable-type objects.

console (groovy) groovy
gremlin> g.V().values('age').fold().max(local)
==>35
g.V().values('age').fold().max(local)

When there are null values being evaluated the null objects are ignored, but if all values are recognized as null the return value is null.

console (groovy) groovy
gremlin> g.inject(null,10, 9, null).max()
==>10
gremlin> g.inject([null,null,null]).max(local)
==>null
g.inject(null,10, 9, null).max()
g.inject([null,null,null]).max(local)

Additional References

Mean Step

The mean()-step (map) operates on a stream of numbers and determines the average of those numbers.

console (groovy) groovy
gremlin> g.V().values('age').mean()
==>30.75
gremlin> g.V().repeat(both()).times(3).values('age').mean() //// (1)
==>30.645833333333332
gremlin> g.V().repeat(both()).times(3).values('age').dedup().mean()
==>30.75
g.V().values('age').mean()
g.V().repeat(both()).times(3).values('age').mean() //// (1)
g.V().repeat(both()).times(3).values('age').dedup().mean()
  1. Realize that traversers are being bulked by repeat(). There may be more of a particular number than another, thus altering the average.

When called as mean(local) it determines the mean of the current, local object (not the objects in the traversal stream). This works for Collection and Number-type objects.

console (groovy) groovy
gremlin> g.V().values('age').fold().mean(local)
==>30.75
g.V().values('age').fold().mean(local)

If mean() encounters null values, they will be ignored (i.e. their traversers not counted toward toward the divisor). If all traversers are null then the stream will return null.

console (groovy) groovy
gremlin> g.inject(null,10, 9, null).mean()
==>9.5
gremlin> g.inject([null,null,null]).mean(local)
==>null
g.inject(null,10, 9, null).mean()
g.inject([null,null,null]).mean(local)

Additional References

Merge Step

The merge()-step (map) combines collections like lists and maps. It expects an incoming traverser to contain a collection objection and will combine that object with its specified argument which must be of a matching type. This is also known as the union operation. If the incoming traverser or its associated argument do not meet the expected type, the step will throw an IllegalArgumentException if any other type is encountered (including null). This step differs from the combine()-step in that it doesn’t allow duplicates.

console (groovy) groovy
gremlin> g.V().values("name").fold().merge(["james","jen","marko","vadas"])
==>[jen,ripple,peter,vadas,james,josh,lop,marko]
gremlin> g.V().values("name").fold().merge(__.constant("james").fold())
==>[ripple,peter,vadas,james,josh,lop,marko]
gremlin> g.V().hasLabel('software').elementMap().merge([year:2009])
==>[id:3,name:lop,label:software,lang:java,year:2009]
==>[id:5,name:ripple,label:software,lang:java,year:2009]
g.V().values("name").fold().merge(["james","jen","marko","vadas"])
g.V().values("name").fold().merge(__.constant("james").fold())
g.V().hasLabel('software').elementMap().merge([year:2009])

Additional References

MergeEdge Step

The mergeE() step is used to add edges and their properties to a graph in a "create if not exist" fashion. The mergeE() step can also be used to find edges matching a given pattern. The input passed to mergeE() can be either a Map, or a child traversal that produces a Map.

Note

There is a corresponding mergeV() step that can be used when creating vertices.

Additionally, option() modulators may be combined with mergeE() to take action depending on whether a vertex was created, or already existed. There are various ways that mergeE() can be used. The simplest being to provide a single Map of keys and values, along with the source and target vertex IDs, as a parameter. A T.id and a T.label may also be provided but this is optional. The mergeE() step can be used directly from the GraphTraversalSource - g, or in the middle of a traversal. For a match with an existing vertex to occur, all values in the Map must exist on a vertex; otherwise, a new vertex will be created. The examples that follow show how mergeE() can be used to add relationships between dogs in the graph.

console (groovy) groovy
gremlin> g.mergeV([(T.id):1,(T.label):'Dog',name:'Toby'])
==>v[1]
gremlin> g.mergeV([(T.id):2,(T.label):'Dog',name:'Brandy']) //// (1)
==>v[2]
gremlin> g.mergeE([(T.label):'Sibling',created:'2022-02-07',(Direction.from):1,(Direction.to):2]) //// (2)
==>e[2][1-Sibling->2]
gremlin> g.E().elementMap()
==>[id:2,label:Sibling,IN:[id:2,label:Dog],OUT:[id:1,label:Dog],created:2022-02-07]
g.mergeV([(T.id):1,(T.label):'Dog',name:'Toby'])
g.mergeV([(T.id):2,(T.label):'Dog',name:'Brandy']) //// (1)
g.mergeE([(T.label):'Sibling',created:'2022-02-07',(Direction.from):1,(Direction.to):2]) //// (2)
g.E().elementMap()
  1. Create two vertices with ID values of 1 and 2.

  2. Create a "Sibling" relationship between the vertices.

Note

The example above is written with gremlin-groovy and evaluated in Gremlin Console as a Groovy script thus allowing Groovy syntax for initializing a Map.

For a mergeE() step to succeed, both the from and to vertices must already exist. It is not possible to create new vertices directly using mergeE(), but mergeV() and mergeE() steps can be combined, in a single query, to achieve that goal.

Note

The mergeE() step will not create vertices that do not exist. In those cases an error will be returned.

If the Direction enum has been statically included, its explicit use can be omitted from the query.

console (groovy) groovy
gremlin> g.mergeV([(T.id):1,(T.label):'Dog',name:'Toby'])
==>v[1]
gremlin> g.mergeV([(T.id):2,(T.label):'Dog',name:'Brandy'])
==>v[2]
gremlin> g.mergeE([(T.label):'Sibling',created:'2022-02-07',(from):1,(to):2])
==>e[2][1-Sibling->2]
gremlin> g.E().elementMap()
==>[id:2,label:Sibling,IN:[id:2,label:Dog],OUT:[id:1,label:Dog],created:2022-02-07]
g.mergeV([(T.id):1,(T.label):'Dog',name:'Toby'])
g.mergeV([(T.id):2,(T.label):'Dog',name:'Brandy'])
g.mergeE([(T.label):'Sibling',created:'2022-02-07',(from):1,(to):2])
g.E().elementMap()

One or more option() steps can be used to control the behavior when an edge is created or updated. Similar to mergeV(), the onCreate Map inherits from the main merge argument - any existence criteria in the main merge argument (T.id, T.label, Direction.OUT, Direction.IN) will be automatically carried over to the onCreate action, and these existence criteria cannot be overriden in the onCreate Map.

console (groovy) groovy
gremlin> g.mergeV([(T.id):1,(T.label):'Dog',name:'Toby'])
==>v[1]
gremlin> g.mergeV([(T.id):2,(T.label):'Dog',name:'Brandy'])
==>v[2]
gremlin> g.withSideEffect('map',[(T.label):'Sibling',(from):1,(to):2]).
           mergeE(select('map')).
             option(Merge.onCreate,[created:'2022-02-07']). //// (1)
             option(Merge.onMatch,[updated:'2022-02-07'])
==>e[2][1-Sibling->2]
gremlin> g.E().elementMap()
==>[id:2,label:Sibling,IN:[id:2,label:Dog],OUT:[id:1,label:Dog],created:2022-02-07]
gremlin> g.withSideEffect('map',[(T.label):'Sibling',(from):1,(to):2]).
           mergeE(select('map')).
             option(Merge.onCreate,[created:'2022-02-07']).
             option(Merge.onMatch,[updated:'2022-02-07']) //// (2)
==>e[2][1-Sibling->2]
gremlin> g.E().elementMap()
==>[id:2,label:Sibling,IN:[id:2,label:Dog],OUT:[id:1,label:Dog],created:2022-02-07,updated:2022-02-07]
g.mergeV([(T.id):1,(T.label):'Dog',name:'Toby'])
g.mergeV([(T.id):2,(T.label):'Dog',name:'Brandy'])
g.withSideEffect('map',[(T.label):'Sibling',(from):1,(to):2]).
  mergeE(select('map')).
    option(Merge.onCreate,[created:'2022-02-07']). //// (1)
    option(Merge.onMatch,[updated:'2022-02-07'])
g.E().elementMap()
g.withSideEffect('map',[(T.label):'Sibling',(from):1,(to):2]).
  mergeE(select('map')).
    option(Merge.onCreate,[created:'2022-02-07']).
    option(Merge.onMatch,[updated:'2022-02-07']) //// (2)
g.E().elementMap()
  1. The edge did not exist - set the created date.

  2. The edge did exist - set the updated date.

More than one edge can be created by a single mergeE() operation. This is done by injecting a list of maps into the traversal and letting them stream into the mergeE() step.

console (groovy) groovy
gremlin> maps = [[(T.label):'Siblings',(from):1,(to):2],
                 [(T.label):'Siblings',(from):1,(to):3]]
==>[label:Siblings,OUT:1,IN:2]
==>[label:Siblings,OUT:1,IN:3]
gremlin> g.mergeV([(T.id):1,(T.label):'Dog',name:'Toby']) //// (1)
==>v[1]
gremlin> g.mergeV([(T.id):2,(T.label):'Dog',name:'Brandy'])
==>v[2]
gremlin> g.mergeV([(T.id):3,(T.label):'Dog',name:'Dax'])
==>v[3]
gremlin> g.inject(maps).unfold().mergeE() //// (2)
==>e[3][1-Siblings->2]
==>e[4][1-Siblings->3]
gremlin> g.E().elementMap()
==>[id:3,label:Siblings,IN:[id:2,label:Dog],OUT:[id:1,label:Dog]]
==>[id:4,label:Siblings,IN:[id:3,label:Dog],OUT:[id:1,label:Dog]]
maps = [[(T.label):'Siblings',(from):1,(to):2],
        [(T.label):'Siblings',(from):1,(to):3]]
g.mergeV([(T.id):1,(T.label):'Dog',name:'Toby']) //// (1)
g.mergeV([(T.id):2,(T.label):'Dog',name:'Brandy'])
g.mergeV([(T.id):3,(T.label):'Dog',name:'Dax'])
g.inject(maps).unfold().mergeE() //// (2)
g.E().elementMap()
  1. Create three dogs.

  2. Stream the edge maps into mergeE() steps.

The mergeE step can be combined with the mergeV step (or any other step producing a Vertex) using the Merge.outV and Merge.inV option modulators. These options can be used to "late-bind" the OUT and IN vertices in the main merge argument and in the onCreate argument:

console (groovy) groovy
gremlin> g.mergeV([(T.id):1,(T.label):'Dog',name:'Toby']).as('Toby').
           mergeV([(T.id):2,(T.label):'Dog',name:'Brandy']).as('Brandy').
           mergeE([(T.label):'Sibling',created:'2022-02-07',(from):Merge.outV,(to):Merge.inV]).
             option(Merge.outV, select('Toby')).
             option(Merge.inV, select('Brandy'))
==>e[2][1-Sibling->2]
gremlin> g.E().elementMap()
==>[id:2,label:Sibling,IN:[id:2,label:Dog],OUT:[id:1,label:Dog],created:2022-02-07]
g.mergeV([(T.id):1,(T.label):'Dog',name:'Toby']).as('Toby').
  mergeV([(T.id):2,(T.label):'Dog',name:'Brandy']).as('Brandy').
  mergeE([(T.label):'Sibling',created:'2022-02-07',(from):Merge.outV,(to):Merge.inV]).
    option(Merge.outV, select('Toby')).
    option(Merge.inV, select('Brandy'))
g.E().elementMap()

The Merge.outV and Merge.inV tokens can be used as placeholders for values for Direction.OUT and Direction.IN respectively in the mergeE arguments. These options can produce Vertices, as in the example above, or they can specify Maps, which will be used to search for Vertices in the graph. This is useful when the exact T.id of the from/to vertices is not known in advance:

console (groovy) groovy
gremlin> g.mergeV([(T.label):'Dog',name:'Toby'])
==>v[0]
gremlin> g.mergeV([(T.label):'Dog',name:'Brandy'])
==>v[2]
gremlin> g.mergeE([(T.label):'Sibling',created:'2022-02-07',(from):Merge.outV,(to):Merge.inV]).
           option(Merge.outV, [(T.label):'Dog',name:'Toby']).
           option(Merge.inV, [(T.label):'Dog',name:'Brandy'])
==>e[4][0-Sibling->2]
gremlin> g.E().elementMap()
==>[id:4,label:Sibling,IN:[id:2,label:Dog],OUT:[id:0,label:Dog],created:2022-02-07]
g.mergeV([(T.label):'Dog',name:'Toby'])
g.mergeV([(T.label):'Dog',name:'Brandy'])
g.mergeE([(T.label):'Sibling',created:'2022-02-07',(from):Merge.outV,(to):Merge.inV]).
  option(Merge.outV, [(T.label):'Dog',name:'Toby']).
  option(Merge.inV, [(T.label):'Dog',name:'Brandy'])
g.E().elementMap()

Additional References

MergeVertex Step

The mergeV() -step is used to add vertices and their properties to a graph in a "create if not exist" fashion. The mergeV() step can also be used to find vertices matching a given pattern. The input passed to mergeV() can be either a Map, or a child Traversal that produces a Map.

Note

There is a corresponding mergeE() step that can be used when creating edges.

Additionally, option() modulators may be combined with mergeV() to take action depending on whether a vertex was created, or already existed. There are various ways mergeV() can be used. The simplest being to provide a single Map of keys and values as a parameter. A T.id and a T.label may also be provided but this is optional. The mergeV() step can be used directly from the GraphTraversalSource - g, or in the middle of a traversal. For a match with an existing vertex to occur, all values in the Map must exist on a vertex; otherwise, a new vertex will be created. The examples that follow show how mergeV() can be used to add some dogs to the graph.

console (groovy) groovy
gremlin> g.mergeV([name: 'Brandy']) //// (1)
==>v[0]
gremlin> g.V().has('name','Brandy')
==>v[0]
gremlin> g.mergeV([(T.label):'Dog',name:'Scamp', age:12]) //// (2)
==>v[2]
gremlin> g.V().hasLabel('Dog').valueMap()
==>[name:[Scamp],age:[12]]
gremlin> g.mergeV([(T.id):300, (T.label):'Dog', name:'Toby', age:10]) //// (3)
==>v[300]
gremlin> g.V().hasLabel('Dog').valueMap().with(WithOptions.tokens)
==>[id:2,label:Dog,name:[Scamp],age:[12]]
==>[id:300,label:Dog,name:[Toby],age:[10]]
g.mergeV([name: 'Brandy']) //// (1)
g.V().has('name','Brandy')
g.mergeV([(T.label):'Dog',name:'Scamp', age:12]) //// (2)
g.V().hasLabel('Dog').valueMap()
g.mergeV([(T.id):300, (T.label):'Dog', name:'Toby', age:10]) //// (3)
g.V().hasLabel('Dog').valueMap().with(WithOptions.tokens)
  1. Create a vertex for Brandy as no other matching ones exist yet.

  2. Create a vertex for Scamp and also add a Dog label his age.

  3. Create a vertex for Toby with an T.id of 300.

Note

The example above is written with gremlin-groovy and evaluated in Gremlin Console as a Groovy script thus allowing Groovy syntax for initializing a Map.

If a vertex already exists that matches the map passed to mergeV(), the existing vertex will be returned, otherwise a new one will be created. In this way, mergeV() provides "get or create" semantics.

console (groovy) groovy
gremlin> g.mergeV([name: 'Brandy']) //// (1)
==>v[0]
g.mergeV([name: 'Brandy']) //1
  1. A vertex for Brandy already exists so return it. A new one is not created.

It’s important to note that every key/value pair passed to mergeV() must already exist on one or more vertices for there to be a match. If a match is found, the vertex, or vertices, representing that match will be returned. If a vertex representing a dog called Brandy already exists, but it does not have an "age" property, the mergeV() below will not find a match and a new vertex will be created.

console (groovy) groovy
gremlin> g.addV('Dog').property('name','Brandy') //// (1)
==>v[0]
gremlin> g.mergeV([(T.label):'Dog',name:'Brandy',age:13]) //// (2)
==>v[2]
g.addV('Dog').property('name','Brandy') //// (1)
g.mergeV([(T.label):'Dog',name:'Brandy',age:13]) //2
  1. Create a vertex for Brandy with no age property.

  2. A new vertex is created as there is no exact match to any existing vertices.

A common scenario is to search for a vertex with a known T.id and if it exists return that vertex. If it does not exist, create it. As we have seen, one way to do this is to pass the T.id and all properties directly to mergeV(). Another is to use Merge.onCreate. Note that the Map specified for Match.onCreate does not need to include the T.id already present in the original search. The values provided to the mergeV() Map are inherited by the onCreate action and combined with the Map provided to Merge.onCreate. Overrides of the T.id or T.label in the onCreate Map are prohibited.

console (groovy) groovy
gremlin> g.mergeV([(T.id):300]).
           option(Merge.onCreate,[(T.label):'Dog', name:'Toby', age:10])
==>v[300]
g.mergeV([(T.id):300]).
  option(Merge.onCreate,[(T.label):'Dog', name:'Toby', age:10])

To take specific action when the vertex already exists, Merge.onMatch can be used. The second parameter to the option step can be either a Map whose values are used to update the vertex or another Gremlin traversal that generates a Map.

Note

If mergeV() is given an empty Map; such as mergeV([:]), it will match, and return, every vertex in the graph. This is the same behavior seen with V([]).
console (groovy) groovy
gremlin> g.mergeV([(T.id):300]).
           option(Merge.onCreate,[(T.label):'Dog', name:'Toby', age:10]). //// (1)
           option(Merge.onMatch,[age:11]) //// (2)
==>v[300]
gremlin> g.withSideEffect('new-data',[age:11]).
           mergeV([(T.id):300]).
           option(Merge.onCreate,[(T.label):'Dog', name:'Toby', age:10]).
           option(Merge.onMatch,select('new-data')) //// (3)
==>v[300]
gremlin> g.V(300).valueMap().with(WithOptions.tokens)
==>[id:300,label:Dog,name:[Toby],age:[11]]
g.mergeV([(T.id):300]).
  option(Merge.onCreate,[(T.label):'Dog', name:'Toby', age:10]). //// (1)
  option(Merge.onMatch,[age:11]) //// (2)
g.withSideEffect('new-data',[age:11]).
  mergeV([(T.id):300]).
  option(Merge.onCreate,[(T.label):'Dog', name:'Toby', age:10]).
  option(Merge.onMatch,select('new-data')) //// (3)
g.V(300).valueMap().with(WithOptions.tokens)
  1. If no match found create the vertex using these values.

  2. If a match is found, change the age property value.

  3. Change the age property by selecting from the new-data map.

It is sometimes helpful to incorporate fail() step into scenarios where there is a need to stop the traversal for one event or the other:

gremlin> g.mergeV([(T.id): 1]).
......1>     option(onCreate, fail("vertex did not exist")).
......2>     option(onMatch, [modified: 2022])
fail() Step Triggered
======================================================================================================================================================================
Message  > vertex did not exist
Traverser> false
  Bulk   > 1
Traversal> fail("vertex did not exist")
Parent   > TinkerMergeVertexStep [mergeV([(T.id):((int) 1)]).option(Merge.onCreate,__.fail("vertex did not exist")).option(Merge.onMatch,[("modified"):((int) 2022)])]
Metadata > {}
======================================================================================================================================================================

When working with multi-properties, there are two ways to specify them for mergeV(). First, you can specify them individually using a CardinalityValue as the value in the Map. The CardinalityValue allows you to specify the value as well as the Cardinality for that value. Note that it is only possible to specify one value with this syntax even if you are using set or list.

console (groovy) groovy
gremlin> g.mergeV([(T.label):'Dog', name:'Max']). //// (1)
             option(onCreate, [alias: set('Maximus')]). //// (2)
           property(set,'alias','Maxamillion') //// (3)
==>v[0]
gremlin> g.V().has('name','Max').valueMap().with(WithOptions.tokens)
==>[id:0,label:Dog,name:[Max],alias:[Maximus,Maxamillion]]
g.mergeV([(T.label):'Dog', name:'Max']). //// (1)
    option(onCreate, [alias: set('Maximus')]). //// (2)
  property(set,'alias','Maxamillion') //// (3)
g.V().has('name','Max').valueMap().with(WithOptions.tokens)
  1. Find or create a vertex for Max.

  2. If Max is not found then add an alias of set cardinality.

  3. Whether Max was found or created, add another alias with set cardinality.

The second option is to specify Cardinality for the entire range of values as follows:

console (groovy) groovy
gremlin> g.mergeV([(T.label):'Dog', name:'Max']).
             option(onCreate, [alias: 'Maximus', city: 'Boston'], set) //// (1)
==>v[0]
gremlin> g.mergeV([(T.label):'Dog', name:'Max']).
             option(onCreate, [alias: 'Maximus', city: single('Boston')], set) //// (2)
==>v[0]
g.mergeV([(T.label):'Dog', name:'Max']).
    option(onCreate, [alias: 'Maximus', city: 'Boston'], set) //// (1)
g.mergeV([(T.label):'Dog', name:'Max']).
    option(onCreate, [alias: 'Maximus', city: single('Boston')], set) //2
  1. If Max is created then set the alias and city with cardinality of set.

  2. If Max is created then set the alias with cardinality of set and city with cardinality single.

More than one vertex can be created by a single mergeV() operation. This is done by injecting a List of Map objects into the traversal and letting them stream into the mergeV() step.

console (groovy) groovy
gremlin> maps = [[(T.label) : 'Dog', name: 'Toby'  , breed: 'Golden Retriever'],
                 [(T.label) : 'Dog', name: 'Brandy', breed: 'Golden Retriever'],
                 [(T.label) : 'Dog', name: 'Scamp' , breed: 'King Charles Spaniel'],
                 [(T.label) : 'Dog', name: 'Shadow', breed: 'Mixed'],
                 [(T.label) : 'Dog', name: 'Rocket', breed: 'Golden Retriever'],
                 [(T.label) : 'Dog', name: 'Dax'   , breed: 'Mixed'],
                 [(T.label) : 'Dog', name: 'Baxter', breed: 'Mixed'],
                 [(T.label) : 'Dog', name: 'Zoe'   , breed: 'Corgi'],
                 [(T.label) : 'Dog', name: 'Pixel' , breed: 'Mixed']]
==>[label:Dog,name:Toby,breed:Golden Retriever]
==>[label:Dog,name:Brandy,breed:Golden Retriever]
==>[label:Dog,name:Scamp,breed:King Charles Spaniel]
==>[label:Dog,name:Shadow,breed:Mixed]
==>[label:Dog,name:Rocket,breed:Golden Retriever]
==>[label:Dog,name:Dax,breed:Mixed]
==>[label:Dog,name:Baxter,breed:Mixed]
==>[label:Dog,name:Zoe,breed:Corgi]
==>[label:Dog,name:Pixel,breed:Mixed]
gremlin> g.inject(maps).unfold().mergeV()
==>v[0]
==>v[3]
==>v[6]
==>v[9]
==>v[12]
==>v[15]
==>v[18]
==>v[21]
==>v[24]
gremlin> g.V().hasLabel('Dog').valueMap().with(WithOptions.tokens)
==>[id:0,label:Dog,name:[Toby],breed:[Golden Retriever]]
==>[id:18,label:Dog,name:[Baxter],breed:[Mixed]]
==>[id:3,label:Dog,name:[Brandy],breed:[Golden Retriever]]
==>[id:21,label:Dog,name:[Zoe],breed:[Corgi]]
==>[id:6,label:Dog,name:[Scamp],breed:[King Charles Spaniel]]
==>[id:24,label:Dog,name:[Pixel],breed:[Mixed]]
==>[id:9,label:Dog,name:[Shadow],breed:[Mixed]]
==>[id:12,label:Dog,name:[Rocket],breed:[Golden Retriever]]
==>[id:15,label:Dog,name:[Dax],breed:[Mixed]]
maps = [[(T.label) : 'Dog', name: 'Toby'  , breed: 'Golden Retriever'],
        [(T.label) : 'Dog', name: 'Brandy', breed: 'Golden Retriever'],
        [(T.label) : 'Dog', name: 'Scamp' , breed: 'King Charles Spaniel'],
        [(T.label) : 'Dog', name: 'Shadow', breed: 'Mixed'],
        [(T.label) : 'Dog', name: 'Rocket', breed: 'Golden Retriever'],
        [(T.label) : 'Dog', name: 'Dax'   , breed: 'Mixed'],
        [(T.label) : 'Dog', name: 'Baxter', breed: 'Mixed'],
        [(T.label) : 'Dog', name: 'Zoe'   , breed: 'Corgi'],
        [(T.label) : 'Dog', name: 'Pixel' , breed: 'Mixed']]
g.inject(maps).unfold().mergeV()
g.V().hasLabel('Dog').valueMap().with(WithOptions.tokens)

Another useful pattern that can be used with mergeV() involves putting multiple maps in a list and selecting different maps based on the action being taken. The examples below use a list containing three maps. The first containing just the ID to be searched for. The second map contains all the information to use when the vertex is created. The third map contains additional information that will be applied if an existing vertex is found.

console (groovy) groovy
gremlin> g.inject([[(T.id):400],[(T.label):'Dog',name:'Pixel',age:1],[updated:'2022-02-1']]).as('m').
           mergeV(select('m').limit(local,1).unfold()). //// (1)
           option(Merge.onCreate, select('m').range(local,1,2).unfold()). //// (2)
           option(Merge.onMatch, select('m').tail(local).unfold()) //// (3)
==>v[400]
gremlin> g.V(400).valueMap().with(WithOptions.tokens)
==>[id:400,label:Dog,name:[Pixel],age:[1]]
gremlin> g.inject([[(T.id):400],[(T.label):'Dog',name:'Pixel',age:1],[updated:'2022-02-1']]).as('m').
           mergeV(select('m').limit(local,1).unfold()).
           option(Merge.onCreate, select('m').range(local,1,2).unfold()).
           option(Merge.onMatch, select('m').tail(local).unfold()) //// (4)
==>v[400]
gremlin> g.V(400).valueMap().with(WithOptions.tokens) //// (5)
==>[id:400,label:Dog,name:[Pixel],updated:[2022-02-1],age:[1]]
g.inject([[(T.id):400],[(T.label):'Dog',name:'Pixel',age:1],[updated:'2022-02-1']]).as('m').
  mergeV(select('m').limit(local,1).unfold()). //// (1)
  option(Merge.onCreate, select('m').range(local,1,2).unfold()). //// (2)
  option(Merge.onMatch, select('m').tail(local).unfold()) //// (3)
g.V(400).valueMap().with(WithOptions.tokens)
g.inject([[(T.id):400],[(T.label):'Dog',name:'Pixel',age:1],[updated:'2022-02-1']]).as('m').
  mergeV(select('m').limit(local,1).unfold()).
  option(Merge.onCreate, select('m').range(local,1,2).unfold()).
  option(Merge.onMatch, select('m').tail(local).unfold()) //// (4)
g.V(400).valueMap().with(WithOptions.tokens)  //5
  1. Use the first map to search for a vertex with an ID of 400.

  2. If the vertex was not found, use the second map to create it.

  3. If the vertex was found, add an updated property.

  4. Pixel exists now, so we will take this option.

  5. The updated property has now been added.

Additional References

Min Step

The min()-step (map) operates on a stream of comparable objects and determines which is the first object according to its natural order in the stream.

console (groovy) groovy
gremlin> g.V().values('age').min()
==>27
gremlin> g.V().repeat(both()).times(3).values('age').min()
==>27
gremlin> g.V().values('name').min()
==>josh
g.V().values('age').min()
g.V().repeat(both()).times(3).values('age').min()
g.V().values('name').min()

When called as min(local) it determines the minimum value of the current, local object (not the objects in the traversal stream). This works for Collection and Comparable-type objects.

console (groovy) groovy
gremlin> g.V().values('age').fold().min(local)
==>27
g.V().values('age').fold().min(local)

When there are null values being evaluated the null objects are ignored, but if all values are recognized as null the return value is null.

console (groovy) groovy
gremlin> g.inject(null,10, 9, null).min()
==>9
gremlin> g.inject([null,null,null]).min(local)
==>null
g.inject(null,10, 9, null).min()
g.inject([null,null,null]).min(local)

Additional References

None Step

It is possible to filter list traversers using none()-step (filter). Every item in the list will be tested against the supplied predicate and if none of the items pass then the traverser is passed along the stream, otherwise it is filtered. Empty lists are passed along but null or non-iterable traversers are filtered out.

Note

Prior to release 3.8.0, none() was a traversal discarding step primarily used by iterate(). This step has since been renamed to discard()
console (groovy) groovy
gremlin> g.V().values('age').fold().none(gt(25)) //// (1)
g.V().values('age').fold().none(gt(25)) //1
  1. Return the list of ages only if no one’s age is greater than 25.

Additional References

Not Step

The not()-step (filter) removes objects from the traversal stream when the traversal provided as an argument returns an object.

Groovy

The term not is a reserved word in Groovy, and when therefore used as part of an anonymous traversal must be referred to in Gremlin with the double underscore __.not().

Python

The term not is a reserved word in Python, and therefore must be referred to in Gremlin with not_().

console (groovy) groovy
gremlin> g.V().not(hasLabel('person')).elementMap()
==>[id:3,label:software,name:lop,lang:java]
==>[id:5,label:software,name:ripple,lang:java]
gremlin> g.V().hasLabel('person').
           not(out('created').count().is(gt(1))).values('name') //// (1)
==>marko
==>vadas
==>peter
g.V().not(hasLabel('person')).elementMap()
g.V().hasLabel('person').
  not(out('created').count().is(gt(1))).values('name')   //1
  1. josh created two projects and vadas none

Additional References

Option Step

An option to a branch() or choose().

Additional References

Optional Step

The optional()-step (branch/flatMap) returns the result of the specified traversal if it yields a result else it returns the calling element, i.e. the identity().

console (groovy) groovy
gremlin> g.V(2).optional(out('knows')) //// (1)
==>v[2]
gremlin> g.V(2).optional(__.in('knows')) //// (2)
==>v[1]
g.V(2).optional(out('knows')) //// (1)
g.V(2).optional(__.in('knows')) //2
  1. vadas does not have an outgoing knows-edge so vadas is returned.

  2. vadas does have an incoming knows-edge so marko is returned.

optional is particularly useful for lifting entire graphs when used in conjunction with path or tree.

console (groovy) groovy
gremlin> g.V().hasLabel('person').optional(out('knows').optional(out('created'))).path() //// (1)
==>[v[1],v[2]]
==>[v[1],v[4],v[5]]
==>[v[1],v[4],v[3]]
==>[v[2]]
==>[v[4]]
==>[v[6]]
g.V().hasLabel('person').optional(out('knows').optional(out('created'))).path() //1
  1. Returns the paths of everybody followed by who they know followed by what they created.

Additional References

Or Step

The or()-step ensures that at least one of the provided traversals yield a result (filter). Please see and() for and-semantics.

Python

The term or is a reserved word in Python, and therefore must be referred to in Gremlin with or_().

console (groovy) groovy
gremlin> g.V().or(
            __.outE('created'),
            __.inE('created').count().is(gt(1))).
              values('name')
==>marko
==>lop
==>josh
==>peter
g.V().or(
   __.outE('created'),
   __.inE('created').count().is(gt(1))).
     values('name')

The or()-step can take an arbitrary number of traversals. At least one of the traversals must produce at least one output for the original traverser to pass to the next step.

console (groovy) groovy
gremlin> g.V().where(outE('created').or().outE('knows')).values('name')
==>marko
==>josh
==>peter
g.V().where(outE('created').or().outE('knows')).values('name')

Additional References

Order Step

When the objects of the traversal stream need to be sorted, order()-step (map) can be leveraged.

console (groovy) groovy
gremlin> g.V().values('name').order()
==>josh
==>lop
==>marko
==>peter
==>ripple
==>vadas
gremlin> g.V().values('name').order().by(desc)
==>vadas
==>ripple
==>peter
==>marko
==>lop
==>josh
gremlin> g.V().hasLabel('person').order().by('age', asc).values('name')
==>vadas
==>marko
==>josh
==>peter
g.V().values('name').order()
g.V().values('name').order().by(desc)
g.V().hasLabel('person').order().by('age', asc).values('name')

One of the most traversed objects in a traversal is an Element. An element can have properties associated with it (i.e. key/value pairs). In many situations, it is desirable to sort an element traversal stream according to a comparison of their properties.

console (groovy) groovy
gremlin> g.V().values('name')
==>marko
==>vadas
==>lop
==>josh
==>ripple
==>peter
gremlin> g.V().order().by('name',asc).values('name')
==>josh
==>lop
==>marko
==>peter
==>ripple
==>vadas
gremlin> g.V().order().by('name',desc).values('name')
==>vadas
==>ripple
==>peter
==>marko
==>lop
==>josh
gremlin> g.V().both().order().by('age') //// (1)
==>v[2]
==>v[1]
==>v[1]
==>v[1]
==>v[4]
==>v[4]
==>v[4]
==>v[6]
g.V().values('name')
g.V().order().by('name',asc).values('name')
g.V().order().by('name',desc).values('name')
g.V().both().order().by('age') //1
  1. The "age" property is not productive for all vertices and therefore those values are filtered.

The order()-step allows the user to provide an arbitrary number of comparators for primary, secondary, etc. sorting. In the example below, the primary ordering is based on the outgoing created-edge count. The secondary ordering is based on the age of the person.

console (groovy) groovy
gremlin> g.V().hasLabel('person').order().by(outE('created').count(), asc).
                                          by('age', asc).values('name')
==>vadas
==>marko
==>peter
==>josh
gremlin> g.V().hasLabel('person').order().by(outE('created').count(), asc).
                                          by('age', desc).values('name')
==>vadas
==>peter
==>marko
==>josh
g.V().hasLabel('person').order().by(outE('created').count(), asc).
                                 by('age', asc).values('name')
g.V().hasLabel('person').order().by(outE('created').count(), asc).
                                 by('age', desc).values('name')

Randomizing the order of the traversers at a particular point in the traversal is possible with Order.shuffle.

console (groovy) groovy
gremlin> g.V().hasLabel('person').order().by(shuffle)
==>v[1]
==>v[2]
==>v[4]
==>v[6]
gremlin> g.V().hasLabel('person').order().by(shuffle)
==>v[4]
==>v[6]
==>v[2]
==>v[1]
g.V().hasLabel('person').order().by(shuffle)
g.V().hasLabel('person').order().by(shuffle)

It is possible to use order(local) to order the current local object and not the entire traversal stream. This works for Collection- and Map-type objects. For any other object, the object is returned unchanged.

console (groovy) groovy
gremlin> g.V().values('age').fold().order(local).by(desc) //// (1)
==>[35,32,29,27]
gremlin> g.V().values('age').order(local).by(desc) //// (2)
==>29
==>27
==>32
==>35
gremlin> g.V().groupCount().by(inE().count()).order(local).by(values, desc) //// (3)
==>[1:3,0:2,3:1]
gremlin> g.V().groupCount().by(inE().count()).order(local).by(keys, asc) //// (4)
==>[0:2,1:3,3:1]
g.V().values('age').fold().order(local).by(desc) //// (1)
g.V().values('age').order(local).by(desc) //// (2)
g.V().groupCount().by(inE().count()).order(local).by(values, desc) //// (3)
g.V().groupCount().by(inE().count()).order(local).by(keys, asc) //4
  1. The ages are gathered into a list and then that list is sorted in decreasing order.

  2. The ages are not gathered and thus order(local) is "ordering" single integers and thus, does nothing.

  3. The groupCount() map is ordered by its values in decreasing order.

  4. The groupCount() map is ordered by its keys in increasing order.

Note

The values and keys enums are from Column which is used to select "columns" from a Map, Map.Entry, or Path.

If a property key does not exist, then it will be treated as null which will sort it first for Order.asc and last for Order.desc.

console (groovy) groovy
gremlin> g.V().order().by("age").elementMap()
==>[id:2,label:person,name:vadas,age:27]
==>[id:1,label:person,name:marko,age:29]
==>[id:4,label:person,name:josh,age:32]
==>[id:6,label:person,name:peter,age:35]
g.V().order().by("age").elementMap()

Note

Prior to version 3.3.4, ordering was defined by Order.incr for ascending order and Order.decr for descending order. Those tokens were deprecated and eventually removed in 3.5.0.

Additional References

The pageRank()-step (map/sideEffect) calculates PageRank using PageRankVertexProgram.

Important

The pageRank()-step is a VertexComputing-step and as such, can only be used against a graph that supports GraphComputer (OLAP).
console (groovy) groovy
gremlin> g = traversal().with(graph).withComputer()
==>graphtraversalsource[tinkergraph[vertices:6 edges:6], graphcomputer]
gremlin> g.V().pageRank().with(PageRank.propertyName, 'friendRank').values('pageRank')
gremlin> g.V().hasLabel('person').
           pageRank().
             with(PageRank.edges, __.outE('knows')).
             with(PageRank.propertyName, 'friendRank').
           order().by('friendRank',desc).
           elementMap('name','friendRank')
==>[id:1,label:person,friendRank:0.5839416733381598,name:marko]
==>[id:4,label:person,friendRank:0.8321166533236799,name:josh]
==>[id:2,label:person,friendRank:0.8321166533236799,name:vadas]
==>[id:6,label:person,friendRank:0.5839416733381598,name:peter]
g = traversal().with(graph).withComputer()
g.V().pageRank().with(PageRank.propertyName, 'friendRank').values('pageRank')
g.V().hasLabel('person').
  pageRank().
    with(PageRank.edges, __.outE('knows')).
    with(PageRank.propertyName, 'friendRank').
  order().by('friendRank',desc).
  elementMap('name','friendRank')

Note the use of the with() modulating step which provides configuration options to the algorithm. It takes configuration keys from the PageRank and is automatically imported to the Gremlin Console.

The explain()-step can be used to understand how the traversal is compiled into multiple GraphComputer jobs.

console (groovy) groovy
gremlin> g = traversal().with(graph).withComputer()
==>graphtraversalsource[tinkergraph[vertices:6 edges:6], graphcomputer]
gremlin> g.V().hasLabel('person').
           pageRank().
             with(PageRank.edges, __.outE('knows')).
             with(PageRank.propertyName, 'friendRank').
           order().by('friendRank',desc).
           elementMap('name','friendRank').explain()
==>Traversal Explanation
=============================================================================================================================================================================================================================================
Original Traversal                    [GraphStep(vertex,[]), HasStep([~label.eq(person)]), PageRankVertexProgramStep([VertexStep(OUT,[knows],edge)],friendRank,20,graphfilter[none]), OrderGlobalStep([[value(friendRank), desc]]), ElementMa
                                         pStep([name, friendRank])]
ConnectiveStrategy              [D]   [GraphStep(vertex,[]), HasStep([~label.eq(person)]), PageRankVertexProgramStep([VertexStep(OUT,[knows],edge)],friendRank,20,graphfilter[none]), OrderGlobalStep([[value(friendRank), desc]]), ElementMa
                                         pStep([name, friendRank])]
VertexProgramStrategy           [D]   [TraversalVertexProgramStep([GraphStep(vertex,[]), HasStep([~label.eq(person)])],graphfilter[none]), PageRankVertexProgramStep([VertexStep(OUT,[knows],edge)],friendRank,20,graphfilter[none]), Travers
                                         alVertexProgramStep([OrderGlobalStep([[value(friendRank), desc]]), ElementMapStep([name, friendRank])],graphfilter[none]), ComputerResultStep]
IdentityRemovalStrategy         [O]   [TraversalVertexProgramStep([GraphStep(vertex,[]), HasStep([~label.eq(person)])],graphfilter[none]), PageRankVertexProgramStep([VertexStep(OUT,[knows],edge)],friendRank,20,graphfilter[none]), Travers
                                         alVertexProgramStep([OrderGlobalStep([[value(friendRank), desc]]), ElementMapStep([name, friendRank])],graphfilter[none]), ComputerResultStep]
MatchPredicateStrategy          [O]   [TraversalVertexProgramStep([GraphStep(vertex,[]), HasStep([~label.eq(person)])],graphfilter[none]), PageRankVertexProgramStep([VertexStep(OUT,[knows],edge)],friendRank,20,graphfilter[none]), Travers
                                         alVertexProgramStep([OrderGlobalStep([[value(friendRank), desc]]), ElementMapStep([name, friendRank])],graphfilter[none]), ComputerResultStep]
FilterRankingStrategy           [O]   [TraversalVertexProgramStep([GraphStep(vertex,[]), HasStep([~label.eq(person)])],graphfilter[none]), PageRankVertexProgramStep([VertexStep(OUT,[knows],edge)],friendRank,20,graphfilter[none]), Travers
                                         alVertexProgramStep([OrderGlobalStep([[value(friendRank), desc]]), ElementMapStep([name, friendRank])],graphfilter[none]), ComputerResultStep]
CountStrategy                   [O]   [TraversalVertexProgramStep([GraphStep(vertex,[]), HasStep([~label.eq(person)])],graphfilter[none]), PageRankVertexProgramStep([VertexStep(OUT,[knows],edge)],friendRank,20,graphfilter[none]), Travers
                                         alVertexProgramStep([OrderGlobalStep([[value(friendRank), desc]]), ElementMapStep([name, friendRank])],graphfilter[none]), ComputerResultStep]
EarlyLimitStrategy              [O]   [TraversalVertexProgramStep([GraphStep(vertex,[]), HasStep([~label.eq(person)])],graphfilter[none]), PageRankVertexProgramStep([VertexStep(OUT,[knows],edge)],friendRank,20,graphfilter[none]), Travers
                                         alVertexProgramStep([OrderGlobalStep([[value(friendRank), desc]]), ElementMapStep([name, friendRank])],graphfilter[none]), ComputerResultStep]
IncidentToAdjacentStrategy      [O]   [TraversalVertexProgramStep([GraphStep(vertex,[]), HasStep([~label.eq(person)])],graphfilter[none]), PageRankVertexProgramStep([VertexStep(OUT,[knows],edge)],friendRank,20,graphfilter[none]), Travers
                                         alVertexProgramStep([OrderGlobalStep([[value(friendRank), desc]]), ElementMapStep([name, friendRank])],graphfilter[none]), ComputerResultStep]
PathProcessorStrategy           [O]   [TraversalVertexProgramStep([GraphStep(vertex,[]), HasStep([~label.eq(person)])],graphfilter[none]), PageRankVertexProgramStep([VertexStep(OUT,[knows],edge)],friendRank,20,graphfilter[none]), Travers
                                         alVertexProgramStep([OrderGlobalStep([[value(friendRank), desc]]), ElementMapStep([name, friendRank])],graphfilter[none]), ComputerResultStep]
InlineFilterStrategy            [O]   [TraversalVertexProgramStep([GraphStep(vertex,[]), HasStep([~label.eq(person)])],graphfilter[none]), PageRankVertexProgramStep([VertexStep(OUT,[knows],edge)],friendRank,20,graphfilter[none]), Travers
                                         alVertexProgramStep([OrderGlobalStep([[value(friendRank), desc]]), ElementMapStep([name, friendRank])],graphfilter[none]), ComputerResultStep]
ByModulatorOptimizationStrategy [O]   [TraversalVertexProgramStep([GraphStep(vertex,[]), HasStep([~label.eq(person)])],graphfilter[none]), PageRankVertexProgramStep([VertexStep(OUT,[knows],edge)],friendRank,20,graphfilter[none]), Travers
                                         alVertexProgramStep([OrderGlobalStep([[value(friendRank), desc]]), ElementMapStep([name, friendRank])],graphfilter[none]), ComputerResultStep]
AdjacentToIncidentStrategy      [O]   [TraversalVertexProgramStep([GraphStep(vertex,[]), HasStep([~label.eq(person)])],graphfilter[none]), PageRankVertexProgramStep([VertexStep(OUT,[knows],edge)],friendRank,20,graphfilter[none]), Travers
                                         alVertexProgramStep([OrderGlobalStep([[value(friendRank), desc]]), ElementMapStep([name, friendRank])],graphfilter[none]), ComputerResultStep]
RepeatUnrollStrategy            [O]   [TraversalVertexProgramStep([GraphStep(vertex,[]), HasStep([~label.eq(person)])],graphfilter[none]), PageRankVertexProgramStep([VertexStep(OUT,[knows],edge)],friendRank,20,graphfilter[none]), Travers
                                         alVertexProgramStep([OrderGlobalStep([[value(friendRank), desc]]), ElementMapStep([name, friendRank])],graphfilter[none]), ComputerResultStep]
PathRetractionStrategy          [O]   [TraversalVertexProgramStep([GraphStep(vertex,[]), HasStep([~label.eq(person)])],graphfilter[none]), PageRankVertexProgramStep([VertexStep(OUT,[knows],edge)],friendRank,20,graphfilter[none]), Travers
                                         alVertexProgramStep([OrderGlobalStep([[value(friendRank), desc]]), ElementMapStep([name, friendRank])],graphfilter[none]), ComputerResultStep]
LazyBarrierStrategy             [O]   [TraversalVertexProgramStep([GraphStep(vertex,[]), HasStep([~label.eq(person)])],graphfilter[none]), PageRankVertexProgramStep([VertexStep(OUT,[knows],edge)],friendRank,20,graphfilter[none]), Travers
                                         alVertexProgramStep([OrderGlobalStep([[value(friendRank), desc]]), ElementMapStep([name, friendRank])],graphfilter[none]), ComputerResultStep]
OrderLimitStrategy              [O]   [TraversalVertexProgramStep([GraphStep(vertex,[]), HasStep([~label.eq(person)])],graphfilter[none]), PageRankVertexProgramStep([VertexStep(OUT,[knows],edge)],friendRank,20,graphfilter[none]), Travers
                                         alVertexProgramStep([OrderGlobalStep([[value(friendRank), desc]]), ElementMapStep([name, friendRank])],graphfilter[none]), ComputerResultStep]
MessagePassingReductionStrategy [O]   [TraversalVertexProgramStep([GraphStep(vertex,[]), HasStep([~label.eq(person)])],graphfilter[none]), PageRankVertexProgramStep([VertexStep(OUT,[knows],edge)],friendRank,20,graphfilter[none]), Travers
                                         alVertexProgramStep([OrderGlobalStep([[value(friendRank), desc]]), ElementMapStep([name, friendRank])],graphfilter[none]), ComputerResultStep]
GValueReductionStrategy         [O]   [TraversalVertexProgramStep([GraphStep(vertex,[]), HasStep([~label.eq(person)])],graphfilter[none]), PageRankVertexProgramStep([VertexStep(OUT,[knows],edge)],friendRank,20,graphfilter[none]), Travers
                                         alVertexProgramStep([OrderGlobalStep([[value(friendRank), desc]]), ElementMapStep([name, friendRank])],graphfilter[none]), ComputerResultStep]
TinkerGraphCountStrategy        [P]   [TraversalVertexProgramStep([GraphStep(vertex,[]), HasStep([~label.eq(person)])],graphfilter[none]), PageRankVertexProgramStep([VertexStep(OUT,[knows],edge)],friendRank,20,graphfilter[none]), Travers
                                         alVertexProgramStep([OrderGlobalStep([[value(friendRank), desc]]), ElementMapStep([name, friendRank])],graphfilter[none]), ComputerResultStep]
TinkerGraphStepStrategy         [P]   [TraversalVertexProgramStep([GraphStep(vertex,[]), HasStep([~label.eq(person)])],graphfilter[none]), PageRankVertexProgramStep([VertexStep(OUT,[knows],edge)],friendRank,20,graphfilter[none]), Travers
                                         alVertexProgramStep([OrderGlobalStep([[value(friendRank), desc]]), ElementMapStep([name, friendRank])],graphfilter[none]), ComputerResultStep]
ProfileStrategy                 [F]   [TraversalVertexProgramStep([GraphStep(vertex,[]), HasStep([~label.eq(person)])],graphfilter[none]), PageRankVertexProgramStep([VertexStep(OUT,[knows],edge)],friendRank,20,graphfilter[none]), Travers
                                         alVertexProgramStep([OrderGlobalStep([[value(friendRank), desc]]), ElementMapStep([name, friendRank])],graphfilter[none]), ComputerResultStep]
ComputerVerificationStrategy    [V]   [TraversalVertexProgramStep([GraphStep(vertex,[]), HasStep([~label.eq(person)])],graphfilter[none]), PageRankVertexProgramStep([VertexStep(OUT,[knows],edge)],friendRank,20,graphfilter[none]), Travers
                                         alVertexProgramStep([OrderGlobalStep([[value(friendRank), desc]]), ElementMapStep([name, friendRank])],graphfilter[none]), ComputerResultStep]
StandardVerificationStrategy    [V]   [TraversalVertexProgramStep([GraphStep(vertex,[]), HasStep([~label.eq(person)])

Read the original on tinkerpop.apache.org ↗