Hello, ScriptEngine’s prefer to have compile scripts with bindings as a compiled script will execute much faster than one that needs to be compiled. Thus, for a traversal that will be run over and over again, but with for instance different g.V(id) sources, its all about using ScriptEngine bindings. How do we support bindings in a language agnostic way?
The big problem is Java. The GraphTraversal API is strongly typed and thus, we can’t simply do stuff like g.V(bind(“id”,2)). Or can we? b = new Bindings(); g = graph.traversal().withBindings(b); g.V(b.of(“id”,2)).out(“created”).values(“name”) What is this all about? When you do b.of(“id”,2), it returns 2 (Java API is happy). However, the underlying Bytecode will know when a new argument has been passed to it. It will ask Bindings, "was a binding for 2 just added?" If so, then the Bytecode rewrites itself such that the argument is binding(“id”,2). Then, when the traversal is sent to GremlinServer to be evaluated (lets say via Gremlin-Groovy), the Bytecode translator will make the Groovy String script look as: g.V(id).out(“created”).values(“name”) And it will have JSR223 ScriptEngine bindings of {id:2}. https://github.com/apache/tinkerpop/blob/TINKERPOP-1278/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/java/translator/GroovyTranslator.java <https://github.com/apache/tinkerpop/blob/TINKERPOP-1278/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/java/translator/GroovyTranslator.java> Thus, TinkerPop Bindings become a first class citizen in Gremlin. Next, what about when Gremlin-Java is translating Bytecode. Well, Java doesn’t care about script compilation speed as its Bytecode translator doesn’t generate a String, but a Traversal via reflection. https://github.com/apache/tinkerpop/blob/TINKERPOP-1278/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/JavaTranslator.java <https://github.com/apache/tinkerpop/blob/TINKERPOP-1278/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/JavaTranslator.java> If JavaTranslator sees a binding in the Bytecode is ignores the binding key, and only uses its value: g.V(2).out(“created”).values(“name”) So this can get awkward for users. However, we can make bindings reusable. b = new Bindings(); g = graph.traversal().withBindings(b); g.V(b.of(“id”,2)).out(“created”).values(“name”) b.clear() g.V().out(b.of(“a”,”knows”)) b.clear() In fact, it would be possible to automatically reset the bindings every time a traversal is compiled. Thus, b = new Bindings(); g = graph.traversal().withBindings(b); g.V(b.of(“id”,2)).out(“created”).values(“name”) g.V().out(b.of(“a”,”knows”)) Now, you may say — “well, aren’t traversal sources suppose to be thread safe.” To that I say: “yes and if you do it like this, you can only execute one traversal at a time.” If you want thread safety: g = graph.traversal(); b = new Bindings(); g.withBindings(b).V(b.of(“id”,2)).out(“created”).values(“name”) g.withBindings(b).V().out(b.of(“a”,”knows”)) For most people, they will have a RemoteGraph traversal source and not worry about thread safety here. On GremlinServer, thread safety isn’t an issue as withBindings() is a TraversalSource step and thus, part of the Bytecode reconstructed at compile time. Anywho, that is the best I could come up with that will work for Java, Groovy, and Python. Unfortunately Java (and Groovy) are statically typed so we have to work around this. Thoughts?, Marko. http://markorodriguez.com