7

Right now I am able to generate a query to create as many vertices and edges as I want.

e.g.

g.V().
addV('vert1').as('a').
addV('vert2').as('b').
addE('has').from('a').to('b')

^^^^^^^^^^^^^ This works. Easy enough right? Now lets create a gremlin query that only creates these vertices if their label is unique. Then create an edge between the two.

g.V().has(label,'vert1').fold().
    coalesce(
        unfold(),
        addV('vert1')
    ).as('a').
    V().has(label,'vert2').fold().
    coalesce(
        unfold(),
        addV('vert2')
    ).as('b').
    addE('has').from('a').to('b')

^^^^^^^^^^^^^This does not work

hopefully you can understand what I am trying to do though. Can anyone help me?

Thanks

Dominus
  • 808
  • 11
  • 25
Austin Malpede
  • 115
  • 2
  • 8
  • the first line `g.V().addV('vert1').as('a').addV('vert2').as('b').addE('has').from('a').to('b')` does not work for me in AWS Neptune. Any idea why? Seems like the `.as()` is invalid syntax – cryanbhu Oct 20 '18 at 14:01
  • What is the error you are seeing? – Austin Malpede Oct 21 '18 at 15:06
  • `g.V().addV('vert1').as('a').addV('vert2').as('b').addE('has').from('a').to('b')` ^ SyntaxError: invalid syntax. You' cant really see it here, but the arrow is pointing to the as() clause. – cryanbhu Oct 22 '18 at 05:42
  • get rid of the .V() at the beginning. It will look like g.addV('vert1').as('a').addV('vert2').as('b').addE('has').from('a').to('b') – Austin Malpede Oct 23 '18 at 13:16
  • it still doesn't work. I think it is because of Gremlin Python? Maybe the `as` is a special command – cryanbhu Oct 24 '18 at 05:54
  • 1
    Looking at the source code here https://github.com/apache/tinkerpop/blob/master/gremlin-python/src/main/jython/gremlin_python/process/graph_traversal.py I am unable to see the as step defined. This is very odd, not sure why this might be. This could be a question for @stephenmalette. Are there any alternative languages you could use? I know gremlin.net is very well developed. – Austin Malpede Oct 24 '18 at 15:26
  • 1
    @cryanbhu after further investigation of the python traversal class it looks like the step is defined as as_. try `g.addV('vert1').as_('a').addV('vert2').as_('b').addE('has').from('a').to('b')`. Happy coding! – Austin Malpede Oct 24 '18 at 15:28
  • Didn't realize i could just check out the gremlin-python code directly there. Thank you! @austin-malpede – cryanbhu Oct 25 '18 at 05:19

2 Answers2

11

You have a fold() which is a ReducingBarrierStep that follows after your step label at as('a') and the path history to "a" is lost after that step. You can read more about this aspect of Gremlin here.

You just need to re-write your query to account for that - one way might be to just aggregate() the value of "a" rather than simply naming the step "a":

gremlin> g = TinkerGraph.open().traversal()
==>graphtraversalsource[tinkergraph[vertices:0 edges:0], standard]
gremlin> g.V().
......1>   has(label,'vert1').fold().
......2>   coalesce(unfold(),
......3>            addV('vert1')).aggregate('a').
......4>   V().has(label,'vert2').fold().
......5>   coalesce(unfold(),
......6>            addV('vert2')).as('b').
......7>   select('a').unfold().
......8>   addE('has').to('b')
==>e[2][0-has->1]

If you need to return all the elements, just project() the returned edge and transform the results as necessary:

gremlin> g.V().
......1>   has(label,'vert1').fold().
......2>   coalesce(unfold(),
......3>            addV('vert1')).aggregate('a').
......4>   V().has(label,'vert2').fold().
......5>   coalesce(unfold(),
......6>            addV('vert2')).as('b').
......7>   select('a').unfold().
......8>   addE('has').to('b').
......9>   project('e','in','out').
.....10>     by().
.....11>     by(inV()).
.....12>     by(outV())
==>[e:e[2][0-has->1],in:v[1],out:v[0]]

Of course, using a select() at the end might not be so bad either:

gremlin> g = TinkerGraph.open().traversal()
==>graphtraversalsource[tinkergraph[vertices:0 edges:0], standard]
gremlin> g.V().
......1>   has(label,'vert1').fold().
......2>   coalesce(unfold(),
......3>            addV('vert1')).aggregate('a').
......4>   V().has(label,'vert2').fold().
......5>   coalesce(unfold(),
......6>            addV('vert2')).as('b').
......7>   select('a').unfold().
......8>   addE('has').to('b').as('x').
......9>   select('a','b','x')
==>[a:[v[0]],b:v[1],x:e[2][0-has->1]]
stephen mallette
  • 45,298
  • 5
  • 67
  • 135
  • Would it be possible to have 'a' stored unfolded so that the edge could be constructed like addE('has').from('a').to('b')? – Austin Malpede Aug 07 '18 at 17:30
  • 1
    To answer my comment above it looks like addE('has').from(select('a').unfold()).to(select('b').unfold) is what I am looking for. Thanks for your help Stephan. You are the GRAPH KING – Austin Malpede Aug 07 '18 at 17:35
  • For the solution you came up with is there any way to have everything that was created returned? You can see that e[2][0-has->1] is your return value. What if I want both the vertices returned? Hopefully there's an alternative to adding select('a','b') to the end. – Austin Malpede Aug 08 '18 at 13:18
  • updated my answer - i'd just `project()` the resulting edge. i think your idea to `select()` is not so bad either. might be the most efficient approach actually, as those elements are already in memory. – stephen mallette Aug 08 '18 at 13:56
  • hi @stephenmallette Can you help me answer my question here. https://stackoverflow.com/questions/54265985/create-if-not-exist-vertex-and-edge-in-1-query-gremlin – thangdc94 Jan 19 '19 at 10:27
0

I, too, was looking for an idempotent way to create several vertices in a single command and get the results back. (I use Cosmos DB's Gremlin API, and the typical solution using fold...coalesce...unfold doesn't work when chained together.)

Through some experimentation, I came up with this alternative whose complexity is linear as you add more vertices. I'm using inject() to artificially create a "source" for the first invocation of coalesce().

g.inject("0")
  .coalesce(__.V(['pk1','00']), addV('MyV')
    .property('id','00')
    .property('partitionKey','pk1')).as('x')
  .coalesce(__.V(['pk1','01']), addV('MyV')
    .property('id','01')
    .property('partitionKey','pk1'))
    .as('x')
  .coalesce(__.V(['pk1','02']), addV('MyV')
    .property('id','02')
    .property('partitionKey','pk1'))
    .as('x')
.select('x')

For those unconcerned about partition keys, that looks like this:

g.inject("0")
  .coalesce(__.V('00'), addV('MyV').property('id','00')).as('x')
  .coalesce(__.V('01'), addV('MyV').property('id','01')).as('x')
  .coalesce(__.V('02'), addV('MyV').property('id','02')).as('x')
.select('x')

Additionally, we can create any number of edges between new vertices at the same time by providing unique labels (with as()).

g.inject("0")
  .coalesce(__.V(['pk2','05']), addV('MyV').property('id','05')
    .property('partitionKey','pk2')
    .property('ABC','123467890'))
    .as('x').as('a')
  .coalesce(__.V(['pk2','06']), addV('MyV').property('id','06')
    .property('partitionKey','pk2')
    .property('ABC','123467890'))
    .as('x')
  .coalesce(g.E(['pk2','07']), addE('MyE').property('id','07').from('a'))
    .as('x')
.select('x')
Gyromite
  • 769
  • 6
  • 16