Understanding Scopes in Gremlin

441 views
Skip to first unread message

Marko Rodriguez

unread,
Oct 5, 2017, 3:34:18 PM10/5/17
to gremli...@googlegroups.com
Hello,

I’m really proud of the new math()-step. It is a really powerful and elegant solution to arbitrary calculations of traverser scopes. For shits and giggles, I want to take this email to explain “scoping” in Gremlin and to show off math().

Scoping is a big concept in Gremlin. There is a Scoping interface that scope-based steps implement.


There are four types of scopes:

1. Current scope — the current data referenced by the traverser (“path head”).
2. Path scope — a particular piece of data in the path of the traverser (“path history”).
3. Side-effect scope — a particular piece of data in the global traversal blackboard.
4. Map scope — a particular piece of data in the current scope map (“map value by key”).

I will demonstrate each scope using the new math()-step which, in fact, implements Scoping.

gremlin> g = TinkerFactory.createModern().traversal()
==>graphtraversalsource[tinkergraph[vertices:6 edges:6], standard]

1. Current Scope

The current scope refers to the current object referenced by the traverser. That is, the “traverser.get()” object. Another way to think about the current scope is to think in terms of the path of the traverser where the current scope is the head of the path. With the math()-step, the variable _ refers to the current scope.

gremlin> g.V().values("age").math("sin _")
==>-0.6636338842129675
==>0.956375928404503
==>0.5514266812416906
==>-0.428182669496151

2. Path Scope

The path scope refers to data previously seen by the traverser. That is, data in the traverser’s path history. Paths can be accessed by path(), however, individual parts of the path can be labeled using as() and accessed later via the path label name. Thus, in the traversal below, “a” and “b” refer to objects previously traversed by the traverser.

gremlin> g.V().as("a").out("knows").as("b”).
           math("a / b").by("age")
==>1.0740740740740742
==>0.90625

3. Side-Effect Scope

The side-effect scope refers objects in the global side-effects of the traversal. Side-effects are not local to the traverser, but instead, global to the traversal. In the traversal below you can see how “x” is being referenced in the math()-step and thus, the side-effect data is being used.

gremlin> g.withSideEffect("x",100).V().values("age").math("_ / x")
==>0.29
==>0.27
==>0.32
==>0.35

4. Map Scope

Map scope refers to objects within the current map object. Thus, its like current scope, but a bit “deeper.” In the traversal below the project()-step generates a map with keys “a” and “b”. The subsequent math()-step is then able to access the “a” and “b” values in the respective map and use them for the division operation.

gremlin> g.V().hasLabel("person”).
           project("a","b”).
             by("age”).
             by(bothE().count()).
           math("a / b")
==>9.666666666666666
==>27.0
==>10.666666666666666
==>35.0

Scoping is all about variable data access and forms the fundamental interface for access to the memory structures of Gremlin. In essence, it is through scoping that Gremlin moves beyond being a finite state machine to a push down automata and from their enjoys all the expressivity that that entails.

I hope that was useful.

Take care,
Marko.

David Welch

unread,
Dec 28, 2017, 1:58:33 PM12/28/17
to Gremlin-users
Thanks Marko. Came across this while trying to learn more and very helpful. 

Quick question: how can we "programmatically" access some of these variables? Say, one of the .as(...) variables in a side effect / map. For example, is there a way to do something like the following?: 

g.V().as('child').out('father').out('father').map{ it.get().value('name').toString() + 'Fathered' + child.value('name')}


Like I said, still new to the whole Gremlin space but I've poured over the docs and checked out example code and feel like I'm missing something conceptual.

Thanks :) 

Daniel Kuppitz

unread,
Dec 28, 2017, 2:13:35 PM12/28/17
to gremli...@googlegroups.com
You can use a lambda as in your example:

gremlin> g.V().as('person').out('knows').map {it.get().value('name').toString() + ' is a friend of ' + it.path('person').value('name')}
==>vadas is a friend of marko
==>josh is a friend of marko

However, you should always prefer solutions that don't require lambdas, e.g.:

gremlin> g.V().as('person').out('knows').
           project('person','friendOf').
             by('name').
             by(select('person').by('name'))
==>[person:vadas,friendOf:marko]
==>[person:josh,friendOf:marko]

Cheers,
Daniel


--
You received this message because you are subscribed to the Google Groups "Gremlin-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to gremlin-users+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/gremlin-users/5fe12342-220c-49aa-bd74-4985825e00f8%40googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

David Welch

unread,
Dec 29, 2017, 9:51:43 AM12/29/17
to Gremlin-users
I've been reading over the docs and toying for a good 1-2 months, but this simple example just totally blew my mind. The combination of projection + selection hadn't really clicked until just now. Thanks Daniel! 
To unsubscribe from this group and stop receiving emails from it, send an email to gremlin-user...@googlegroups.com.

Stephen Mallette

unread,
Dec 29, 2017, 11:45:23 AM12/29/17
to Gremlin-users
in that context, i tend to think of select() as providing for a way to override the "direction" of inner traversals. i think the pattern is on display in various examples, but it might not be immediately recognizable.

To unsubscribe from this group and stop receiving emails from it, send an email to gremlin-users+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/gremlin-users/dd030adf-d392-4219-881d-cd1dfc112839%40googlegroups.com.
Reply all
Reply to author
Forward
0 new messages