Links define a specific relationship between two object types. Links have a direction, but can be traversed in both ways forward and backward.
To define a relationship we use the link keyword:
type Movie {
required property title -> str;
required link director -> Person;
multi link actors -> Person;
}
type Person {
required property first_name -> str;
required property last_name -> str;
}Links allow fetching relationships with a single query:
tutorial> ......... ......... .........
select Movie {
director: { first_name },
actors: { first_name },
};{
Object {
director: Object { first_name: 'Denis' },
actors: {
Object { first_name: 'Harrison' },
Object { first_name: 'Ryan' },
Object { first_name: 'Ana' },
}
}
}Similarly you can run some aggregates on the nested sets:
tutorial>
SELECT Movie { title, actors_number:=count(.actors) };{Object { title: 'Blade Runner 2049', actors_number: 3 }}You can find more information on aggregates in the Cookbook and the reference of set functions (or just search for “aggregate”).
In the movie example above, we have only shown a forward link traversal:
tutorial>
SELECT Movie { title, actors: { first_name } };{
Object {
title: 'Blade Runner 2049',
actors: {
Object { first_name: 'Harrison' },
Object { first_name: 'Ryan' },
Object { first_name: 'Ana' },
}
}
}Here is another example of using a forward link. This time we only return
last names of the artists as plain string (not an object). In this case, we
need to alias the field with :=:
tutorial> ......... ......... .........
SELECT Movie {
title,
starring := Movie.actors.last_name,
};{Object {
title: 'Blade Runner 2049',
starring: {
'Ford',
'Gosling',
'de Armas',
}
}}To find all movies that a person is starred in we use a backward link
traversal .< operator:
tutorial> ......... ......... .........
SELECT Person {
first_name,
movies := Person.<actors[IS Movie].title,
} FILTER .first_name = 'Ryan';{Object {
first_name: 'Ryan',
movies: {
'Blade Runner 2049',
}
}}You might also note that we’ve added [IS Movie], which we call
type intersection operator. This is how backward link
traversal works: EdgeDB fetches every object in the entire database having the
field actors which is a Person. So we narrow down the set of objects to
Movie and select a title from it.
All other tools work on backward link:
tutorial> ......... ......... .........
SELECT Person {
first_name,
movies := Person.<actors[IS Movie] { title, year }
} FILTER .first_name = 'Ryan';{Object {
first_name: 'Ryan',
movies: {
Object { title: 'Blade Runner 2049', year: 2017 },
}
}}Or more complex example:
tutorial> ......... ......... ......... ......... .........
SELECT Person {
first_name,
colleagues := Person.<actors[IS Movie].actors {
first_name
}
} FILTER .first_name = 'Ryan';{
Object {
first_name: 'Ryan',
colleagues: {
Object { first_name: 'Ana' },
Object { first_name: 'Harrison' },
Object { first_name: 'Ryan' },
}
}
}Now you may notice that Ryan Gosling is mentioned as a colleague of himself. To fix it we can add a filter:
tutorial> ......... ......... ......... ......... ......... .........
SELECT Person {
first_name,
colleagues := (
SELECT Person.<actors[IS Movie].actors { first_name }
FILTER Person.<actors[IS Movie].actors != Person
),
} FILTER .first_name = 'Ryan';Note: we wrapped a backward link access by SELECT subquery to add a filter.
The last query can be rewritten in a nicer way using an alias:
tutorial> ......... ......... ......... ......... ......... ......... .........
SELECT Person {
first_name,
colleagues := (
WITH Peer := Person.<actors[IS Movie].actors
SELECT Peer { first_name }
FILTER Peer != Person
),
} FILTER .first_name = 'Ryan';{Object {
first_name: 'Ryan',
colleagues: {
Object { first_name: 'Ana' },
Object { first_name: 'Harrison' },
}
}}Note also how elegantly use Peer != Person instead of
Peer.id != Person.id to compare object identity.