Data Model

Xata is a multi-model serverless database service. It combines features from relational databases, with features from NoSQL databases and search engines. We do this internally by storing the data both in a transactional database (OLTP) as well as in a search/analytics engine (OLAP). This is done transparently for you and the different stores are exposed via the same API.

Data is organized in "tables" which are grouped into "databases". Tables have a strict schema, which contains a list of columns. Xata supports many column types and the type information is used, among other things, to generate type-safe clients.

You can represent relations between tables of the same database by using columns with type link, and it is possible to "join" tables at query time.

Records

The following is an example of a record that you can store natively in Xata:

{
  "name": "John Doe",
  "email": "john@example.com",
  "age": 42,
  "address": {
    "street": "123 Main St",
    "city": "New York"
  },
  "labels": ["admin", "user"]
}

The keys in the JSON are the column names. The values are the data. The coresponding schema file for the above record looks like this:

{
  "formatVersion": "1.0",
  "tables": [
    {
      "name": "users",
      "columns": [
        {
          "name": "name",
          "type": "string"
        },
        {
          "name": "email",
          "type": "email"
        },
        {
          "name": "age",
          "type": "int"
        },
        {
          "name": "address",
          "type": "object",
          "columns": [
            {
              "name": "street",
              "type": "string"
            },
            {
              "name": "city",
              "type": "string"
            }
          ]
        },
        {
          "name": "labels",
          "type": "multiple"
        }
      ]
    }
  ]
}

In the above, it's worth noting:

  • Each column has a well-defined type. Xata has a strongly typed schema, as opposed to a schemaless model.
  • Some of the Xata data types are higher level than typically in a database. For example, the "email" type performs automatic validation of email addresses.

Column types

string

The string type represents a simple string. The values of this type are indexed both for quick exact matching and for full-text search. Set unique to make sure the strings you insert are unique.

Example definition:

{
  "name": "name",
  "type": "string"
}

text

The text type represents a long-form text. The difference to string is that the values of the text columns are indexed and optimised for free-text search, but not for exact matching.

Example definition:

{
  "name": "address",
  "type": "text"
}

int

The int type represents an integer. Internally it's represented as a 64-bit signed integer. This means the minimum value is -9223372036854775808 and the maximum value is 9223372036854775807. Set unique to make sure the integers you insert are unique.

Example definition:

{
  "name": "age",
  "type": "int"
}

float

The float type represents a double precision floating point number. Internally, it's represented as a 64-bit signed number with up to 15 decimal digit precision. Set unique to make sure the floats you insert are unique.

Example definition:

{
  "name": "distance",
  "type": "float"
}

datetime

The datetime type represents a point in time. It has the date part and the time part, and both are mandatory. A RFC 3339-compliant string is used for input and output representation. Set unique to make sure the values you insert are unique.

Example definition:

{
  "name": "createdAt",
  "type": "datetime"
}

Example values:

{
  "createdAt": "2020-11-10T10:38:16Z"
}
{
  "createdAt": "2020-11-10T12:38:16+02:00"
}

bool

The bool type represents a boolean.

Example definition:

{
  "name": "isPublished",
  "type": "bool"
}

email

The email type represents an email address. Valid email addresses are not longer than 254 characters and respect this regular expression:

^[a-zA-Z0-9.!#$%&'*+\\/=?^_`{|}~-]+@
[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?
(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$

Set unique to make sure the strings you insert are unique.

Example definition:

{
  "name": "emailAddress",
  "type": "email"
}

Example value:

{
  "emailAddress": "test@example.com"
}

multiple

The multiple type represents an array of strings.

Example definition:

{
  "name": "labels",
  "type": "multiple"
}

To insert a value into a multiple column, use a JSON array of strings, for example:

{
  "labels": ["admin", "user"]
}

object

The object type represents a group of columns. Objects can also be nested multiple times by adding a column of type object inside the object. Set unique to make sure the objects you insert are unique.

Example definition:

{
  "name": "address",
  "type": "object",
  "columns": [
    {
      "name": "street",
      "type": "string"
    },
    {
      "name": "city",
      "type": "string"
    }
  ]
}

To insert a value of type object, simply use a JSON map:

{
  "address": {
    "street": "123 Main St",
    "city": "New York"
  }
}

The link type represents a link to another table. See the links and relations section for more information. Set unique to make sure the link values you insert are unique.

Example definition:

{
  "name": "author",
  "type": "link",
  "link": {
    "table": "users"
  }
}

In the above example, the link.table property is mandatory and indicates the target table.

To insert the value for a link column, use the target record ID as the value. For example:

{
  "author": "rec_1234567890"
}

Special Columns

Xata maintains a special set of columns that don't have to be defined in the schema. The id and the xata.* columns names are reserved and you cannot create your own columns with these names. No other column names are reserved.

The id and xata.* are not explicitely represented in the schema file, because they are present for all tables and maintained by Xata.

id

The id column contains the record ID. It is of type string and it is guaranteed to be unique within the table. If you don't explicitly provide an id value, Xata will generate one for you, with the following properties:

  • globally unique
  • sortable, with the newest record having the highest ID

It is possible to use your own ID values, by using the insert record with ID API.

xata.version

The xata.version column contains the current version of the record. It is of type int and it is automatically incremented any time the record is updated. A newly inserted record will have a version of 0.

This column is meant to be use for optimistic concurrency control, see the dedicated section for more information.

You can represent 1:N relations between tables by using columns of the link type. The value of the link column points to a record in the target table, specified by the ID. At query time, you can easily include columns from the target table by specifying the "columns" field in the request. For an example, see Selecting Columns from the Linked Tables.

It is also possible to use link columns that target the same table. For example, you can have a parent column that points to the parent record of the current record.

You can represent N:M relations between two tables by using a third table dedicated for the relationship. For example, lets say you have a mentors table and a mentee table, and you want to represent an N:M relation where a mentor can have multiple mentees and a mentee can have multiple mentors. You can create a relationships table with a schema looking like this:

{
  "name": "relationships",
  "columns": [
    {
      "name": "mentor",
      "type": "link",
      "link": {
        "table": "mentors"
      }
    },
    {
      "name": "mentee",
      "type": "link",
      "link": {
        "table": "mentees"
      }
    },
    {
      "name": "status",
      "type": "string"
    }
  ]
}

In the above, you have links to the mentors and mentees tables, but you can also add other columns that are specific for the relationship. In the SQL world, this is called an associative or a junction table.

Constraints

You can add constraints to protect the integrity of your data. We support the following constaints:

  • unique: All values of a column must be different.

Limitations: You can add constraints only to new columns of empty tables.

Conclusion

This is the Xata data model as it stands today. Knowing what's available to you, we'd encourage exploring more concepts such as the schema or branches. If you're ready to start playing with the API, head on over to its dedicated getting started guide.


Last modified 15 days ago