Search code examples
gremlingraph-databasestinkerpoptinkerpop3

Combine two gremlin queries with individual start steps into one


I have two gremlin queries, both with individual start steps. I have no control over their inner structure (they are passed to me as method arguments). My task is to combine them into a single traversal. So basically I need to implement this:

public GraphTraversal<*,*> createUnion(
    GraphTraversal<*, *> t1, 
    GraphTraversal<*,*> t2
){
   // how?
}

We have to assume that:

  • Both t1 and t2 have individual start steps (.V() / .E())
  • If t1 starts with .V(), t2 may start with E() or vice versa.

I tried the gremlin union step (which does accept sub-traversals), but the following (while syntactically valid) doesn't work:

t1.union(
    __.identity(),
   t2
)

... because union steps in gremlin will always be evaluated per input element of t1. Clearly that's not what we want here.

I also tried the inject(...) step:

t1.inject(t2.toList().toArray(new Object[0]))

There are two issues with this:

  1. t2 is eagerly evaluated during the construction of the query
  2. Any labels, side effects etc. which are defined in t2 will not be available in the resulting traversal.

Are there any better ways to do this? Ideally a way that preserves the labels in t1 and t2 and preserves lazy evaluation?


Solution

  • We've often seen cases for union() to be a start step, but it hasn't been implemented as such yet. You can workaround this by using inject() and a throwaway traverser:

    gremlin> t1 = __.V().has('person','name','josh');[]
    gremlin> t2 = __.V().has('person','name','peter');[]
    gremlin> g.inject(0).union(t1, t2)
    ==>v[4]
    ==>v[6]
    

    The dummy or "0" is just used to start the traversal and then is ignored at the union() since the child traversals start with mid-traversal V().

    Take care to make sure that your traversal that uses this pattern will actually optimize the execution. An explain()/profile() should hint you into the underlying behavior.