Use scalar predicate on higher level as filter at lower levels


#1

I have loaded Ethereum blockchain into DGraph. Schema is this:

address: string @index(hash) .
from: uid @reverse .
to: uid @reverse .
hash: string @index(hash) .
value: float .
timestamp: int @index(int) .
block_number: int @index(int) .

Data is organized like this:

<1> “abc”
<2> “def”
<3> “hhh”
<3> 12345
<1> <3>
<3> <2>

The RDF above is for sending Ethereum from address with UID 1 to address with UID 2 through transaction with UID 3. I have omitted non-relevant scalar predicates.

I need to scan graph N levels from the address of interest. Let’s say I sent something, I want to see where it went then. I can build a query by nesting levels, like that:

 me(func: eq(address, "0x13b0e7e1d01b2678659a83a3d83fb60e1e594892")) {
   from {
     timestamp
     to {
       address
       from {
         timestamp
         to {
           address
         }
       }
     }
   }
 }

However, I need to farther filter this query by adding a condition that in chain (addressA)-[from]->(trx1)-[to]->(addressB)-[from]->(trx2)-[to]->(addressC), timestamp of trx2 must be greater than timestamp of trx1. Is it doable with GraphQL? I have read about variable propagation, but cannot see how it can be used in my task. I understand that I can define a variable which will have timestamp of transactions of the first level:

 var(func: eq(address, "0x13b0e7e1d01b2678659a83a3d83fb60e1e594892")) {
   from {
     t as timestamp
   }
 }
 
 me(func: uid(t)) {
   val(t)
   to {
     address
   }
 }

But how do I propagate value of timestamp scalar predicate down to nested level? Is there a concept of a parameter to a function, so that I won’t hardcode address in my function, but rather pass address predicate to a function call?


(Michel Conrado) #2

Based on the first part of your question, I think Recurse Query is a good option.

 me(func: eq(address, "0x13b0e7e1d01b2678659a83a3d83fb60e1e594892"))
 @recurse(depth: 15, loop: true) {
   from
   timestamp
   to
   address
 }

https://docs.dgraph.io/query-language/#recurse-query

But I did not quite understand the second part. But I make it clear that you can use filters with Recurse Query. However below is a link to k-shortest-path-queries. That may be useful.

https://docs.dgraph.io/query-language/#k-shortest-path-queries

Anything I’m around.

Cheers.


#3

Thank you for the answer! Currently I have another issue which is more important. If I have simple query:

{
  me(func: eq(address, "0x13b0e7e1d01b2678659a83a3d83fb60e1e594892")) {
    from {
      timestamp
      to {
        address
      }
    }
  }
}

It completes fairly fast:

“parsing_ns”:24304,“processing_ns”:4658520,“encoding_ns”:2217665

But when I add filter by timestamp scalar predicate, it slows down dramatically:

{
  me(func: eq(address, "0x13b0e7e1d01b2678659a83a3d83fb60e1e594892")) {
    from @filter(gt(timestamp, 1512534503)) {
      timestamp
      to {
        address
      }
    }
  }
}

“parsing_ns”:22584,“processing_ns”:1596928218,“encoding_ns”:2194180

See that execution time has bumped from 4ms to 1.6s. Am I doing anything wrong with the filter?


(Nikita Zaletov) #4

unfortunately for now dgraph needs index for all eq, gt and other operations, so it reads all values from that index that are greater than 1512534503, and then uses this list.
issue is described here:

possible workarounds:
use facets. filter by facets doesn’t require index and works as you want. it works but it looks ugly. example is posted in that issue above


#5

Nikita, thank you! Facets helped with performance a lot. Query which was taking 190 seconds now takes 19 with the same result. However, with facets for some reason query with parameters ceased to work from Java:

query me($a: string, $t: int, $f: int, $o: int) {
  me(func: eq(address, $a)) {
   from @facets(gt(ts, $t)) (first: $f, offset: $o) {
     timestamp
     to {
       address
     }
   }
 }
}

This one is not working, returning empty dataset. If I hardcode parameters in query string, it works, however in this case I suspect I spend too much time on parsing? With filter on predicate instead of facet, parameters have been working. Again I’m doing something wrong?