We’re overhauling Dgraph’s docs to make them clearer and more approachable. If you notice any issues during this transition or have suggestions, please let us know.

Query variables

Syntax Examples:

  • varName as q(func: ...) { ... }
  • varName as var(func: ...) { ... }
  • varName as predicate { ... }
  • varName as predicate @filter(...) { ... }

Types : uid

Nodes (UIDs) matched at one place in a query can be stored in a variable and used elsewhere. Query variables can be used in other query blocks or in a child node of the defining block.

Query variables do not affect the semantics of the query at the point of definition. Query variables are evaluated to all nodes matched by the defining block.

In general, query blocks are executed in parallel, but variables impose an evaluation order on some blocks. Cycles induced by variable dependence are not permitted.

If a variable is defined, it must be used elsewhere in the query.

A query variable is used by extracting the UIDs in it with uid(var-name).

The syntax func: uid(A,B) or @filter(uid(A,B)) means the union of UIDs for variables A and B.

Query Example: the movies of Angelia Jolie and Brad Pitt where both have acted on movies in the same genre. Note that B and D match all genres for all movies, not genres per movie.

{
  var(func:allofterms(name@en, "angelina jolie")) {
    actor.film {
        A AS performance.film # All films acted in by Angelina Jolie
        B As genre # Genres of all the films acted in by Angelina Jolie
    }
  }

  var(func:allofterms(name@en, "brad pitt")) {
    actor.film {
        C AS performance.film # All films acted in by Brad Pitt
        D as genre # Genres of all the films acted in by Brad Pitt
    }
  }

  films(func: uid(D)) @filter(uid(B)) { # Genres from both Angelina and Brad
    name@en ~genre @filter(uid(A, C)) { # Movies in either A or C. name@en }
  }
}

Value variables

Syntax Examples:

  • varName as scalarPredicate
  • varName as count(predicate)
  • varName as avg(...)
  • varName as math(...)

Types : int, float, String, dateTime, default, geo, bool

Value variables store scalar values. Value variables are a map from the UIDs of the enclosing block to the corresponding values.

It therefore only makes sense to use the values from a value variable in a context that matches the same UIDs - if used in a block matching different UIDs the value variable is undefined.

It is an error to define a value variable but not use it elsewhere in the query.

Value variables are used by extracting the values with val(var-name), or by extracting the UIDs with uid(var-name).

Facet values can be stored in value variables.

Query Example: the number of movie roles played by the actors of the 80’s classic “The Princess Bride”. Query variable pbActors matches the UIDs of all actors from the movie. Value variable roles is thus a map from actor UID to number of roles. Value variable roles can be used in the totalRoles query block because that query block also matches the pbActors UIDs, so the actor to number of roles map is available.

{
  var(func: allofterms(name@en, "The Princess Bride")) {
    starring {
      pbActors as performance.actor {
        roles as count(actor.film)
      }
    }
  }
  totalRoles(func: uid(pbActors), orderasc: val(roles)) {
    name@en
    numRoles : val(roles)
  }
}

Value variables can be used in place of UID variables by extracting the UID list from the map.

Query Example: the same query as the previous example, but using value variable roles for matching UIDs in the totalRoles query block.

{
  var(func: allofterms(name@en, "The Princess Bride")) {
    starring {
      performance.actor {
        roles as count(actor.film)
      }
    }
  }

  totalRoles(func: uid(roles), orderasc: val(roles)) {
    name@en
    numRoles : val(roles)
  }
}

## Variable propagation

Like query variables, value variables can be used in other query blocks and in
blocks nested within the defining block. When used in a block nested within the
block that defines the variable, the value is computed as a sum of the variable
for parent nodes along all paths to the point of use. This is called variable
propagation.

For example:

```json
{
  q(func: uid(0x01)) {
    myscore as math(1) # A
    friends { # B
      ...myscore...
    }
  }
}

At line A, a value variable myscore is defined as mapping node with UID 0x01 to value 1. At B, the value for each friend is still 1: there is only one path to each friend. Traversing the friend edge twice reaches the friends of friends. The variable myscore gets propagated such that each friend of friend receives the sum of its parents values: if a friend of a friend is reachable from only one friend, the value is still 1, if they’re reachable from two friends, the value is two and so on. That is, the value of myscore for each friend of friends inside the block marked C will be the number of paths to them.

The value that a node receives for a propagated variable is the sum of the values of all its parent nodes.

This propagation is useful, for example, in normalizing a sum across users, finding the number of paths between nodes and accumulating a sum through a graph.

Query Example: for each Harry Potter movie, the number of roles played by actor Warwick Davis.

{
  num_roles(func: eq(name@en, "Warwick Davis")) @cascade @normalize {
    paths as math(1)  # records number of paths to each character
    paths as math(1)  # records number of paths to each character

    actor : name@en

    actor.film {
      performance.film @filter(allofterms(name@en, "Harry Potter")) {
        film_name : name@en
        characters : math(paths)  # how many paths (i.e. characters) reach this film
      }
    }

}

Query Example: each actor who has been in a Peter Jackson movie and the fraction of Peter Jackson movies they have appeared in.

{
  movie_fraction(func:eq(name@en, "Peter Jackson")) @normalize {

    paths as math(1)
    total_films : num_films as count(director.film)
    director : name@en

    director.film {
      starring {
        performance.actor {
          fraction : math(paths / (num_films/paths))
          actor : name@en
        }
      }
    }

}

More examples can be found in two Dgraph blog posts about using variable propagation for recommendation engines (post 1, post 2).

Math on value variables

Value variables can be combined using mathematical functions. For example, this could be used to associate a score which is then used to order or perform other operations, such as might be used in building news feeds, simple recommendation systems, and so on.

Math statements must be enclosed within math( <exp> ) and must be stored to a value variable.

The supported operators are as follows:

OperatorsTypes acceptedWhat it does
+ - * / %int, floatperforms the corresponding operation
min maxAll types except geo, bool (binary functions)selects the min/max value among the two
< > <= >= == !=All types except geo, boolReturns true or false based on the values
floor ceil ln exp sqrtint, float (unary function)performs the corresponding operation
sincedateTimeReturns the number of seconds in float from the time specified
pow(a, b)int, floatReturns a to the power b
logbase(a,b)int, floatReturns log(a) to the base b
cond(a, b, c)first operand must be a Booleanselects b if a is true else c

If an integer overflow occurs, or an operand is passed to a math operation (such as ln, logbase, sqrt, pow) which results in an illegal operation, Dgraph will return an error.

Query Example: Form a score for each of Steven Spielberg’s movies as the sum of number of actors, number of genres and number of countries. List the top five such movies in order of decreasing score.

{
  var(func:allofterms(name@en, "steven spielberg")) {
    films as director.film {
        p as count(starring)
        q as count(genre)
        r as count(country)
        score as math(p + q + r)
    }
  }

  TopMovies(func: uid(films), orderdesc: val(score), first: 5){
    name@en
    val(score)
  }
}

Value variables and aggregations of them can be used in filters.

Query Example: Calculate a score for each Steven Spielberg movie with a condition on release date to penalize movies that are more than 20 years old, filtering on the resulting score.

{
  var(func:allofterms(name@en, "steven spielberg")) {
    films as director.film {
        p as count(starring)
        q as count(genre)
        date as initial_release_date
        years as math(since(date)/(365*24*60\*60))
        score as math(cond(years > 20, 0, ln(p)+q-ln(years)))
    }
  }

  TopMovies(func: uid(films), orderdesc: val(score)) @filter(gt(val(score), 2)){
    name@en val(score) val(date)
  }
}

Values calculated with math operations are stored to value variables and so can be aggregated.

Query Example: Compute a score for each Steven Spielberg movie and then aggregate the score.

{
  steven as var(func:eq(name@en, "Steven Spielberg")) @filter(has(director.film)) {
    director.film { p as count(starring) q as count(genre) r as count(country)
      score as math(p + q + r)
    }
    directorScore as sum(val(score))
  }
  score(func: uid(steven)) { name@en val(directorScore) }
}