Changing the type of a property

This example shows how to change the type of a property. We’ll use a character in an adventure game as the type of data we will evolve.

Let’s start with this schema:

  1. type Character {
  2. required property name -> str;
  3. required property description -> str;
  4. }

We edit the schema file and perform our first migration:

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

The intent is for the description to provide some text which serves both as something to be shown to the player as well as determining some game actions. Se we end up with something like this:

  1. db>
  1. select Character {name, description};
  1. {
  2. default::Character {name: 'Alice', description: 'Tall and strong'},
  3. default::Character {name: 'Billie', description: 'Smart and aloof'},
  4. default::Character {name: 'Cameron', description: 'Dashing and smooth'},
  5. }

However, as we keep developing our game it becomes apparent that this is less of a “description” and more of a “character class”, so at first we just rename the property to reflect that:

  1. type Character {
  2. required property name -> str;
  3. required property class -> str;
  4. }

The migration gives us this:

  1. $
  1. edgedb migration create
  1. did you rename property 'description' of object type 'default::Character'
  2. to 'class'? [y,n,l,c,b,s,q,?]
  3. > y
  4. Created ./dbschema/migrations/00002.edgeql, id:
  5. m1ljrgrofsqkvo5hsxc62mnztdhlerxp6ucdto262se6dinhuj4mqq
  1. $
  1. edgedb migrate
  1. Applied m1ljrgrofsqkvo5hsxc62mnztdhlerxp6ucdto262se6dinhuj4mqq
  2. (00002.edgeql)

EdgeDB detected that the change looked like a property was being renamed, which we confirmed. Since this was an existing property being renamed, the data is all preserved:

  1. db>
  1. select Character {name, class};
  1. {
  2. default::Character {name: 'Alice', class: 'Tall and strong'},
  3. default::Character {name: 'Billie', class: 'Smart and aloof'},
  4. default::Character {name: 'Cameron', class: 'Dashing and smooth'},
  5. }

The contents of the class property are a bit too verbose, so we decide to update them. In order for this update to be consistently applied across several developers, we will make it in the form of a data migration:

  1. $
  1. edgedb migration create --allow-empty
  1. Created ./dbschema/migrations/00003.edgeql, id:
  2. m1qv2pdksjxxzlnujfed4b6to2ppuodj3xqax4p3r75yfef7kd7jna

Now we can edit the file 00003.edgeql directly:

  1. CREATE MIGRATION m1qv2pdksjxxzlnujfed4b6to2ppuodj3xqax4p3r75yfef7kd7jna
  2. ONTO m1ljrgrofsqkvo5hsxc62mnztdhlerxp6ucdto262se6dinhuj4mqq
  3. {
  4. update default::Character
  5. set {
  6. class :=
  7. 'warrior' if .class = 'Tall and strong' else
  8. 'scholar' if .class = 'Smart and aloof' else
  9. 'rogue'
  10. };
  11. };

We’re ready to apply the migration:

  1. $
  1. edgedb migrate
  1. edgedb error: could not read migrations in ./dbschema/migrations:
  2. could not read migration file ./dbschema/migrations/00003.edgeql:
  3. migration name should be
  4. `m1ryafvp24g5eqjeu65zr4bqf6m3qath3lckfdhoecfncmr7zshehq`
  5. but `m1qv2pdksjxxzlnujfed4b6to2ppuodj3xqax4p3r75yfef7kd7jna` is used
  6. instead.
  7. Migration names are computed from the hash of the migration
  8. contents. To proceed you must fix the statement to read as:
  9. CREATE MIGRATION m1ryafvp24g5eqjeu65zr4bqf6m3qath3lckfdhoecfncmr7zshehq
  10. ONTO ...
  11. if this migration is not applied to any database. Alternatively,
  12. revert the changes to the file.

The migration tool detected that we’ve altered the file and asks us to update the migration name (acting as a checksum) if this was deliberate. This is done as a precaution against accidental changes. Since we’ve done this on purpose, we can update the file and run edgedb migrate again.

As the game becomes more stable there’s no reason for the class to be a str anymore, instead we can use an enum to make sure that we don’t accidentally use some invalid value for it.

  1. scalar type CharacterClass extending enum<warrior, scholar, rogue>;
  2. type Character {
  3. required property name -> str;
  4. required property class -> CharacterClass;
  5. }

Fortunately, we’ve already updated the class strings to match the enum values, so that a simple cast will convert all the values. If we had not done this earlier we would need to do it now in order for the type change to work.

  1. $
  1. edgedb migration create
  1. did you create scalar type 'default::CharacterClass'? [y,n,l,c,b,s,q,?]
  2. > y
  3. did you alter the type of property 'class' of object type
  4. 'default::Character'? [y,n,l,c,b,s,q,?]
  5. > y
  6. Created ./dbschema/migrations/00004.edgeql, id:
  7. m1hc4yynkejef2hh7fvymvg3f26nmynpffksg7yvfksqufif6lulgq
  1. $
  1. edgedb migrate
  1. Applied m1hc4yynkejef2hh7fvymvg3f26nmynpffksg7yvfksqufif6lulgq
  2. (00004.edgeql)

The final migration converted all the class property values:

  1. db>
  1. select Character {name, class};
  1. {
  2. default::Character {name: 'Alice', class: warrior},
  3. default::Character {name: 'Billie', class: scholar},
  4. default::Character {name: 'Cameron', class: rogue},
  5. }