Adding backlinks​

This example shows how to handle a schema that makes use of a backlink. We’ll use a linked-list structure to represent a sequence of events.

We’ll start with this schema:

  1. type Event {
  2. required property name -> str;
  3. link prev -> Event;
  4. # ... more properties and links
  5. }

We specify a prev link because that will make adding a new Event at the end of the chain easier, since we’ll be able to specify the payload and the chain the Event should be appended to in a single insert. Once we’ve updated the schema file we proceed with our first migration:

  1. edgedb migration create
  1. did you create object type 'default::Event'? [y,n,l,c,b,s,q,?]
  2. > y
  3. Created ./dbschema/migrations/00001.edgeql, id:
  4. m1v3ahcx5f43y6mlsdmlz2agnf6msbc7rt3zstiqmezaqx4ev2qovq
  1. edgedb migrate
  1. Applied m1v3ahcx5f43y6mlsdmlz2agnf6msbc7rt3zstiqmezaqx4ev2qovq
  2. (00001.edgeql)

We now have a way of chaining events together. We might create a few events like these:

  1. select Event {
  2. name,
  3. prev: { name },
  4. };
  1. {
  2. default::Event {name: 'setup', prev: {}},
  3. default::Event {name: 'work', prev: default::Event {name: 'setup'}},
  4. default::Event {name: 'cleanup', prev: default::Event {name: 'work'}},
  5. }

It seems like having a next link would be useful, too. So we can define it as a computed link by using backlink notation:

  1. type Event {
  2. required property name -> str;
  3. link prev -> Event;
  4. link next := .<prev[is Event];
  5. }

The migration is straightforward enough:

  1. edgedb migration create
  1. did you create link 'next' of object type 'default::Event'?
  2. [y,n,l,c,b,s,q,?]
  3. > y
  4. Created ./dbschema/migrations/00002.edgeql, id:
  5. m1qpukyvw2m4lmomoseni7vdmevk4wzgsbviojacyrqgiyqjp5sdsa
  1. edgedb migrate
  1. Applied m1qpukyvw2m4lmomoseni7vdmevk4wzgsbviojacyrqgiyqjp5sdsa
  2. (00002.edgeql)

Trying out the new link on our existing data gives us:

  1. select Event {
  2. name,
  3. prev_name := .prev.name,
  4. next_name := .next.name,
  5. };
  1. {
  2. default::Event {
  3. name: 'setup',
  4. prev_name: {},
  5. next_name: {'work'},
  6. },
  7. default::Event {
  8. name: 'work',
  9. prev_name: 'setup',
  10. next_name: {'cleanup'},
  11. },
  12. default::Event {
  13. name: 'cleanup',
  14. prev_name: 'work',
  15. next_name: {},
  16. },
  17. }

That’s not quite right. The value of next_name appears to be a set rather than a singleton. This is because the link prev is many-to-one and so next is one-to-many, making it a multi link. Let’s fix that by making the link prev a one-to-one, after all we’re interested in building event chains, not trees.

  1. type Event {
  2. required property name -> str;
  3. link prev -> Event {
  4. constraint exclusive;
  5. };
  6. link next := .<prev[is Event];
  7. }

Since the next link is computed, the migration should not need any additional user input even though we’re reducing the link’s cardinality:

  1. edgedb migration create
  1. did you create constraint 'std::exclusive' of link 'prev'?
  2. [y,n,l,c,b,s,q,?]
  3. > y
  4. Created ./dbschema/migrations/00003.edgeql, id:
  5. m17or2bfywuckdqeornjmjh7c2voxgatspcewyefcd4p2vbdepimoa
  1. edgedb migrate
  1. Applied m17or2bfywuckdqeornjmjh7c2voxgatspcewyefcd4p2vbdepimoa
  2. (00003.edgeql)

The new next computed link is now inferred as a single link and so the query results for next_name and prev_name are symmetrical:

  1. select Event {
  2. name,
  3. prev_name := .prev.name,
  4. next_name := .next.name,
  5. };
  1. {
  2. default::Event {name: 'setup', prev_name: {}, next_name: 'work'},
  3. default::Event {name: 'work', prev_name: 'setup', next_name: 'cleanup'},
  4. default::Event {name: 'cleanup', prev_name: 'work', next_name: {}},
  5. }