Search code examples
gremlin

I want to intersect 2 aggregated collections in gremlin


I have a complex scenario where some indirect permissions can be overridden by direct associated permissions.

So basically Role can have A,B,C actions allowed based on his designation But the special permissions can be attached to user as well. So lets say special permission grants only action A,D.

So ultimately he can only perform action A.

And he can't perform B,C,D because its not available in both.

I have written a query with some other considerations as well.

I am stuck at Intersection of 2 aggregated results.

 g.V().hasLabel('r').has('name', 'r1').
    as('r').
  match(
    __.as('a').
    out('has pb').out('allow').
    where(__.in('deny').
      hasLabel('pb').out('deny').
      count().is(0)).fold().aggregate('pballow'),
    __.as('a').
    out('has p').out('allow').
    where(__.in('deny').
      hasLabel('p').out('deny').
      count().is(0)).fold().aggregate('pallow')).
      select('pballow','pallow')
  

I want to intersect pballow and pallow

The sample data and written query already available on following link.

https://gremlify.com/cmr4i3z3ckh/4

So on that result should be only id:3899.


Solution

  • You can do some Gremlin collection manipulation to get your answer.

    g = TinkerGraph.open().traversal()
    g.addV('a').as('1').
      property(single, 'name', 'a1').addV('r').
        as('2').
      property(single, 'name', 'r1').addV('r').
        as('3').
      property(single, 'name', 'r2').addV('p').
        as('4').
      property(single, 'name', 'p1').addV('pb').
        as('5').
      property(single, 'name', 'pb1').addV('a').
        as('6').
      property(single, 'name', 'a1').addV('a').
        as('7').
      property(single, 'name', 'a2').addV('a').
        as('8').
      property(single, 'name', 'a3').addV('re').
        as('9').
      property(single, 'name', 're1').addV('scp').
        as('10').
      property(single, 'name', 'scp1').addV('al').
        as('11').
      property(single, 'name', 'l').addV('al').
        as('12').
      property(single, 'name', 'r').addV('tc').
        as('13').
      property(single, 'name', 'tc1').addV('s').
        as('14').
      property(single, 'name', 's1').
      addE('has scp').from('1').to('10').
      addE('has r').from('1').to('3').addE('has r').
      from('1').to('2').addE('has pb').from('2').
      to('5').addE('has p').from('2').to('4').
      addE('allow').from('4').to('6').addE('allow').
      from('4').to('7').addE('allow').from('4').
      to('8').addE('allow').from('5').to('8').
      addE('deny').from('5').to('6').
      addE('of type').from('6').to('11').addE('on').
      from('6').to('9').addE('of tc').from('7').
      to('13').addE('of type').from('7').to('12').
      addE('on').from('7').to('9').addE('of tc').
      from('8').to('13').addE('of type').from('8').
      to('12').addE('on').from('8').to('9').
      addE('asmd').from('9').to('2').addE('of').
      from('9').to('14')
    

    In your case you have a deeply nested result which I'll show here (I made some optimizations to your code in a few places):

    gremlin> g.V().has('r','name', 'r1').as('r').
    ......1>   match(
    ......2>     __.as('a').
    ......3>     out('has pb').out('allow').
    ......4>     where(__.not(__.in('deny').
    ......5>           hasLabel('pb').out('deny'))).fold().aggregate('pballow'),
    ......6>     __.as('a').
    ......7>     out('has p').out('allow').
    ......8>     where(__.not(__.in('deny').
    ......9>           hasLabel('p').out('deny'))).fold().aggregate('pallow')).
    .....10>   select('pballow','pallow')
    ==>[pballow:[[v[14]]],pallow:[[v[10],v[12],v[14]]]]
    

    That deep nesting requires a lot of unfolding to unpack the vertices you want to intersect, but it can be done:

    gremlin>  g.V().has('r','name', 'r1').as('r').
    ......1>   match(
    ......2>     __.as('a').
    ......3>     out('has pb').out('allow').
    ......4>     where(__.not(__.in('deny').
    ......5>           hasLabel('pb').out('deny'))).fold().aggregate('pballow'),
    ......6>     __.as('a').
    ......7>     out('has p').out('allow').
    ......8>     where(__.not(__.in('deny').
    ......9>           hasLabel('p').out('deny'))).fold().aggregate('pallow')).
    .....10>   select('pballow','pallow').
    .....11>   select(values).
    .....12>   unfold().unfold().unfold().
    .....13>   groupCount().
    .....14>   unfold().
    .....15>   where(select(values).is(gt(1))).
    .....16>   select(keys)
    ==>v[14]
    

    I think you can simplify this traversal though and avoid some of that unpacking. I see no need for match(), the step labelling with as() or the use of aggregate() which creates a side-effect. It seems like union() can replace all that and make this traversal more readable:

    gremlin> g.V().has('r','name', 'r1').
    ......1>   union(out('has pb').out('allow').
    ......2>         where(__.not(__.in('deny').
    ......3>               hasLabel('pb').out('deny'))),
    ......4>         out('has p').out('allow').
    ......5>         where(__.not(__.in('deny').
    ......6>               hasLabel('p').out('deny')))).
    ......7>   groupCount().
    ......8>   unfold().
    ......9>   where(select(values).is(gt(1))).
    .....10>   select(keys)
    ==>v[14]