Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Define field and future improvements #1014

Merged
merged 2 commits into from
Nov 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions src/content/doc-surrealql/datamodel/futures.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,34 @@ CREATE person SET
;
```

## Futures inside schema definitions

A future can be added to a schema definition as well.

```surql
DEFINE FIELD accessed_at ON TABLE user VALUE <future> { time::now() };

CREATE user:one;
SELECT * FROM ONLY user:one;
-- Sleep for one second
SLEEP 1s;
-- `accessed_at` is a different value now
SELECT * FROM ONLY user:one;
```

This differs from a non-future `VALUE` clause which is only calculated when it is modified (created or updated), but is not recalculated during a `SELECT` query which does not modify a record.

```surql
DEFINE FIELD updated ON TABLE user VALUE time::now();

CREATE user:one;
SELECT * FROM ONLY user:one;
-- Sleep for one second
SLEEP 1s;
-- `updated` is still the same
SELECT * FROM ONLY user:one;
```

## Next steps

You've now seen how to create dynamically computed properties on records, using either simple values, and values which depend on local and remote record fields. Take a look at the next chapter to understand how types can be cast and converted to other types.
125 changes: 88 additions & 37 deletions src/content/doc-surrealql/statements/define/field.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ The following expression shows the simplest way to use the `DEFINE FIELD` statem
-- Declare the name of a field.
DEFINE FIELD email ON TABLE user;
```

## Defining data types

The `DEFINE FIELD` statement allows you to set the data type of a field. For a full list of supported data types, see [Data types](/docs/surrealql/datamodel).
Expand All @@ -60,6 +61,44 @@ DEFINE FIELD locked ON TABLE user TYPE bool;
DEFINE FIELD login_attempts ON TABLE user TYPE number;
```

A `|` vertical bar can be used to allow a field to be one of a set of types. The following example shows a field that can be a [`UUID`](/docs/surrealql/datamodel/uuid) or an [`int`](/docs/surrealql/datamodel/numbers#integer-numbers), perhaps for `user` records that have varying data due to two diffent legacy ID types.

```surql
-- Set a field to have either the uuid or int type
DEFINE FIELD user_id ON TABLE user TYPE uuid|int;
```

### Array type

You can also set a field to have the array data type. The array data type can be used to store a list of values. You can also set the data type of the array's contents.

```surql
-- Set a field to have the array data type
DEFINE FIELD roles ON TABLE user TYPE array<string>;

-- Set a field to have the array data type, equivalent to `array<any>`
DEFINE FIELD posts ON TABLE user TYPE array;

-- Set a field to have the array object data type
DEFINE FIELD emails ON TABLE user TYPE array<object>;
```

### Making a field optional

You can make a field optional by wrapping the inner type in an `option`, which allows you to store `NONE` values in the field.

```surql
-- A user may enter a biography, but it is not required.
-- By using the option type you also allow for NONE values.
DEFINE FIELD biography ON TABLE user TYPE option<string>;
```

The example below shows how to define a field `user` on a `POST` table. The field is of type [record](/docs/surrealql/datamodel/records). This means that the field can store a `record<user>` or `NONE`.

```surql
DEFINE FIELD user ON TABLE POST TYPE option<record<user>>;
```

### Flexible data types

Flexible types allow you to have `SCHEMALESS` functionality on a `SCHEMAFULL` table. This is necessary for working with nested `object` types, which by nature do not have defined fields.
Expand Down Expand Up @@ -110,67 +149,79 @@ With `FLEXIBLE`, the output will be as expected as the schema now flexibly allow
]
```

### Array type

You can also set a field to have the array data type. The array data type can be used to store a list of values. You can also set the data type of the array's contents.
The fields of an object can be defined individually using the `.` operator.

