简体   繁体   中英

How can I conditionally add a vertex and multiple edges using Gremlin in Amazon Neptune?

I'm using Amazon Neptune for a personal project and I'm having trouble writing a query. I'm using Gremlin-Java to query and mutate the graph.

The best way I can think of to represent the desired outputs for different inputs is with diagrams, so below is the problem statement in diagram form, broken into 3 cases. Lastly, a full solution isn't necessary (though would be accepted) - even a nudge in the right direction would be much appreciated!

Here's the code I have that should perform this mutation, but it must have a flaw (or multiple flaws!) because it doesn't mutate the graph at all in Case 1 and I've thus been unable to test Case 2 . Case 3 , Case 4 , and Case 5 would also not exhibit the desired behavior with this code.

void createShard(String username, String shardName, Set<String> inheritedShardNames, Set<String> inheritedUsers) {}
    g.V()
        .hasLabel("shard)
        .has("shardName", P.within(inheritedShardNames))
        .as("inheritedShards")
    .V()
        .hasLabel("user")
        .has("username", P.within(inheritedUsers))
        .as("inheritedUsers")
    .V()
        .has("user", "username", username)
        .as("user")
    .addV("shard)
        .property(single, "shardName", shardName)
        .property(single, "createdAt", new Date())
        .as("newShard")
    .addE("shardInheritsShard").from("newShard").to("inheritedShards")
    .addE("shardInheritsUser").from("newShard").to("inheritedUsers")
    .addE("userOwnsShard").from("user").to("newShard")
    .addE("userFollowsShard").from("user").to("newShard")
    .iterate();
}

Case 1

Function call:

createShard(
    "john",
    "newShard",
    new HashSet<>(),
    new HashSet<>()
)

Initial Graph State UML

Desired Final Graph State UML

Case 2

Function call:

createShard(
    "john",
    "newShard",
    new HashSet<>(Arrays.asList("firstShardToInherit", "secondShardToInherit")),
    new HashSet<>(Arrays.asList("john", "userToInherit"))
)

Initial Graph State UML

Desired Final Graph State UML

Case 3

Function call:

createShard(
    "john",
    "newShard",
    new HashSet<>(Arrays.asList("userWhoDoesNotExist")),
    new HashSet<>(),
)

Initial Graph State UML

Desired Final Graph State: Same as initial graph state. Would be ideal if the mutation query would throw an exception like java.util.NoSuchElementException or give some other indication that the graph wasn't mutated by the query.

Case 4

Function call:

createShard(
    "john",
    "newShard",
    new HashSet<>(Arrays.asList()),
    new HashSet<>(Arrays.asList("shardWhichDoesNotExist"))
)

Initial Graph State UML

Desired Final Graph State: Same as initial graph state. Would be ideal if the mutation query would throw an exception like java.util.NoSuchElementException or give some other indication that the graph wasn't mutated by the query.

Case 5

Function call:

createShard(
    "john",
    "existingShardName",
    new HashSet<>(),
    new HashSet<>()
)

Initial Graph State UML

Desired Final Graph State: Same as initial graph state. Would be ideal if the mutation query would throw an exception or give some other indication that the graph wasn't mutated by the query because a vertex with the same label and "shardName" property already exists.

This is a complex question but I think your problems tend to stem from assuming that more your traversal will execute than what actually is. Let's take "Case 1" as an example where it starts with just one "user" vertex in existence with the "name" of "john". Your traversal begins as:

g.V().hasLabel("shard).
  has("shardName", P.within(inheritedShardNames)).as("inheritedShards")

As there are now "shard" vertices in the graph, the traversal immediately produces no traversers (ie objects that travel in the stream to carry the data) and immediately terminates as there is nothing to trigger downstream steps. It seems that you want some form of upsert or "get or create" sort of pattern to solve your problem.

A quick example in Gremlin Console demonstrates the issue:

gremlin> g = TinkerFactory.createModern().traversal()
==>graphtraversalsource[tinkergraph[vertices:6 edges:6], standard]
gremlin> g.V().hasLabel('country').addV('person')
gremlin> g.V().hasLabel('country').fold().addV('person')
==>v[13]

The first gV().hasLabel('country').addV('person') does nothing because there are no "country" vertices in the graph and therefore addV() is never triggered. On the other hand, the traversal following that one does add a vertex because calling fold() is a reducing operation that will produce a traverser carrying a List object that is empty, which in turn enables addV() to be called.

The patterns for upsert-style traversals with Gremlin are described in a variety of places. Please see:

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM