Dgraph GraphQL Tour

Schema

Reverse edges

In Dgraph, edges are directional. A query can’t traverse an edge in reverse. Fundamentally, there are two options for querying in both directions:

  • Add the reverse edge to the schema and then add all the reverse edge data.
  • Tell Dgraph to always store the reverse edge using the @reverse keyword in the DQL schema. (Not available in the GraphQL schema)

In the generated GraphQL API, Dgraph provides support for the first option with easy-to-use directives. Normally a user would have to define two-way edges by creating two edges for each desired bidirectional edge. In the GraphQL schema, a user can alternatively provide the @hasInverse directive and the generated GraphQL API will ensure that all relationships created or removed from within the API are balanced. This directive only needs to be applied to one end of the two-way relationship, but it helps schema readability if this directive is applied on both ends of the relationship. @hasInverse does the same thing whether it is applied on one side, the other side, or both sides of the two-way edge relationship.

You have already seen this directive in action. In the schema provided in the previous lessons, you applied this @hasInverse directive between the following relationships:

  • Person.friends <-> Person.friends
  • Person.ownsPets <-> Animal.owner
  • Person.employers <-> Company.employees
  • Person.manages <-> Person.manager

When applying the @hasInverse directive, it is required to define the field on the opposite side of the edge.

Here is an example of an very simple schema that uses the @hasInverse directive to create a bidirectional edge between Person.employers and Company.employees:

type Company {
  employees: [Person] @hasInverse(field: "employers")
}
type Person {
  employers: [Company]
}

You can define a variety of different types of relationships between types in your schema using bidirectional edges: one-to-one (1:1), one-to-many (1:n), and many-to-many (n:n).

To define a 1:1 relationship, do not include the list brackets on either side of the relationship. This will ensure that one node only ever points to at most one node on the opposite side of the edge.

type Person {
  office: Office
}
type Office {
  person: Person @hasInverse(field: "office")
}

To define a one-to-many (1:n) relationship, wrap the “many” side of the relationship in a list. The following example shows that an Office belongs to one Company, but a single Company can have many offices.

type Company {
  offices: [Office] @hasInverse(field: "company")
}
type Office {
  company: Company
}

To define a many-to-many (n:n) relationship, wrap both sides of the relationship in a list. The following examples shows that a Person has many roles, and a Role can belong to many different people.

type Person {
  roles: [Role]
}
type Role {
  people: [Person] @hasInverse(field: "roles")
}

One-to-one and many-to-many relationships can point to the same edge of another node. You saw an example of this previously in our social graph schema example, where a Person can have multiple friends:

type Person {
  friends: [Person] @hasInverse(field: "friends")
}

You can iterate on your existing schema to add or remove these bidirectional edges. If you remove one edge or side from a two-way relationship, all of the functionality and data will remain for the single-edge relationship.

Even though the GraphQL API will keep edges with @hasInverse directives balanced, it is possible that the edges will fall out-of-sync when you iterate your schema or bypass the GraphQL API to access other Dgraph endpoints. A community member has posted a script that you can run to repair unbalanced inverse relationships.

Something to try: Add a relationship from a Company to their mascot Animal, and add the inverse relationship from the Animal to the companies that it represents.

3.4 Reverse edges