Light
Dark
System
v2latest
v3dev
v2latest
v1

Links

Links define a specific relationship between two object types.

You can add an exclusive constraint to a link to guarantee that no other instances can link to the same target(s).

Copy
type Person {
  property name -> str;
}

type GroupChat {
  required multi link members -> Person {
    constraint exclusive;
  }
}

In the GroupChat example, the GroupChat.members link is now exclusive. Two GroupChat objects cannot link to the same Person; put differently, no Person can be a member of multiple GroupChat.

By combinining link cardinality and exclusivity constraints, we can model every kind of relationship: one-to-one, one-to-many, many-to-one, and many-to-many.

Relation type

Cardinality

Exclusive

One-to-one

single

Yes

One-to-many

multi

Yes

Many-to-one

single

No

Many-to-many

multi

No

Many-to-one relationships typically represent concepts like ownership, membership, or hierarchies. For example, Person and Shirt. One person may own many shirts, and a shirt is (usually) owned by just one person.

Copy
type Person {
  required property name -> str
}

type Shirt {
  required property color -> str;
  link owner -> Person;
}

Since links are single by default, each Shirt only corresponds to one Person. In the absence of any exclusivity constraints, multiple shirts can link to the same Person. Thus, we have a one-to-many relationship between Person and Shirt.

When fetching a Person, it’s possible to deeply fetch their collection of Shirts by traversing the Shirt.owner link in reverse. This is known as a backlink; read the select docs to learn more.

Conceptually, one-to-many and many-to-one relationships are identical; the “directionality” of a relation is just a matter of perspective. Here, the same “shirt owner” relationship is represented with a multi link.

Copy
type Person {
  required property name -> str;
  multi link shirts -> Shirt {
    # ensures a one-to-many relationship
    constraint exclusive;
  }
}

type Shirt {
  required property color -> str;
}

Don’t forget the exclusive constraint! This is required to ensure that each Shirt corresponds to a single Person. Without it, the relationship will be many-to-many.

Under the hood, a multi link is stored in an intermediate association table, whereas a single link is stored as a column in the object type where it is declared. As a result, single links are marginally more efficient. Generally single links are recommended when modeling 1:N relations.

Under a one-to-one relationship, the source object links to a single instance of the target type, and vice versa. As an example consider a schema to represent assigned parking spaces.

Copy
type Employee {
  required property name -> str;
  link assigned_space -> ParkingSpace {
    constraint exclusive;
  }
}

type ParkingSpace {
  required property number -> int64;
}

All links are single unless otherwise specified, so no Employee can have more than one assigned_space. Moreover, the exclusive constraint guarantees that a given ParkingSpace can’t be assigned to multiple employees at once. Together the single link and exclusivity constraint constitute a one-to-one relationship.

A many-to-many relation is the least constrained kind of relationship. There is no exclusivity or cardinality constraints in either direction. As an example consider a simple app where a User can “like” their favorite Movies.

Copy
type User {
  required property name -> str;
  multi link likes -> Movie;
}
type Movie {
  required property title -> str;
}

A user can like multiple movies. And in the absence of an exclusive constraint, each movie can be liked by multiple users. Thus this is a many-to-many relationship.

Like properties, links can declare a default value in the form of an EdgeQL expression, which will be executed upon insertion. In the example below, new people are automatically assigned three random friends.

Copy
type Person {
  required property name -> str;
  multi link friends -> Person {
    default := (select Person order by random() limit 3);
  }
}

Links can declare their own deletion policy. There are two kinds of events that might trigger these policies: target deletion and source deletion.

Target deletion policies determine what action should be taken when the target of a given link is deleted. They are declared with the on target delete clause.

Copy
type MessageThread {
  property title -> str;
}

type Message {
  property content -> str;
  link chat -> MessageThread {
    on target delete delete source;
  }
}

The Message.chat link in the example uses the delete source policy. There are 4 available target deletion policies.

  • restrict (default) - Any attempt to delete the target object immediately raises an exception.

  • delete source - when the target of a link is deleted, the source is also deleted. This is useful for implementing cascading deletes.

    There is a limit to the depth of a deletion cascade due to an upstream stack size limitation.

  • allow - the target object is deleted and is removed from the set of the link targets.

  • deferred restrict - any attempt to delete the target object raises an exception at the end of the transaction, unless by that time this object is no longer in the set of link targets.

Source deletion policies determine what action should be taken when the source of a given link is deleted. They are declared with the on source delete clause.

Copy
type MessageThread {
  property title -> str;
  multi link messages -> Message {
    on source delete delete target;
  }
}

type Message {
  property content -> str;
}

Under this policy, deleting a MessageThread will unconditionally delete its messages as well.

To avoid deleting a Message that is linked to by other MessageThread objects via their message link, append if orphan to that link’s deletion policy.

Copy
type MessageThread {
  property title -> str;
  multi link messages -> Message {
    on source delete delete target;
    on source delete delete target if orphan;
  }
}

Deletion policies using if orphan will result in the target being deleted unless it is linked by another object via the same link the policy is on. This qualifier does not apply globally across all links in the database or across different links even if they’re on the same type. For example, a Message might be linked from both a MessageThread and a Channel. If the MessageThread linking to it is deleted, the deletion policy would still result in the Message being deleted as long as no other MessageThread objects link to it on that same field. It is orphaned with respect to the MessageThread type’s link field, even though it is not orphaned globally.

Similarly, if the MessageThread had two links both linking to messages — maybe the existing messages link and another called related to link other related Message objects that are not in the thread — if orphan could result in linked messages being deleted even if they were also linked from another MessageThread object’s related link because they were orphaned with respect to the messages link.

Light
Dark
System