Negations over Recurse Blocks

Hi all :slight_smile:

Hoping for some help with a query, and a possible feature request.

I have a set of C# AST data stored in Dgraph. I haven’t been able to figure out a way to “find all methods that don’t have a for loop.”

I can “find all methods with a for loop” by finding all for loops, and recursing back to the method declaration like so:

{
  recurse (func: eq(kind, "csharp.for_statement")){
    method as ~child
  }
  
  method(func: uid(method)) {
    @filter(eq(kind, "csharp.method_declaration"))
    kind
    identifier_token
  }
}

I can also “find all for loops inside methods” by reversing reversing the variable assignment:

{
  recurse (func: eq(kind, "csharp.method_declaration")){
    for as child
  }
  
  for(func: uid(for)) {
    @filter(eq(kind, "csharp.for_statement"))
    kind
    identifier_token
  }
}

But I can’t get “methods without for loops”, or “for loops that aren’t in methods” with this method. What do you think is the best way to do it?

Just spitballing: might it be possible to express the whole query as a single block with a root of func: eq(kind, "csharp.method_declaration"), put the recurse inside the block, and just use NOT on the filter?

Something vaguely like this:

 me(func: eq(kind, "csharp.method_declaration")) @cascade {
    recurse () {
        @filter(NOT eq(kind, "csharp.for_statement")
        kind
        child
    }
  }

Hey @BlakeMScurr
For methods without for loops, you can get methodsWithFor and also all methods and then do a not like below.

{
  recurse (func: eq(kind, "csharp.for_statement")) {
    methodsWithFor as ~child @filter(eq(kind, "charp.method_declaration"))
  }
  
  methods_without_for(func: eq(kind, "csharp.method_declaration")) @filter(not uid(methodsWithFor)) {
    identifier_token
    kind
  }
}

Similarly for loops that aren’t in methods - Subtract for loops that are in a method from all the for loops.

{
  recurse (func: eq(kind, "csharp.method_declaration")) {
    forLoopInMethod as child @filter(eq(kind, "csharp.for_statement"))
  }
  
  forLoopNotInMethods(func: eq(kind, "csharp.for_statement")) @filter(not uid(forLoopInMethod)) {
    kind
    identifier_token
  }
}
1 Like

OK nice, thanks! That captured it.

However, we have come across a couple of other reasons why recursion inside blocks would be nice. It would be great to get your feedback on it, and find out if it’s possible.

1/ It’s really nice to be able to express a graph pattern, and return values for every part of that pattern, which is the classic GraphQL style. For example, we can grab a method with a for loop with a variable, and get a full decsription of all those elements:

me(func: eq(kind, "method")) @cascade {
    expand(_all_)
    child @filter(kind, "forloop") {
        expand(_all_)
        child @filter(kind, "variable")
        expand(_all_)
    }
}

But as soon as we introduce recursion, we have to split our query into blocks, and choose which values we are interested in. In the “for loops in methods” statement from above, I can only use the results from the from the second block, because the first block is just all method children:

{
  recurse (func: eq(kind, "csharp.method_declaration")){
    for as child
    expand(_all_)
  }
  
  for(func: uid(for)) {
    @filter(eq(kind, "csharp.for_statement"))
    expand(_all_)
  }
}

So I have to write two different queries (or one query with multiple recurse blocks) to show all the methods with for loops, and show the for loops themselves. This is probably less efficient, and definitely not as nice to work with.

2/ With your current solution we have to choose the “direction” of the query based on whether we’re interested in for loops or methods. By direction I mean which block depends on variables from which.

But it would be nicer if you could choose the direction based on the number of for loops vs methods to optimize speed. For example, if there are lots of methods without for loops, but not many for loops outside of methods, it would be faster to find start at the for loops and find the methods that contain them.

But if we go in that direction we can’t find the for loops that are in methods, as our for loop block will just represent all for loops.

Hope that all makes sense, and thanks for all your help so far :smiley: it’s making Dgraph really nice to use.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.