Implementing an anonymous traversal in a DSL

105 views
Skip to first unread message

Daniel Craig

unread,
Mar 25, 2022, 3:56:32 PM3/25/22
to Gremlin-users
Hi, using a DSL that I am developing, I want to be able to do something like the following:

g.upsertMovie(String movieId).hasActor(__.upsertActor(String actorId))

I expect this traversal to create an actor and a movie with an edge between them.

Creating the upsertMovie step is not an issue.  How can I implement the anonymous traversal __.upsertActor(String actorId) though?  The tinkerpop documentation advises me to avoid using a lambda.   Thanks!

Florian Hockmann

unread,
Mar 28, 2022, 4:00:12 AM3/28/22
to Gremlin-users
Did you see the part of the reference docs about developing a DSL: https://tinkerpop.apache.org/docs/current/reference/#gremlin-java-dsl ?

It shows examples that extend GraphTraversal and GraphTraversalSource and also explains that the __ can be extended that way. You extend that class and then implement your own upsertActor() method which calls a method in __ (probably addV()) and then methods from GraphTraversal.

Yaara Zegelman

unread,
Mar 28, 2022, 9:14:09 AM3/28/22
to Gremlin-users
If you're writing in Java I guess Florian's answer solves it.

I also implemented DSL in javascript and came across this issue. I eventually looked at how gremlin-javascript implements the statics and decided to copy everything with small changes:
```
function callOnEmptyTraversal(fnName: string, args: any[]) {
const g = new MyTraversal(null, null, new process.Bytecode());
// @ts-expect-error
return g[fnName].apply(g, args);
}

/**
* Contains the static method definitions
* @type {Object}
*/
const statics = {
// My functions
myFunction: (...args: any[]) => callOnEmptyTraversal('myFunction', args),
// copied from library
V: (...args: any[]) => callOnEmptyTraversal('V', args),
addE: (...args: any[]) => callOnEmptyTraversal('addE', args),
addV: (...args: any[]) => callOnEmptyTraversal('addV', args),
aggregate: (...args: any[]) => callOnEmptyTraversal('aggregate', args),
and: (...args: any[]) => callOnEmptyTraversal('and', args),
as: (...args: any[]) => callOnEmptyTraversal('as', args),
barrier: (...args: any[]) => callOnEmptyTraversal('barrier', args),
both: (...args: any[]) => callOnEmptyTraversal('both', args),
bothE: (...args: any[]) => callOnEmptyTraversal('bothE', args),
bothV: (...args: any[]) => callOnEmptyTraversal('bothV', args),
branch: (...args: any[]) => callOnEmptyTraversal('branch', args),
cap: (...args: any[]) => callOnEmptyTraversal('cap', args),
choose: (...args: any[]) => callOnEmptyTraversal('choose', args),
coalesce: (...args: any[]) => callOnEmptyTraversal('coalesce', args),
coin: (...args: any[]) => callOnEmptyTraversal('coin', args),
constant: (...args: any[]) => callOnEmptyTraversal('constant', args),
count: (...args: any[]) => callOnEmptyTraversal('count', args),
cyclicPath: (...args: any[]) => callOnEmptyTraversal('cyclicPath', args),
dedup: (...args: any[]) => callOnEmptyTraversal('dedup', args),
drop: (...args: any[]) => callOnEmptyTraversal('drop', args),
elementMap: (...args: any[]) => callOnEmptyTraversal('elementMap', args),
emit: (...args: any[]) => callOnEmptyTraversal('emit', args),
filter: (...args: any[]) => callOnEmptyTraversal('filter', args),
flatMap: (...args: any[]) => callOnEmptyTraversal('flatMap', args),
fold: (...args: any[]) => callOnEmptyTraversal('fold', args),
group: (...args: any[]) => callOnEmptyTraversal('group', args),
groupCount: (...args: any[]) => callOnEmptyTraversal('groupCount', args),
has: (...args: any[]) => callOnEmptyTraversal('has', args),
hasId: (...args: any[]) => callOnEmptyTraversal('hasId', args),
hasKey: (...args: any[]) => callOnEmptyTraversal('hasKey', args),
hasLabel: (...args: any[]) => callOnEmptyTraversal('hasLabel', args),
hasNot: (...args: any[]) => callOnEmptyTraversal('hasNot', args),
hasValue: (...args: any[]) => callOnEmptyTraversal('hasValue', args),
id: (...args: any[]) => callOnEmptyTraversal('id', args),
identity: (...args: any[]) => callOnEmptyTraversal('identity', args),
in_: (...args: any[]) => callOnEmptyTraversal('in_', args),
inE: (...args: any[]) => callOnEmptyTraversal('inE', args),
inV: (...args: any[]) => callOnEmptyTraversal('inV', args),
index: (...args: any[]) => callOnEmptyTraversal('index', args),
inject: (...args: any[]) => callOnEmptyTraversal('inject', args),
is: (...args: any[]) => callOnEmptyTraversal('is', args),
key: (...args: any[]) => callOnEmptyTraversal('key', args),
label: (...args: any[]) => callOnEmptyTraversal('label', args),
limit: (...args: any[]) => callOnEmptyTraversal('limit', args),
local: (...args: any[]) => callOnEmptyTraversal('local', args),
loops: (...args: any[]) => callOnEmptyTraversal('loops', args),
map: (...args: any[]) => callOnEmptyTraversal('map', args),
match: (...args: any[]) => callOnEmptyTraversal('match', args),
math: (...args: any[]) => callOnEmptyTraversal('math', args),
max: (...args: any[]) => callOnEmptyTraversal('max', args),
mean: (...args: any[]) => callOnEmptyTraversal('mean', args),
min: (...args: any[]) => callOnEmptyTraversal('min', args),
not: (...args: any[]) => callOnEmptyTraversal('not', args),
optional: (...args: any[]) => callOnEmptyTraversal('optional', args),
or: (...args: any[]) => callOnEmptyTraversal('or', args),
order: (...args: any[]) => callOnEmptyTraversal('order', args),
otherV: (...args: any[]) => callOnEmptyTraversal('otherV', args),
out: (...args: any[]) => callOnEmptyTraversal('out', args),
outE: (...args: any[]) => callOnEmptyTraversal('outE', args),
outV: (...args: any[]) => callOnEmptyTraversal('outV', args),
path: (...args: any[]) => callOnEmptyTraversal('path', args),
project: (...args: any[]) => callOnEmptyTraversal('project', args),
properties: (...args: any[]) => callOnEmptyTraversal('properties', args),
property: (...args: any[]) => callOnEmptyTraversal('property', args),
propertyMap: (...args: any[]) => callOnEmptyTraversal('propertyMap', args),
range: (...args: any[]) => callOnEmptyTraversal('range', args),
repeat: (...args: any[]) => callOnEmptyTraversal('repeat', args),
sack: (...args: any[]) => callOnEmptyTraversal('sack', args),
sample: (...args: any[]) => callOnEmptyTraversal('sample', args),
select: (...args: any[]) => callOnEmptyTraversal('select', args),
sideEffect: (...args: any[]) => callOnEmptyTraversal('sideEffect', args),
simplePath: (...args: any[]) => callOnEmptyTraversal('simplePath', args),
skip: (...args: any[]) => callOnEmptyTraversal('skip', args),
store: (...args: any[]) => callOnEmptyTraversal('store', args),
subgraph: (...args: any[]) => callOnEmptyTraversal('subgraph', args),
sum: (...args: any[]) => callOnEmptyTraversal('sum', args),
tail: (...args: any[]) => callOnEmptyTraversal('tail', args),
timeLimit: (...args: any[]) => callOnEmptyTraversal('timeLimit', args),
times: (...args: any[]) => callOnEmptyTraversal('times', args),
to: (...args: any[]) => callOnEmptyTraversal('to', args),
toE: (...args: any[]) => callOnEmptyTraversal('toE', args),
toV: (...args: any[]) => callOnEmptyTraversal('toV', args),
tree: (...args: any[]) => callOnEmptyTraversal('tree', args),
unfold: (...args: any[]) => callOnEmptyTraversal('unfold', args),
union: (...args: any[]) => callOnEmptyTraversal('union', args),
until: (...args: any[]) => callOnEmptyTraversal('until', args),
value: (...args: any[]) => callOnEmptyTraversal('value', args),
valueMap: (...args: any[]) => callOnEmptyTraversal('valueMap', args),
values: (...args: any[]) => callOnEmptyTraversal('values', args),
where: (...args: any[]) => callOnEmptyTraversal('where', args)
};
```
:(

Florian Hockmann

unread,
Mar 28, 2022, 9:47:55 AM3/28/22
to Gremlin-users
> If you're writing in Java I guess Florian's answer solves it.

> I also implemented DSL in javascript and came across this issue.

Did you see how the reference docs suggest to implement a DSL in Javascript?
For anonymous traversals which are implemented in Java in the __ class, it shows this code:

function anonymous() {
  return new SocialTraversal(null, null, new Bytecode());
}

function aged(age) {
  return anonymous().aged(age);
}


with this explanation:

Typically, steps that are made available on a GraphTraversal (i.e. SocialTraversal in this example) should also be made available as spawns for anonymous traversals. The recommendation is that these steps be exposed in the module as standalone functions. In the example above, the standalone aged() step creates an anonymous traversal through an anonymous() utility function. The method for creating these standalone functions can be handled in other ways if desired.

Daniel Craig

unread,
Mar 28, 2022, 11:34:57 AM3/28/22
to Gremlin-users
Thanks Florian and Yaara!  I learned that in Java the __ class is generated automatically, that's quite a nice convenience.  Cheers!
Reply all
Reply to author
Forward
0 new messages