Search code examples
gogremlinupsertamazon-neptune

Gremlin MergeV To Update an Existing Element's property and Single Cardinality


I am using Amazon Neptune with Gremlin-go for our Graph Database needs. I am working on code to insert/update (upsert) an element with g.mergeV(). However, when a match is found, and a property gets updated, the new value is added to a value-list. I wanted to set the property that is getting updated as a single (Cardinality), so at any given time it will have only one value. This is my code

g.mergeV(['firstname': 'fname']).
    option(onCreate, [(T.label): 'Someone','lastname': 'lname']).
    option(onMatch, ['lastname': 'lname1']))

I would like the values on update to look like this

{'firstname': ['fname'], 'lastname': ['lname1']}

But, values after update look like this

{'firstname': ['fname'], 'lastname': ['lname','lname1']}

Unfortunately, Neptune's default property Cardinality is a List. I was hoping using the property() would work if a match is found, however get this error

g.mergeV(['firstname': 'fname']).
    option(onCreate, [(T.label): 'Someone','lastname': 'lname']).
    option(onMatch, __.property(single, ['lastname': 'lname1'])))

"detailedMessage": "com.amazon.neptune.tinkerpop.structure.NeptuneVertex cannot be cast to java.util.Map"

I have also tried the fold/coalesce/unfold route, but values don't get updated if a record is found

g.V().hasLabel("Someone").
      has("firstname", "fname").
      has("lastname", "lname").
      fold().
      coalesce(unfold(), 
               __.addV("Someone").property(single, ["firstname": "fname", "lastname": "lname1"]))

I am open to using another way, as long as it is upsert.


Solution

  • A few comments.

    1. The default Cardinality used by Amazon Neptune is actually Set and not List
    2. To use the coalesce form of the query, you need to provide an action more than just unfold for the LHS as that will be the path taken if the vertex already exists. So the query will be something like this:
    g.V().hasLabel("Someone").
          has("firstname", "fname").
          has("lastname", "lname").
          fold().
          coalesce(unfold().property(single,"firstname","Newname"), 
                   addV("Someone").property(single, ["firstname": "fname", "lastname": "lname1"]))
    
    1. The mergeV query can be made to work. You just need to use a slightly different form along these lines. The sideEffect performs the actual update and the constant([:]) generates an empty map as there are no more changes needed.
    g.mergeV(["firstname":"firstname"]).
       option(Merge.onMatch,sideEffect(property(single,"firstname","Newname")).constant([:]))
    

    Note You will need to be running Amazon Neptune with an engine version of 1.2.1.0.R2 or higher for the sideEffect version of the query to work.