```surql
-- Set a field to have the array data type
DEFINE FIELD roles ON TABLE user TYPE array<string>;
-- A respective subfield for the array field will automatically be defined.
-- If there are any existing definitions, clauses like VALUE and ASSERT will automatically be merged.
DEFINE FIELD roles.* ON TABLE user TYPE string;

-- Set a field to have the array data type, equavalent to `array<any>`
DEFINE FIELD posts ON TABLE user TYPE array;
-- The subfield will automatically be defined with the `any` data type, but we can overwrite it
-- Set the contents of the array to only support a record data type
DEFINE FIELD posts.* ON TABLE user TYPE record;

-- Set a field to have the array object data type
DEFINE FIELD emails ON TABLE user TYPE array<object>;
-- Define nested object property types
DEFINE FIELD emails.*.address ON TABLE user TYPE string;
DEFINE FIELD emails.*.primary ON TABLE user TYPE bool;
DEFINE FIELD emails.address ON TABLE user TYPE string;
DEFINE FIELD emails.primary ON TABLE user TYPE bool;
```
### Making a field optional

You can make a field optional by wrapping the inner type in an `option`, which allows you to store `NONE` values in the field.
### Using the `DEFAULT` clause to set a default value

You can set a default value for a field using the `DEFAULT` clause. The default value will be used if no value is provided for the field.

```surql
-- A user may enter a biography, but it is not required.
-- By using the option type you also allow for NONE values.
DEFINE FIELD biography ON TABLE user TYPE option<string>;
-- A user is not locked by default.
DEFINE FIELD locked ON TABLE user TYPE bool
-- Set a default value if empty
DEFAULT false;
```

The example below shows how to define a field `user` on a `POST` table. The field is of type [record](/docs/surrealql/datamodel/records). This means that the field can store a `record<user>` or `NONE`.
### Using the `VALUE` clause to set a field's value

The `VALUE` clause differs from `DEFAULT` in that a default value is calculated if no other is indicated, otherwise accepting the value given in a query.

```surql
DEFINE FIELD user ON TABLE POST TYPE option<record<user>>;
```
DEFINE FIELD updated ON TABLE user DEFAULT time::now();

### Setting a default value
-- Set `updated` to the year 1900
CREATE user SET updated = d"1900-01-01";
-- Then set to the year 1910
UPDATE user SET updated = d"1910-01-01";
```

You can set a default value for a field using the `DEFAULT` clause. The default value will be used if no value is provided for the field.
A `VALUE` clause, on the other hand, will ignore attempts to set the field to any other value.

```surql
-- A user is not locked by default.
DEFINE FIELD locked ON TABLE user TYPE bool
-- Set a default value if empty
DEFAULT false;
DEFINE FIELD updated ON TABLE user VALUE time::now();

-- Ignores 1900 date, sets `updated` to current time
CREATE user SET updated = d"1900-01-01";
-- Ignores again, updates to current time
UPDATE user SET updated = d"1900-01-01";
```

### `DEFAULT` inherited from `VALUE`
As the example above shows, a `VALUE` clause sets the value every time a record is modified (created or updated). However, the value will not be recalculated in a `SELECT` statement, which simply accesses the current set value.

If the `VALUE` clause contains a static query, meaning not depending on any variables, and in case no `DEFAULT` clause is specified, then the `VALUE` clause will be used as the `DEFAULT` clause as well.
```surql
DEFINE FIELD updated ON TABLE user VALUE time::now();

CREATE user:one;
SELECT * FROM ONLY user:one;
-- Sleep for one second
SLEEP 1s;
-- `updated` is still the same
SELECT * FROM ONLY user:one;
```

To create a field that is calculated each time it is accessed, a [`future`](/docs/surrealql/datamodel/futures) can be used.

```surql
DEFINE FIELD updated ON resource VALUE time::now();
DEFINE FIELD accessed_at ON TABLE user VALUE <future> { time::now() };

CREATE user:one;
SELECT * FROM ONLY user:one;
-- Sleep for one second
SLEEP 1s;
-- `accessed_at` is a different value now
SELECT * FROM ONLY user:one;
```

### Alter a passed value
### Altering a passed value

You can alter a passed value using the `VALUE` clause. This is useful for altering the value of a field before it is stored in the database.

In the example below, the `VALUE` clause is used to ensure that the email address is always stored in lowercase characters by using the [`string::lowercase`](/docs/surrealql/functions/database/string#stringlowercase) function.

```surql
Expand Down