Search code examples
graphgremlintinkerpop

Error when constructing Gremlin traversal using Lambda function


I have a vertex that is set with properties startDate and endDate:

Date start = new Date();
Date end = Date.from(start.toInstant().plusSeconds(600));
g.V(8L).property("startDate", start).property("endDate", end).next();

Now I want to compute the difference endDate-startDate and I could do that with sack():

g.V(8L)
    .sack(Operator.assign).by(values("endDate").map(it -> ((Date)it.get()).getTime()))
    .sack(Operator.minus).by(values("startDate").map(it -> ((Date)it.get()).getTime()))
    .sack()

However, this does not work if the traversal is sent remotely.

By converting the Lambda into Lambda.function(), the below traversal works remotely:

String funcGetTime = "v -> ((Date)v.get()).getTime()";
g.V(8L)
        .sack(Operator.assign).by(values("endDate").map(Lambda.function(funcGetTime)))
        .sack(Operator.minus).by(values("startDate").map(Lambda.function(funcGetTime)))
        .sack()

However, the traversal no longer works in my in-memory graph local setup. The error message is:

java.lang.IllegalArgumentException: The provided traverser does not map to a value: v[8]->[PropertiesStep([endDate],value), LambdaMapStep(lambda[v -> ((Date)v.get()).getTime()])]

My question is: Is there a traversal that does the same thing (calculate the diff between endDate and startDate) that can work for both local and remote? I need it since my unit tests are run locally with in-memory graph while during running, my application invokes the traversal by connecting to a remote graph.

The reason I have to compute the diff in the traversal (instead of doing in my application) is because I want to use this diff further in the traversal later.

Thanks.


Solution

  • Gremlin does not yet have datetime operators that can help you with this and as you've found a remote lambda won't evaluate in the same fashion as a local one. The remote one requires that the entire traversal be evaluated in the GremlinGroovyScriptEngine, which basically takes the Gremlin Bytecode, runs it through the GroovyTranslator and then evaluates the whole thing as a script. I can simulate that in the Gremlin Console as follows:

    gremlin> evaluate(GroovyTranslator.of("g").translate(g.V().map(Lambda.function("v -> v"))).script)
    ==>v[1]
    ==>v[2]
    ==>v[3]
    ==>v[4]
    ==>v[5]
    ==>v[6]
    

    If you need the remote to work the same as local, I can't think of any workarounds short of either the above approach or by storing your date as long (perhaps as a form of denormalization) so that you can do the calculations directly without having to the DateTime conversion. Hopefully, Gremlin will get better date operations in future versions.