Data model

A data model describes and organizes various data elements within an app and standardizes how various data elements relate to one another.

Syntax overview

Imagine allows you to represent your app's data model using the following structure and syntax:

Model 'model-name' {
'field-name' <field-type> [<field-property> 'field-property-value', <field-property> 'field-property-value']
'field-name' <field-type> [<field-property> 'field-property-value', <field-property> 'field-property-value']
}
Relation <relation-name> {
many 'child-relation-name' from <child-model-name>
one 'parent-relation-name' from <parent-model-name>
}

Example

As an example of a data model, let's take a fictional music app, Musica that has the concepts of a Musician and an Album.

Each Musician has the following fields:

  • first name, which is a string that is less than 100 characters, and we want this field to be the unique identifier of the Musician
  • last name, which is a string that is less than 100 characters
  • age, which is a number between the range 10 and 100

Each Album has the following fields:

  • album name, which is a string that is less than 200 characters, and we want this field to the unique identifier of the Album
  • release date and time, which is the date and time an album is released
  • no. of songs, which is number between 0 and 50

Also, a Musician may have many Albums as a lead vocalist.

In this example, the concepts of Musician and Album, each of their fields, the properties of each of the fields, as well as how Musician and Album relate to each other, all together constitute the Musica's data model.

Representation in Imagine syntax

The Musica data model would be represented in Imagine's syntax as follows:

Model Musician {
first_name string [max-length 100, primary-key]
last_name string [max-length 100]
age integer [range 10 100]
}
Model Album {
album_name string [max-length 200, primary-key]
release_date_time datetime
num_songs integer [range 0 100]
}
Relation albums_lead_vocalist {
many albums from Album
one lead_vocalist from Musician
}

Settings

There are no settings currently required for the data model domain.


Domain-specific syntax

Imagine's Domain-specific syntax for the data model domain supports two main modules: Model and Relation.

We will describe the various elements in each module in detail below.

Model

The Model module allows you to define a new data model in your app. This concept is analogous to a class in programming languages or table in a database.

Model 'model-name' {
'field-name' <field-type> [<field-property> 'field-property-value', <field-property> 'field-property-value']
'field-name' <field-type> [<field-property> 'field-property-value', <field-property> 'field-property-value']
}

The various elements of a Model are as follows:

Model-name

Each Model is followed by a unique user-selected <model-name> for that Model. Please see this for rules on selecting a <model-name>.

In the example below, Musician and Album are <model-names>.

Model Musician {
first_name string [max-length 100, primary-key]
last_name string [max-length 100]
age integer [range 10 100]
}
Model Album {
album_name string [max-length 200, primary-key]
release_date_time datetime []
num_songs integer [range 0 100]
rating float [range 0.0 5.0]
}


Field-name

Each Model has various fields which describe various attributes of the Model, and each field has a user-selected field-name. Please see this for rules on selecting a field-name

In the example below, first-name, last-name and age are field-names for the different fields in the Model Musician.

Model Musician {
first_name string [max-length 100, primary-key]
last_name string [max-length 100]
age integer [range 10 100]
}


Field-type

Each field is of a certain <field-type>, which is one of:

  • string - a string of characters
  • integer - a positive or negative whole number
  • datetime - date and time
  • float - a positive or negative number with a decimal point
  • boolean - a boolean value of either True or False

In the example below, the Model Album contains the following fields

  • album_name of <field-type> string
  • release_date_time of <field-type> datetime
  • num_songs of <field-type> integer
  • rating of <field-type> float
  • bestseller of <field-type> boolean
Model Album {
album_name string [max-length 200, primary-key]
release_date_time datetime []
num_songs integer [range 0 100]
rating float [range 1.0 5.0]
bestseller boolean []
}


Field-property, Field-property-value

