Gremlin Snippets are typically short and fun dissections of some aspect of the Gremlin language. For a full list of all steps in the Gremlin language see the Reference Documentation of Apache TinkerPop™. This snippet is based on Gremlin 3.4.6.This snippet demonstrates its lesson using the data of the "modern" toy graph (image).Please consider bringing any discussion or questions about this snippet to Discord or the Gremlin Users Mailing List.



The constant() step looks a bit like inject(). Both look like they take some arbitrary “constant” sort of value and “inject” it into the traversal stream.

gremlin> g.inject(1)
==>1
gremlin> g.V().has('person','name','marko').constant('c')
==>c

It might beg the question as to why inject() is both a start step and traversal step while constant() is simply the latter. On closer inspection we can see that while similar in function, the two steps play completely different roles.

In the case of constant(), it is a map() step, in the sense that whatever traverser comes into constant() will be transformed to the specified value regardless of what that traverser originally contained. In the previous example, a Vertex became the String value of “c”. In the following examples we can see other basic transformations:

gremlin> g.V().has('person','name',within('josh','marko')).constant('c')
==>c
==>c
gremlin> g.V().has('person','name',within('josh','marko')).fold().constant('c')
==>c

In the first traversal line above, there are two vertices returned and therefore each is mapped to “c”. In the second traversal line, those two vertices are reduced to a List with fold() and therefore there is only one traverser containing those two elements. As a result, there is a single List to transform and therefore a single “c” as the output.

The behavior of inject() is quite different in that its purpose is not to transform the incoming traverser as a map() step but to insert the additional specified objects into the stream with that incoming traverser. Given that inject() places objects into the stream, it is fitting that it live alongside V() and E() as start steps as they themselves places objects into the traversal stream to start it. The behavior of inject() is obvious as a start step:

gremlin> g.inject(1, 2, 3)
==>1
==>2
==>3

It is more nuanced when used mid-traversal as a standard traversal step:

gremlin> g.V().has('person','name','marko').inject('i')
==>i
==>v[1]
gremlin> g.V().has('person','name',within('josh','marko')).inject('i')
==>i
==>v[1]
==>v[4]
gremlin> g.V().has('person','name',within('josh','marko')).inject('i1', 'i2')
==>i1
==>i2
==>v[1]
==>v[4]

Note that inject() behaves as though it is called once and not called once per traverser as we saw with constant(). Even when inject() is induced from within a map() as a child traversal it exhausts itself and then produces no additional items to the stream as shown in the following bit of Gremlin:

gremlin> g.V().has('person','name',within('josh','marko')).map(inject('i').fold())
==>[v[1],i]
==>[v[4]]

If we wanted that behavior we’d likely prefer the use of constant() (with union()):

gremlin> g.V().has('person','name',within('josh','marko')).
......1>   map(union(identity(),constant('c')).fold())
==>[v[1],c]
==>[v[4],c]