Skip to content

Commit

Permalink
UPSERT statement review
Browse files Browse the repository at this point in the history
  • Loading branch information
Dhghomon committed Sep 18, 2024
1 parent 8f9495e commit 9836523
Showing 1 changed file with 70 additions and 22 deletions.
92 changes: 70 additions & 22 deletions src/content/doc-surrealql/statements/upsert.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,9 @@ import Since from '@components/Since.astro'

<Since v="v2.0.0" />

The `UPSERT` statement can be used to modify records in the database if they already exist. If the record does not exist, it will be created.

This is different from the [`UPDATE`](/docs/surrealql/statements/update) statement, which will fail if the record does not exist.
The `UPSERT` statement can be used to modify records in the database if they already exist. If a specified record ID does not exist, it will be created.

This is different from the [`UPDATE`](/docs/surrealql/statements/update) statement, which will do nothing if a specified record ID does not exist.

### Statement syntax

Expand All @@ -22,64 +21,113 @@ UPSERT [ ONLY ] @targets
[ CONTENT @value
| MERGE @value
| PATCH @value
| SET @field = @value ...
| [ SET @field = @value, ... | UNSET @field, ... ]
]
[ WHERE @condition ]
[ RETURN NONE | RETURN BEFORE | RETURN AFTER | RETURN DIFF | RETURN @statement_param, ... ]
[ TIMEOUT @duration ]
[ PARALLEL ]
;
```

## Example usage

For example, if you want to UPSERT a record if it exists, or create it if it does not, you can use the `UPSERT` statement.
If you want to update a record if it exists, or create it if it does not, you can use the `UPSERT` statement with a specific record ID.

```surql
-- UPSERT or create a record with a specific numeric id
UPSERT person:100 SET name = 'Tobie', company = 'SurrealDB', skills = ['Rust', 'Go', 'JavaScript'];
-- Update or create a record with a specific numeric id
UPSERT person:100 SET
name = 'Tobie',
company = 'SurrealDB',
skills = ['Rust', 'Go', 'JavaScript'];
```
The `UPSERT` statement can be used to modify records in the database if they already exist. If the record does not exist, it will be created.

In the case where the record ID isn't specified, any exisiting records in the table will be upserted. For example, the following query will UPSERT all records in the `person` table:
In the case where the record ID isn't specified, any existing records in the table will be updated. For example, the following query will update all records in the `person` table but will not create any new ones.

```surql
-- UPSERT all records in a table
-- The skills field is an array. The += operator alone is enough for SurrealDB to infer the type
UPSERT person SET skills += 'breathing';
```

The `UPSERT` statement supports conditional matching using the `WHERE` clause. For example, the following query will upsert the record with the specified ID only if the `name` field is equal to 'Tobie':

```surql
-- UPSERT a record with a specific numeric id only if the name is 'Tobie'
UPSERT person:100 SET name = 'Tobie', company = 'SurrealDB', skills = ['Rust', 'Go', 'JavaScript'] WHERE name = 'Tobie';
UPSERT person SET skills -= 'breathing';
```

## Using ONLY clause

The `ONLY` clause can be used to return a single record instead of an array of records.

```surql
-- UPSERT just a single record
-- Using the ONLY keyword, just an object for the record in question will be returned.
-- This, instead of an array with a single object.
UPSERT ONLY person:tobie SET name = 'Tobie', company = 'SurrealDB', skills = ['Rust', 'Go', 'JavaScript'];
UPSERT ONLY person:tobie SET
name = 'Tobie',
company = 'SurrealDB',
skills = ['Rust', 'Go', 'JavaScript'];
```

## Type inference when using UPSERT

The `+=` operator in the following query is enough for SurrealDB to infer that the `interests` field must be an `array<string>`.

```surql
-- UPSERT a document and remove a tag from an array
UPSERT person:tobie SET interests += 'Java';
```

Type inference will also work with a numeric value such as the `click_count` field below, in which case it will infer the field to be of type `int` with a default value of 0.

```surql
-- UPSERT a document and increment a numeric value
UPSERT webpage:home SET click_count += 1;
```

-- UPSERT a document and remove a tag from an array
UPSERT person:tobie SET interests -= 'Java';
Creating a record by default makes the `UPSERT` statement an ideal way to manage an incrementing field.

```surql
UPSERT event_for:[time::now().format("%Y-%m-%d")] SET
number += 1;
```

```bash title="Possible output"
[
{
id: event_for:[
'2024-09-18'
],
number: 1
}
]
```

Doing the same with an `UPDATE` statement would require much more manual work.

```surql
IF (SELECT * FROM event_for:[time::now().format("%Y-%m-%d")]).is_empty() {
CREATE event_for:[time::now().format("%Y-%m-%d")] SET number = 1;
} ELSE {
UPDATE event_for:[time::now().format("%Y-%m-%d")] SET number += 1;
};
```

## Using WHERE clause

The `UPSERT` statement supports conditional matching of records using a `WHERE` clause. If the expression in the `WHERE` clause evaluates to true, then the respective record will be upserted.
The `UPSERT` statement supports conditional matching using the `WHERE` clause, effectively turning it into an `UPDATE` statement. For example, the following query will update the record with the specified ID only if the `name` field is equal to 'Tobie' and will not create it otherwise.

```surql
-- UPSERT a record with a specific numeric id only if the name is 'Tobie'
UPSERT person:100 SET
name = 'Tobie',
company = 'SurrealDB',
skills = ['Rust', 'Go', 'JavaScript']
WHERE name = 'Tobie';
```

```surql
-- UPSERT all records which match the condition
UPSERT city SET population = 9541000 WHERE name = 'London';
```

## CONTENT clause

Instead of specifying record data using the `SET` clause, it is also possible to use the `CONTENT` keyword to specify the record data using a SurrealQL object.

```surql
Expand Down Expand Up @@ -141,7 +189,7 @@ The `UPSERT` statement supports bulk upsert, which allows multiple records to be
UPSERT person CONTENT [
{id: r"person:jaime", name: "Jaime", surname: "Morgan Hitchcock"},
{id: "tobie", name: "Tobie", surname: "Morgan Hitchcock"},
... 1000 more records
-- ... 1000 more records
]
```

Expand Down

0 comments on commit 9836523

Please sign in to comment.