@recurse
Recurse queries let you traverse a set of predicates (with filter, facets, etc.) until we reach all leaf nodes or we reach the maximum depth which is specified by the depth parameter.
To get 10 movies from a genre that has more than 30000 films and then get two actors for those movies we'd do something as follows:
- Query
- Go
- Java
- Python
- JavaScript (gRPC)
- JavaScript (HTTP)
- Curl
{
me(func: gt(count(~genre), 30000), first: 1) @recurse(depth: 5, loop: true) {
name@en
~genre (first:10) @filter(gt(count(starring), 2))
starring (first: 2)
performance.actor
}
}
package main
import (
"context"
"encoding/json"
"fmt"
"log"
"google.golang.org/grpc"
"github.com/dgraph-io/dgo/v230"
"github.com/dgraph-io/dgo/v230/protos/api"
)
func main() {
conn, err := grpc.Dial("localhost:9080", grpc.WithInsecure())
if err != nil {
log.Fatal(err)
}
defer conn.Close()
dgraphClient := dgo.NewDgraphClient(api.NewDgraphClient(conn))
ctx := context.Background()
txn := dgraphClient.NewTxn()
defer txn.Discard(ctx)
query := `{
me(func: gt(count(~genre), 30000), first: 1) @recurse(depth: 5, loop: true) {
name@en
~genre (first:10) @filter(gt(count(starring), 2))
starring (first: 2)
performance.actor
}
}`
resp, err := txn.Query(ctx, query)
if err != nil {
log.Fatal(err)
}
var result map[string]interface{}
json.Unmarshal(resp.Json, &result)
fmt.Printf("%+v\n", result)
}
import io.dgraph.DgraphClient;
import io.dgraph.DgraphGrpc;
import io.dgraph.DgraphProto;
import io.dgraph.Transaction;
import java.util.Map;
public class App {
public static void main(String[] args) {
ManagedChannel channel = ManagedChannelBuilder
.forAddress("localhost", 9080)
.usePlaintext()
.build();
DgraphGrpc.DgraphStub stub = DgraphGrpc.newStub(channel);
DgraphClient dgraphClient = new DgraphClient(stub);
String query = "{
me(func: gt(count(~genre), 30000), first: 1) @recurse(depth: 5, loop: true) {
name@en
~genre (first:10) @filter(gt(count(starring), 2))
starring (first: 2)
performance.actor
}
}";
Transaction txn = dgraphClient.newTransaction();
try {
DgraphProto.Response response = txn.query(query);
System.out.println(response.getJson().toStringUtf8());
} finally {
txn.discard();
}
}
}
import grpc
from dgraph import DgraphClient, Txn
def main():
client_stub = DgraphClient("localhost:9080")
client = DgraphClient(client_stub)
query = """{
me(func: gt(count(~genre), 30000), first: 1) @recurse(depth: 5, loop: true) {
name@en
~genre (first:10) @filter(gt(count(starring), 2))
starring (first: 2)
performance.actor
}
}"""
txn = client.txn()
try:
response = txn.query(query)
print(response.json)
finally:
txn.discard()
if __name__ == "__main__":
main()
const dgraph = require("dgraph-js");
const grpc = require("grpc");
async function main() {
const clientStub = new dgraph.DgraphClientStub(
"localhost:9080",
grpc.credentials.createInsecure()
);
const dgraphClient = new dgraph.DgraphClient(clientStub);
const query = `{
me(func: gt(count(~genre), 30000), first: 1) @recurse(depth: 5, loop: true) {
name@en
~genre (first:10) @filter(gt(count(starring), 2))
starring (first: 2)
performance.actor
}
}`;
const txn = dgraphClient.newTxn();
try {
const res = await txn.query(query);
const json = res.getJson();
console.log(JSON.stringify(JSON.parse(json), null, 2));
} finally {
await txn.discard();
}
}
main().catch((e) => {
console.error(e);
});
const fetch = require("node-fetch");
async function main() {
const query = `{
me(func: gt(count(~genre), 30000), first: 1) @recurse(depth: 5, loop: true) {
name@en
~genre (first:10) @filter(gt(count(starring), 2))
starring (first: 2)
performance.actor
}
}`;
const response = await fetch("http://localhost:8080/query", {
method: "POST",
headers: {
"Content-Type": "application/dql"
},
body: query
});
const result = await response.json();
console.log(JSON.stringify(result, null, 2));
}
main().catch((e) => {
console.error(e);
});
curl -X POST http://localhost:8080/query \
-H "Content-Type: application/dql" \
-d '{
me(func: gt(count(~genre), 30000), first: 1) @recurse(depth: 5, loop: true) {
name@en
~genre (first:10) @filter(gt(count(starring), 2))
starring (first: 2)
performance.actor
}
}'
Some points to keep in mind while using recurse queries are:
- You can specify only one level of predicates after root. These would be traversed recursively. Both scalar and entity-nodes are treated similarly.
- Only one recurse block is advised per query.
- Be careful as the result size could explode quickly and an error would be returned if the result set gets too large. In such cases use more filters, limit results using pagination, or provide a depth parameter at root as shown in the example above.
- The
loopparameter can be set to false, in which case paths which lead to a loop would be ignored while traversing. - If not specified, the value of the
loopparameter defaults to false. - If the value of the
loopparameter is false and depth is not specified,depthwill default tomath.MaxUint64, which means that the entire graph might be traversed until all the leaf nodes are reached.