Search code examples
graphgraph-databasesgremlintinkerpop3

traversing a graph with restrictions


I am exploring TinkerPop and Gremlin and want to understand if the language/syntax will support the following graph question and traversal

(I guess that if so, the TinkerPop "enabled" graphs [AWS Neptun/OrientDB/Girafe/..] will support it also ?)

(if you know any graph db which can answer my requirements please let me know)

lets say we have:

  • 3 types of vertices: A,B,C
  • 3 types of edges: Hard, Medium, Soft

any two vertices can be connected by any type of edge

e.g. Example Graph Image

our input:

  • starting vertex
  • value to find

our output/answer:

start from 'input vertex' and find any vertex with type/label 'A' which have value equal to 'input value'

our restrictions:

traversing the graph can be done only under the following rules:

  • From vertex 'A' we can move only to vertex 'B' by edge type/label 'Hard'
  • From B or C we can move to B or C if edge.label == 'Medium' || edge.label == 'Hard'
  • From C to A we can move only if edge.label == 'Hard'

p.s. the answer can be a path or a sub-tree or the id of the node or yes/no I do not care as long as we can answer the graph question

e.g. (from the above image example)

input: Vertex Id = 3 & Value = 56

output: Vertex Id = 5


Solution

  • Let's start with your sample graph (in future questions it would be nice if you could provide this script):

    g = TinkerGraph.open().traversal()
    g.addV('A').
        property(id, 3).                     /* should probably be 56 */
        property('Value', 6).as('A3').
      addV('A').
        property(id, 4).
        property('Value', 56).as('A4').
      addE('soft').from('A4').
      addV('A').
        property(id, 5).
        property('Value', 56).as('A5').
      addV('B').
        property(id, 1).
        property('Value', '0x11').as('B1').
      addE('hard').from('A3').
      addV('B').
        property(id, 2).
        property('Value', '0x01').as('B2').
      addE('med').from('B1').
      addV('C').
        property(id, 7).
        property('Value', 'S').as('C7').
      sideEffect(addE('soft').from('B1')).
      addE('hard').to('A5').
      addV('C').
        property(id, 8).
        property('Value', 'J').
      sideEffect(addE('hard').from('B2')).
      sideEffect(addE('med').to('C7')).
      addE('med').from('A3').
      iterate()
    

    The traversal following your traversal rules would be:

    g.V(3).as('input').     /* don't filter by 'Value', filtering by id is enough */
      repeat(choose(label).
               option('A', out('hard').hasLabel('B')).
               option('B', out('med','hard').hasLabel('B', 'C')).
               option('C', union(out('med','hard').hasLabel('B', 'C'),
                                 out('hard').hasLabel('A'))).
             dedup()).
        emit(where(eq('input')).by('Value'))
    

    ...but as I've mentioned in my comment, there's an issue in your sample graph.

    However, if I change the Value of vertex 3 to 56, we get:

    gremlin> g.V(3).as('input').
    ......1>   repeat(choose(label).
    ......2>            option('A', out('hard').hasLabel('B')).
    ......3>            option('B', out('med','hard').hasLabel('B', 'C')).
    ......4>            option('C', union(out('med','hard').hasLabel('B', 'C'),
    ......5>                              out('hard').hasLabel('A'))).
    ......6>          dedup()).
    ......7>     emit(where(eq('input')).by('Value'))
    ==>v[5]