@cascade
With the @cascade directive, nodes that don't have all predicates specified in the query are removed. This can be useful in cases where some filter was applied or if nodes might not have all listed predicates.
Query Example: Harry Potter movies, with each actor and characters played. With @cascade, any character not played by an actor called Warwick is removed, as is any Harry Potter movie without any actors called Warwick. Without @cascade, every character is returned, but only those played by actors called Warwick also have the actor name.
- Query
- Go
- Java
- Python
- JavaScript (gRPC)
- JavaScript (HTTP)
- Curl
{
HP(func: allofterms(name@en, "Harry Potter")) @cascade {
name@en
starring{
performance.character {
name@en
}
performance.actor @filter(allofterms(name@en, "Warwick")){
name@en
}
}
}
}
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 := `{
HP(func: allofterms(name@en, "Harry Potter")) @cascade {
name@en
starring{
performance.character {
name@en
}
performance.actor @filter(allofterms(name@en, "Warwick")){
name@en
}
}
}
}`
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 = "{
HP(func: allofterms(name@en, \"Harry Potter\")) @cascade {
name@en
starring{
performance.character {
name@en
}
performance.actor @filter(allofterms(name@en, \"Warwick\")){
name@en
}
}
}
}";
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 = """{
HP(func: allofterms(name@en, "Harry Potter")) @cascade {
name@en
starring{
performance.character {
name@en
}
performance.actor @filter(allofterms(name@en, "Warwick")){
name@en
}
}
}
}"""
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 = `{
HP(func: allofterms(name@en, "Harry Potter")) @cascade {
name@en
starring{
performance.character {
name@en
}
performance.actor @filter(allofterms(name@en, "Warwick")){
name@en
}
}
}
}`;
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 = `{
HP(func: allofterms(name@en, "Harry Potter")) @cascade {
name@en
starring{
performance.character {
name@en
}
performance.actor @filter(allofterms(name@en, "Warwick")){
name@en
}
}
}
}`;
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 '{
HP(func: allofterms(name@en, "Harry Potter")) @cascade {
name@en
starring{
performance.character {
name@en
}
performance.actor @filter(allofterms(name@en, "Warwick")){
name@en
}
}
}
}'
You can apply @cascade on inner query blocks as well.
- Query
- Go
- Java
- Python
- JavaScript (gRPC)
- JavaScript (HTTP)
- Curl
{
HP(func: allofterms(name@en, "Harry Potter")) {
name@en
genre {
name@en
}
starring @cascade {
performance.character {
name@en
}
performance.actor @filter(allofterms(name@en, "Warwick")){
name@en
}
}
}
}
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 := `{
HP(func: allofterms(name@en, "Harry Potter")) {
name@en
genre {
name@en
}
starring @cascade {
performance.character {
name@en
}
performance.actor @filter(allofterms(name@en, "Warwick")){
name@en
}
}
}
}`
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 = "{
HP(func: allofterms(name@en, \"Harry Potter\")) {
name@en
genre {
name@en
}
starring @cascade {
performance.character {
name@en
}
performance.actor @filter(allofterms(name@en, \"Warwick\")){
name@en
}
}
}
}";
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 = """{
HP(func: allofterms(name@en, "Harry Potter")) {
name@en
genre {
name@en
}
starring @cascade {
performance.character {
name@en
}
performance.actor @filter(allofterms(name@en, "Warwick")){
name@en
}
}
}
}"""
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 = `{
HP(func: allofterms(name@en, "Harry Potter")) {
name@en
genre {
name@en
}
starring @cascade {
performance.character {
name@en
}
performance.actor @filter(allofterms(name@en, "Warwick")){
name@en
}
}
}
}`;
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 = `{
HP(func: allofterms(name@en, "Harry Potter")) {
name@en
genre {
name@en
}
starring @cascade {
performance.character {
name@en
}
performance.actor @filter(allofterms(name@en, "Warwick")){
name@en
}
}
}
}`;
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 '{
HP(func: allofterms(name@en, "Harry Potter")) {
name@en
genre {
name@en
}
starring @cascade {
performance.character {
name@en
}
performance.actor @filter(allofterms(name@en, "Warwick")){
name@en
}
}
}
}'
Parameterized @cascade
The @cascade directive can optionally take a list of fields as an argument.
This changes the default behavior, considering only the supplied fields as mandatory instead of all the fields for a type.
Listed fields are automatically cascaded as a required argument to nested selection sets.
A parameterized cascade works on levels (e.g. on the root function or on lower levels), so
you need to specify @cascade(param) on the exact level where you want it to be applied.
The rule with @cascade(predicate) is that the predicate needs to be in the query at the same level @cascade is.
Take the following query as an example:
- Query
- Go
- Java
- Python
- JavaScript (gRPC)
- JavaScript (HTTP)
- Curl
{
nodes(func: allofterms(name@en, "jones indiana")) {
name@en
genre @filter(anyofterms(name@en, "action adventure")) {
name@en
}
produced_by {
name@en
}
}
}
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 := `{
nodes(func: allofterms(name@en, "jones indiana")) {
name@en
genre @filter(anyofterms(name@en, "action adventure")) {
name@en
}
produced_by {
name@en
}
}
}`
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 = "{
nodes(func: allofterms(name@en, \"jones indiana\")) {
name@en
genre @filter(anyofterms(name@en, \"action adventure\")) {
name@en
}
produced_by {
name@en
}
}
}";
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 = """{
nodes(func: allofterms(name@en, "jones indiana")) {
name@en
genre @filter(anyofterms(name@en, "action adventure")) {
name@en
}
produced_by {
name@en
}
}
}"""
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 = `{
nodes(func: allofterms(name@en, "jones indiana")) {
name@en
genre @filter(anyofterms(name@en, "action adventure")) {
name@en
}
produced_by {
name@en
}
}
}`;
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 = `{
nodes(func: allofterms(name@en, "jones indiana")) {
name@en
genre @filter(anyofterms(name@en, "action adventure")) {
name@en
}
produced_by {
name@en
}
}
}`;
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 '{
nodes(func: allofterms(name@en, "jones indiana")) {
name@en
genre @filter(anyofterms(name@en, "action adventure")) {
name@en
}
produced_by {
name@en
}
}
}'
This query gets nodes that have all the terms "jones indiana" and then traverses to genre and produced_by.
It also adds an additional filter for genre, to only get the ones that either have "action" or "adventure" in the name.
The results include nodes that have no genre and nodes that have no genre and no producer.
If you apply a regular @cascade without a parameter, you'll lose the ones that had genre but no producer.
To get the nodes that have the traversed genre but possibly not produced_by, you can parameterize the cascade:
- Query
- Go
- Java
- Python
- JavaScript (gRPC)
- JavaScript (HTTP)
- Curl
{
nodes(func: allofterms(name@en, "jones indiana")) @cascade(genre) {
name@en
genre @filter(anyofterms(name@en, "action adventure")) {
name@en
}
produced_by {
name@en
}
written_by {
name@en
}
}
}
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 := `{
nodes(func: allofterms(name@en, "jones indiana")) @cascade(genre) {
name@en
genre @filter(anyofterms(name@en, "action adventure")) {
name@en
}
produced_by {
name@en
}
written_by {
name@en
}
}
}`
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 = "{
nodes(func: allofterms(name@en, \"jones indiana\")) @cascade(genre) {
name@en
genre @filter(anyofterms(name@en, \"action adventure\")) {
name@en
}
produced_by {
name@en
}
written_by {
name@en
}
}
}";
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 = """{
nodes(func: allofterms(name@en, "jones indiana")) @cascade(genre) {
name@en
genre @filter(anyofterms(name@en, "action adventure")) {
name@en
}
produced_by {
name@en
}
written_by {
name@en
}
}
}"""
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 = `{
nodes(func: allofterms(name@en, "jones indiana")) @cascade(genre) {
name@en
genre @filter(anyofterms(name@en, "action adventure")) {
name@en
}
produced_by {
name@en
}
written_by {
name@en
}
}
}`;
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 = `{
nodes(func: allofterms(name@en, "jones indiana")) @cascade(genre) {
name@en
genre @filter(anyofterms(name@en, "action adventure")) {
name@en
}
produced_by {
name@en
}
written_by {
name@en
}
}
}`;
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 '{
nodes(func: allofterms(name@en, "jones indiana")) @cascade(genre) {
name@en
genre @filter(anyofterms(name@en, "action adventure")) {
name@en
}
produced_by {
name@en
}
written_by {
name@en
}
}
}'
If you want to check for multiple fields, just comma separate them. For example, to cascade over produced_by and written_by:
- Query
- Go
- Java
- Python
- JavaScript (gRPC)
- JavaScript (HTTP)
- Curl
{
nodes(func: allofterms(name@en, "jones indiana")) @cascade(produced_by,written_by) {
name@en
genre @filter(anyofterms(name@en, "action adventure")) {
name@en
}
produced_by {
name@en
}
written_by {
name@en
}
}
}
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 := `{
nodes(func: allofterms(name@en, "jones indiana")) @cascade(produced_by,written_by) {
name@en
genre @filter(anyofterms(name@en, "action adventure")) {
name@en
}
produced_by {
name@en
}
written_by {
name@en
}
}
}`
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 = "{
nodes(func: allofterms(name@en, \"jones indiana\")) @cascade(produced_by,written_by) {
name@en
genre @filter(anyofterms(name@en, \"action adventure\")) {
name@en
}
produced_by {
name@en
}
written_by {
name@en
}
}
}";
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 = """{
nodes(func: allofterms(name@en, "jones indiana")) @cascade(produced_by,written_by) {
name@en
genre @filter(anyofterms(name@en, "action adventure")) {
name@en
}
produced_by {
name@en
}
written_by {
name@en
}
}
}"""
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 = `{
nodes(func: allofterms(name@en, "jones indiana")) @cascade(produced_by,written_by) {
name@en
genre @filter(anyofterms(name@en, "action adventure")) {
name@en
}
produced_by {
name@en
}
written_by {
name@en
}
}
}`;
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 = `{
nodes(func: allofterms(name@en, "jones indiana")) @cascade(produced_by,written_by) {
name@en
genre @filter(anyofterms(name@en, "action adventure")) {
name@en
}
produced_by {
name@en
}
written_by {
name@en
}
}
}`;
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 '{
nodes(func: allofterms(name@en, "jones indiana")) @cascade(produced_by,written_by) {
name@en
genre @filter(anyofterms(name@en, "action adventure")) {
name@en
}
produced_by {
name@en
}
written_by {
name@en
}
}
}'
Nesting and parameterized cascade
The cascading nature of field selection is overwritten by a nested @cascade.
The previous example can be cascaded down the chain as well, and be overridden on each level as needed.
For example, if you only want the "Indiana Jones movies that were produced by the same person who produced a Jurassic World movie":
- Query
- Go
- Java
- Python
- JavaScript (gRPC)
- JavaScript (HTTP)
- Curl
{
nodes(func: allofterms(name@en, "jones indiana")) @cascade(produced_by) {
name@en
genre @filter(anyofterms(name@en, "action adventure")) {
name@en
}
produced_by @cascade(producer.film) {
name@en
producer.film @filter(allofterms(name@en, "jurassic world")) {
name@en
}
}
written_by {
name@en
}
}
}
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 := `{
nodes(func: allofterms(name@en, "jones indiana")) @cascade(produced_by) {
name@en
genre @filter(anyofterms(name@en, "action adventure")) {
name@en
}
produced_by @cascade(producer.film) {
name@en
producer.film @filter(allofterms(name@en, "jurassic world")) {
name@en
}
}
written_by {
name@en
}
}
}`
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 = "{
nodes(func: allofterms(name@en, \"jones indiana\")) @cascade(produced_by) {
name@en
genre @filter(anyofterms(name@en, \"action adventure\")) {
name@en
}
produced_by @cascade(producer.film) {
name@en
producer.film @filter(allofterms(name@en, \"jurassic world\")) {
name@en
}
}
written_by {
name@en
}
}
}";
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 = """{
nodes(func: allofterms(name@en, "jones indiana")) @cascade(produced_by) {
name@en
genre @filter(anyofterms(name@en, "action adventure")) {
name@en
}
produced_by @cascade(producer.film) {
name@en
producer.film @filter(allofterms(name@en, "jurassic world")) {
name@en
}
}
written_by {
name@en
}
}
}"""
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 = `{
nodes(func: allofterms(name@en, "jones indiana")) @cascade(produced_by) {
name@en
genre @filter(anyofterms(name@en, "action adventure")) {
name@en
}
produced_by @cascade(producer.film) {
name@en
producer.film @filter(allofterms(name@en, "jurassic world")) {
name@en
}
}
written_by {
name@en
}
}
}`;
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 = `{
nodes(func: allofterms(name@en, "jones indiana")) @cascade(produced_by) {
name@en
genre @filter(anyofterms(name@en, "action adventure")) {
name@en
}
produced_by @cascade(producer.film) {
name@en
producer.film @filter(allofterms(name@en, "jurassic world")) {
name@en
}
}
written_by {
name@en
}
}
}`;
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 '{
nodes(func: allofterms(name@en, "jones indiana")) @cascade(produced_by) {
name@en
genre @filter(anyofterms(name@en, "action adventure")) {
name@en
}
produced_by @cascade(producer.film) {
name@en
producer.film @filter(allofterms(name@en, "jurassic world")) {
name@en
}
}
written_by {
name@en
}
}
}'
Another nested example: "Find the Indiana Jones movie that was written by the same person who wrote a Star Wars movie and was produced by the same person who produced Jurassic World":
- Query
- Go
- Java
- Python
- JavaScript (gRPC)
- JavaScript (HTTP)
- Curl
{
nodes(func: allofterms(name@en, "jones indiana")) @cascade(produced_by,written_by) {
name@en
genre @filter(anyofterms(name@en, "action adventure")) {
name@en
}
produced_by @cascade(producer.film) {
name@en
producer.film @filter(allofterms(name@en, "jurassic world")) {
name@en
}
}
written_by @cascade(writer.film) {
name@en
writer.film @filter(allofterms(name@en, "star wars")) {
name@en
}
}
}
}
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 := `{
nodes(func: allofterms(name@en, "jones indiana")) @cascade(produced_by,written_by) {
name@en
genre @filter(anyofterms(name@en, "action adventure")) {
name@en
}
produced_by @cascade(producer.film) {
name@en
producer.film @filter(allofterms(name@en, "jurassic world")) {
name@en
}
}
written_by @cascade(writer.film) {
name@en
writer.film @filter(allofterms(name@en, "star wars")) {
name@en
}
}
}
}`
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 = "{
nodes(func: allofterms(name@en, \"jones indiana\")) @cascade(produced_by,written_by) {
name@en
genre @filter(anyofterms(name@en, \"action adventure\")) {
name@en
}
produced_by @cascade(producer.film) {
name@en
producer.film @filter(allofterms(name@en, \"jurassic world\")) {
name@en
}
}
written_by @cascade(writer.film) {
name@en
writer.film @filter(allofterms(name@en, \"star wars\")) {
name@en
}
}
}
}";
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 = """{
nodes(func: allofterms(name@en, "jones indiana")) @cascade(produced_by,written_by) {
name@en
genre @filter(anyofterms(name@en, "action adventure")) {
name@en
}
produced_by @cascade(producer.film) {
name@en
producer.film @filter(allofterms(name@en, "jurassic world")) {
name@en
}
}
written_by @cascade(writer.film) {
name@en
writer.film @filter(allofterms(name@en, "star wars")) {
name@en
}
}
}
}"""
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 = `{
nodes(func: allofterms(name@en, "jones indiana")) @cascade(produced_by,written_by) {
name@en
genre @filter(anyofterms(name@en, "action adventure")) {
name@en
}
produced_by @cascade(producer.film) {
name@en
producer.film @filter(allofterms(name@en, "jurassic world")) {
name@en
}
}
written_by @cascade(writer.film) {
name@en
writer.film @filter(allofterms(name@en, "star wars")) {
name@en
}
}
}
}`;
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 = `{
nodes(func: allofterms(name@en, "jones indiana")) @cascade(produced_by,written_by) {
name@en
genre @filter(anyofterms(name@en, "action adventure")) {
name@en
}
produced_by @cascade(producer.film) {
name@en
producer.film @filter(allofterms(name@en, "jurassic world")) {
name@en
}
}
written_by @cascade(writer.film) {
name@en
writer.film @filter(allofterms(name@en, "star wars")) {
name@en
}
}
}
}`;
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 '{
nodes(func: allofterms(name@en, "jones indiana")) @cascade(produced_by,written_by) {
name@en
genre @filter(anyofterms(name@en, "action adventure")) {
name@en
}
produced_by @cascade(producer.film) {
name@en
producer.film @filter(allofterms(name@en, "jurassic world")) {
name@en
}
}
written_by @cascade(writer.film) {
name@en
writer.film @filter(allofterms(name@en, "star wars")) {
name@en
}
}
}
}'
Cascade Performance
The @cascade directive processes the nodes after the query, but before Dgraph
returns query results. This means that all of the nodes that would normally be
returned if there was no @cascade applied are still touched in the internal
query process. If you see slower-than-expected performance when using the
@cascade directive, it is probably because the internal query process returns
a large set of nodes but the cascade reduces those to a small set of nodes in query
results. To improve the performance of queries that use the @cascade directive,
you might want to use var blocks or has filters, as described below.
Cascade with var blocks
The performance impact of using var blocks is that it reduces the graph that is touched to generate the final query results.
For example, many of the previous examples could be replaced entirely using var blocks instead of utilizing @cascade.
The following query provides an alternative way to structure the query shown above,
"Find the Indiana Jones movie that was written by the same person who wrote a
Star Wars movie and was produced by the same person who produced Jurassic World",
without using the @cascade directive:
- Query
- Go
- Java
- Python
- JavaScript (gRPC)
- JavaScript (HTTP)
- Curl
{
var(func: allofterms(name@en, "jurassic world")) {
produced_by {
ProducedBy as producer.film
}
}
var(func: allofterms(name@en, "star wars")) {
written_by {
WrittenBy as writer.film
}
}
nodes(func: allofterms(name@en,"indiana jones")) @filter(uid(ProducedBy) AND uid(WrittenBy)) {
name@en
genre {
name@en
}
}
}
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 := `{
var(func: allofterms(name@en, "jurassic world")) {
produced_by {
ProducedBy as producer.film
}
}
var(func: allofterms(name@en, "star wars")) {
written_by {
WrittenBy as writer.film
}
}
nodes(func: allofterms(name@en,"indiana jones")) @filter(uid(ProducedBy) AND uid(WrittenBy)) {
name@en
genre {
name@en
}
}
}`
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 = "{
var(func: allofterms(name@en, \"jurassic world\")) {
produced_by {
ProducedBy as producer.film
}
}
var(func: allofterms(name@en, \"star wars\")) {
written_by {
WrittenBy as writer.film
}
}
nodes(func: allofterms(name@en,\"indiana jones\")) @filter(uid(ProducedBy) AND uid(WrittenBy)) {
name@en
genre {
name@en
}
}
}";
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 = """{
var(func: allofterms(name@en, "jurassic world")) {
produced_by {
ProducedBy as producer.film
}
}
var(func: allofterms(name@en, "star wars")) {
written_by {
WrittenBy as writer.film
}
}
nodes(func: allofterms(name@en,"indiana jones")) @filter(uid(ProducedBy) AND uid(WrittenBy)) {
name@en
genre {
name@en
}
}
}"""
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 = `{
var(func: allofterms(name@en, "jurassic world")) {
produced_by {
ProducedBy as producer.film
}
}
var(func: allofterms(name@en, "star wars")) {
written_by {
WrittenBy as writer.film
}
}
nodes(func: allofterms(name@en,"indiana jones")) @filter(uid(ProducedBy) AND uid(WrittenBy)) {
name@en
genre {
name@en
}
}
}`;
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 = `{
var(func: allofterms(name@en, "jurassic world")) {
produced_by {
ProducedBy as producer.film
}
}
var(func: allofterms(name@en, "star wars")) {
written_by {
WrittenBy as writer.film
}
}
nodes(func: allofterms(name@en,"indiana jones")) @filter(uid(ProducedBy) AND uid(WrittenBy)) {
name@en
genre {
name@en
}
}
}`;
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 '{
var(func: allofterms(name@en, "jurassic world")) {
produced_by {
ProducedBy as producer.film
}
}
var(func: allofterms(name@en, "star wars")) {
written_by {
WrittenBy as writer.film
}
}
nodes(func: allofterms(name@en,"indiana jones")) @filter(uid(ProducedBy) AND uid(WrittenBy)) {
name@en
genre {
name@en
}
}
}'
The performance impact of building queries with multiple var blocks versus
using @cascade depends on the nodes touched to reach the end results. Depending
on the size of your data set and distribution between nodes, refactoring a query
with var blocks instead of @cascade might actually decrease performance
if the query must touch more nodes as a result of the refactor.
Cascade with has filter
In cases where only a small set of nodes have the predicates where @cascade is
applied, it might be beneficial to query performance to include a has filter
for those predicates.
For example, you could run a query like "Find movies that have a sequel whose name contains the term Star Wars" as follows:
- Query
- Go
- Java
- Python
- JavaScript (gRPC)
- JavaScript (HTTP)
- Curl
{
nodes(func: has(sequel)) @filter(type(Film)) @cascade {
count(uid)
name@en
sequel @filter(allofterms(name@en,"Star Wars")) {
name@en
}
}
}
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 := `{
nodes(func: has(sequel)) @filter(type(Film)) @cascade {
count(uid)
name@en
sequel @filter(allofterms(name@en,"Star Wars")) {
name@en
}
}
}`
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 = "{
nodes(func: has(sequel)) @filter(type(Film)) @cascade {
count(uid)
name@en
sequel @filter(allofterms(name@en,\"Star Wars\")) {
name@en
}
}
}";
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 = """{
nodes(func: has(sequel)) @filter(type(Film)) @cascade {
count(uid)
name@en
sequel @filter(allofterms(name@en,"Star Wars")) {
name@en
}
}
}"""
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 = `{
nodes(func: has(sequel)) @filter(type(Film)) @cascade {
count(uid)
name@en
sequel @filter(allofterms(name@en,"Star Wars")) {
name@en
}
}
}`;
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 = `{
nodes(func: has(sequel)) @filter(type(Film)) @cascade {
count(uid)
name@en
sequel @filter(allofterms(name@en,"Star Wars")) {
name@en
}
}
}`;
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 '{
nodes(func: has(sequel)) @filter(type(Film)) @cascade {
count(uid)
name@en
sequel @filter(allofterms(name@en,"Star Wars")) {
name@en
}
}
}'
By using a has filter in the root function instead of type(Movie), you can
reduce the root graph from 275,195 nodes down to 7,747 nodes. Reducing the
root graph before the post-query cascade process results in a higher-performing
query.