Each <field-type> can have various <field-properties> and associated <field-property-values>that govern the behaviour of that field. We have outlines supported <field-properties> and <field-property-values>below:

  • primary-key

    • This <field-property> can be used for string and integer <field-types>.
    • This allows a field to be tagged as the unique identifier for a model.
    • Each model requires at least one field to be given a primary-key <field-property>.
    • The primary key <field-property> does not have an associated <field-property-value>.
    • This is similar to the concept of primary key in MySQL and primary key in PostgreSQL.

  • max-length

    • This <field-property> can be used for the string <field-types>.

    • This allows you to specify the maximum allowable length for a field instance where the <field-types> is string

    • In the example below, the first-name of the model Musician is allowed to have a maximum length of 100 characters.

      ```
      Model Musician {
      first_name string [max-length 100]
      }
      ```

  • choice

    • This <field-property> can be used for the string <field-type>.

    • This allows you to specify the maximum specific value choices that can be selected in for a field.

    • In the example below, the color of the model Shoes can be one of red, blue or green.

      Model Shoes {
      color string [ choice ["red", "blue", "green"] ]
      }

  • range

    • This <field-property> can be used for the integer and float <field-types>.

    • This allows you to specify the allowable range for a field.

    • In the example below, the age of the model Musician is only allowed to have values in the range of 20 and 50 (where both 20 and 50 inclusive).

      ```
      Model Musician {
      age integer [range 20 50]
      }
      ```
    • For integer fields, float values may be used, meaning that the field should be in the appropriate range. That is, age integer [range 20.5 50] is equivalent to age integer [range 21 50].


  • min

    • This <field-property> can be used for the integer and float <field-types>.

    • This allows you to specify the minimum value for a field.

    • In the example below, the age of the model Musician is only allowed to have values that are 20 or higher.

      ```
      Model Musician {
      age integer [min 20]
      }
      ```
    • For integer fields, a float value may be used, meaning that the field should be greater than the ceiling of the number. That is, age integer [min 0.5] is equivalent to age integer [min 1].


  • max

    • This <field-property> can be used for the integer and float <field-types>.

    • This allows you to specify the maximum value for a field.

    • In the example below, the age of the model Musician is only allowed to have values that are 50 or lower.

      ```
      Model Musician {
      age integer [min 50]
      }
      ```
    • For integer fields, a float value may be used, meaning that the field should be smaller than the floor of the number. That is, age integer [max 90.5] is equivalent to age integer [max 90].


  • unique

    • This <field-property> can be used for ALL <field-types>.

    • This allows a field to be tagged in a way that only one unique instance of that field.

    • The unique <field-property> does not have an associated <field-property-value>.

    • In the example below, using the unique <field-property> you can ensure that no two Musician instances can have the same social security number:

      ```
      Model Musician {
      Social_security_number integer [primary-key, unique]
      }
      ```

  • nullable

    • This <field-property> can be used for ALL <field-types>.

    • This allows a field instance to be null.

    • The nullable <field-property> does not have an associated <field-property-value>.

    • In the example below, using the nullable <field-property> for the age field allows this field to be left empty in an instance of the Musician model.

      ```
      Model Musician {
      first_name string [max-length 100, primary-key]
      last_name string [max-length 100]
      age integer [range 10 100, nullable]
      }
      ```

  • default

    • This <field-property> can be used for ALL <field-types>.

    • This allows a default value to be specified for the field.

    • The provided default value should be of the same type as the <field-type>.

    • In the example below, we have chosen a default <field-property> with <field-property-value> of red for the color field and of 20 for the size field in the Model Shoes.

      Model Shoes {
      color string [default "red"]
      size integer [default 10]
      }
    • There are also some special default <field-property-values> you can use:

      • auto-increment

        • This is a special default value which allows automatic incrementing of an integer <field-type>.
        • This is similar to a database's concept of serial in PostgreSQL and auto_increment in MySQL.
      • now

        • This is a special default value that assigns the current time as the value for a datetime <field-type>.
        • This is similar to a database's concept of current_timestamp` in MySQL and current_timestamp** in PostgreSQL.
      • Example use:

        Model Musician {
        id integer [default auto-increment, primary-key]
        last_seen datetime [default now]
        }


Relation

The Relation module allows you to define how any two Models are related in your app. This concept is analogous to relations in a database [ADD LINK].

We current support the many child models to one parent model relation in Imagine's syntax.

Relation <relation-name> {
many 'child-relation-name' from <child-model-name>
one 'parent-relation-name' from <parent-model-name>
parent-key 'unique-parent-field-name'
}

The various elements of a Relation are as follows:

relation-name

The relation-name allows you to name the specific Relation that you are adding to the data model.

In the example below, albums_lead_vocalist is the relation-name.

Relation albums_lead_vocalist {
many albums from Album
one lead_vocalist from Musician
}

Please see this for rules on selecting a <relation-name>.

child-model-name, parent-model-name

For two Models related as many child models to one parent model, <child-model-name> refers to the <model-name> of the child model, and <parent-model-name> refers to the <model-name> of the parent model.

In the example below, a Musician can have many Albums. Here, Album and Musician are both data models, where Album is the <child-model> and Musician is the <parent-model>.

Relation albums_lead_vocalist {
many albums from Album
one lead_vocalist from Musician


child-relation-name, parent-relation-name

A <child-relation-name> and <parent-relation-name> allow you add a specific name to the child and the parent Models that are related.

In the example below, a lead_vocalist (who is a Musician) can have many albums (from the Album data model). Here, lead_vocalist is the <parent-relation-name> and albums is the <child-relation-name>.

Relation albums_lead_vocalist {
many albums from Album
one lead_vocalist from Musician

Please see this for rules on selecting a <child-relation-name> and <parent-relation-name>.

parent-key, unique-parent-field-name

The <unique-parent-field-name> is a unique identifier of the parent-model - it can be the field-name of any field of a Model that has the field-property unique.

If you don't specify a parent-key, the field-name associated with the primary-key in the parent model is used as default the parent-key.

In the following example, 'id' is used as the default parent key.

Model Musician {
id integer [primary-key, autoincrement]
first_name string [max-length 100]
}
Model Album {
album_name string []
}
Relation albums_lead_vocalist {
many albums from Album
one lead_vocalist from Musician
}

Defining Relations allows you to access the relational data in your data model using your API as follows:

  • albums for a lead vocalist using musician-instance.albums, where albums is the <child-relation-name>
  • the lead vocalist to which an Album belongs to using album-instance.lead_vocalist, where lead_vocalist is the <parent-relation-name>

PS - here musician-instance and album-instance are used to described the specific instance of the musician (e.g. Pink Floyd) or the album (e.g. The Wall) that the API is trying to access.