Update Statement
The update statement in Surreal ORM allows you to modify existing records in
your database. It provides various operations to update fields and perform
incremental changes to the data. This documentation provides an overview of the
syntax and usage of the update statement, including the use of the object!
and object_partial! macros for setting values.
Table of Contents
- Syntax
- Using the
cond!Macro - Examples
Syntax
The basic syntax of the update statement is as follows:
#![allow(unused)] fn main() { update::<Type>(id) .content(content) .merge(merge) .replace(replace) .set(settables) .patch(patch_op) .where_(condition) .return_type(return_type) .timeout(duration) .parallel(); }
The update statement supports the following methods:
.content(content): Sets the content of the update statement..merge(merge): Performs a merge operation to update specific fields..replace(replace): Replaces the entire object with a new one..set(settables): Sets the values of the fields to be updated..patch(patch_op): Applies patch operations to the record..where_(condition): Adds a condition to the update statement..return_type(return_type): Specifies the desired return type for the query..timeout(duration): Sets the timeout duration for the query..parallel(): Executes the query in parallel.
Note: Only one of the .content(), .merge(), .replace(), .set(), or .patch() methods can be used at a time.
Using the cond! Macro
The cond! macro provides a concise way to define conditions for update
operations. It enhances code readability while ensuring type safety.
Example:
#![allow(unused)] fn main() { let filter = cond!((strength > 5) && (strength < 15)); }
By using the cond! macro, you can define conditions efficiently and
expressively for the update statement.
For a more in-depth explanation and advanced usage of the cond! macro,
refer to the dedicated chapter on helper macros.
Examples
Updating a Single Object
Using the Update Content
The update statement also supports the content method, which allows you to
specify the updated fields using a separate object. This provides a convenient
way to define the fields to be updated.
#![allow(unused)] fn main() { let created_weapon = create().content(weapon).get_one(db.clone()).await.unwrap(); let weapon_to_update = Weapon { name: "Oyelowo".to_string(), created: Utc::now(), strength: 1000, ..Default::default() }; let updated_weapon = update::<Weapon>(created_weapon.clone().id) .content(weapon_to_update) .get_one(db.clone()) .await?; }
In the above example, the content method is used to specify the fields to be
updated in the created_weapon object using the weapon_to_update object.
Using the object! and object_partial! Macros with Set Operation
The set method of the update statement supports the object! and
object_partial! macros, providing a type-safe and concise way to specify
values when updating records. These macros offer several advantages:
- Type-safety: Both macros ensure that all fields provided belong to the specified struct.
- Parameters and Fields: They allow the use of
parametersorfieldsas values, providing flexibility in constructing dynamic update statements. - Use within Transactions: Especially within the
block!macro for transactions, these macros can be invaluable as they allow dynamic field and parameter manipulations based on transactional logic.
Here's an example showcasing the usage of the object_partial! macro with the
set method:
#![allow(unused)] fn main() { let created_weapon = create().content(weapon).get_one(db.clone()).await.unwrap(); assert_eq!(created_weapon.name, "Laser"); assert_eq!(created_weapon.strength, 0); let ref id = created_weapon.clone().id; let weapon::Schema { strength, .. } = Weapon::schema(); update::<Weapon>(id) .set(object_partial!(Weapon { strength: 923u64 })) .return_one(db.clone()) .await?; let selected: Option<Weapon> = select(All) .from(Weapon::table()) .return_one(db.clone()) .await?; assert_eq!(selected.unwrap().strength, 923); }
In this example, the object_partial! macro is used with the set method to
update the strength field of the Weapon object. This approach offers the
advantages of type-safety and conciseness.
Here's an example showcasing the usage of the object! macro with the set
method:
#![allow(unused)] fn main() { let created_weapon = create().content(weapon).get_one(db.clone()).await.unwrap(); let weapon::Schema { strength, name, .. } = Weapon::schema(); update::<Weapon>(created_weapon.clone().id) .set(object!(Weapon { strength: strength.increment_by(100u64), name: "UpgradedWeapon".to_string() })) .return_one(db.clone()) .await?; }
In this example, the object! macro is used with the set method to
simultaneously set the strength field and rename the Weapon. The macro
ensures that the fields provided belong to the Weapon struct, providing
type-safety.
The primary difference between object! and object_partial! is completeness:
-
object!Macro: This macro requires you to provide values for all fields of the struct. It's useful when you have values for all fields and want to ensure no fields are missed. -
object_partial!Macro: This allows for specifying only a subset of fields. It's especially useful when you only want to update specific fields without having to specify all of them.
In practice, you'll choose between them based on the update requirements. If
you're updating all fields of a record and want to ensure none are missed,
object! is preferable. If you're updating only certain fields,
object_partial! offers a more concise approach.
Using the Set Operation
The update statement also supports the set method, which allows you to
perform 3 major kinds of updates including, overwriting a field with an
equal_to method, increment and decrement method operations for numbers,
append and remove methods for arrays. All the arguments to these methods are
type-checked at compile- time to make sure they are valid for the respective
fields
- Use set method for a single field
#![allow(unused)] fn main() { let created_weapon = create().content(weapon).get_one(db.clone()).await.unwrap(); let weapon_to_update = Weapon { name: "Oyelowo".to_string(), created: Utc::now(), strength: 1000, ..Default::default() }; update::<Weapon>(weapon_to_update.id) .set(strength.increment_by(5u64)) .run(db.clone()) .await?; // You can even pass the entire model instance as an argument update::<Weapon>(weapon_to_update) .set(strength.increment_by(5u64)) .run(db.clone()) .await?; }
- Use set methods for updating multiple fields
#![allow(unused)] fn main() { update::<Weapon>(id) .set([ strength.increment_by(5u64), name.equal("Oyedayo"), ]) .run(db.clone()) .await?; // In addition to array const `[T]`,you can also use a `vec!`. update::<Weapon>(id) .set(vec![ strength.increment_by(5u64), name.equal("Oyedayo"), ]) .run(db.clone()) .await?; }
In the above example, the set method is used to specify the fields to be
updated in the created_weapon object using the weapon_to_update object.
Using the Merge Operation
The merge operation allows you to update a single object by merging new values
into the existing object. The new values overwrite the old ones, while fields
not present in the new object are unaffected.
#![allow(unused)] fn main() { let created_weapon = create().content(weapon).get_one(db.clone()).await.unwrap(); let weapon_to_update = Weapon { name: "Oyelowo".to_string(), created: Utc::now(), strength: 1000, ..Default::default() }; let updated_weapon = update::<Weapon>(created_weapon.clone().id) .merge(weapon_to_update) .get_one(db.clone()) .await?; }
In the above example, the merge operation is used to update the
created_weapon object with the fields from weapon_to_update. The result is
stored in updated_weapon.
Using the Replace Operation
The replace operation allows you to replace an existing object entirely with a
new one. This operation removes all fields not present in the new object.
#![allow(unused)] fn main() { let created_weapon = create().content(weapon).get_one(db.clone()).await.unwrap(); let weapon_to_replace = Weapon { name: "Oyelowo".to_string(), created: Utc::now(), strength: 823, ..Default::default() }; let updated_weapon = update::<Weapon>(created_weapon.clone().id) .replace(weapon_to_replace) .get_one(db.clone()) .await?; }
In the above example, the replace operation replaces the created_weapon
object with the fields from weapon_to_replace. The result is stored in
updated_weapon.
Using the Patch Operation
The patch operation allows you to perform detailed modifications on fields
using methods such as patch_change, patch_replace, patch_remove, and
patch_add. It enables incremental changes to string fields, replacing field
values, removing fields, or adding new fields.
Using the Patch Add Operation
The patch_add operation adds a new field to the object. It allows you to
include additional fields during the update.
- Applying single patch
#![allow(unused)] fn main() { let created_weapon = create().content(weapon).get_one(db.clone()).await.unwrap(); let updated_weapon = update::<Weapon>(created_weapon.clone().id) .patch(nice.patch_add(true)) .get_one(db.clone()) .await?; }
- Applying multiple patches
#![allow(unused)] fn main() { let ref _updated_weapon = update::<WeaponOld>(old_weapon.clone().id) .patch([nice.patch_add(true), bunchOfOtherFields.patch_add(56)]) .return_one(db.clone()) .await; }
In the above example, the patch_add operation adds the nice field with the
value true to the created_weapon object.
Using the Patch Replace Operation
The patch_replace operation replaces the value of a field with a new value. It
allows you to update a field to a different value.
#![allow(unused)] fn main() { let created_weapon = create().content(weapon).get_one(db.clone()).await.unwrap(); let updated_weapon = update::<Weapon>(created_weapon.clone().id) .patch(strength.patch_replace(34u64)) .get_one(db.clone()) .await?; }
In the above example, the patch_replace operation replaces the value of the
strength field in the created_weapon object with the specified value.
Using the Patch Remove Operation
The patch_remove operation removes a field from the object entirely. This
operation is destructive, and the field will no longer be available after the
update. Make sure that the struct used here does not require that field to be
present. You can create a copy of the existing struct but without the new field.
#![allow(unused)] fn main() { let created_weapon = create().content(weapon).get_one(db.clone()).await.unwrap(); let updated_weapon = update::<Weapon>(created_weapon.clone().id) .patch(bunchOfOtherFields.patch_remove()) .get_one(db.clone()) .await?; }
In the above example, the patch_remove operation removes the
bunchOfOtherFields field from the created_weapon object.
Using the Patch Change Operation
The patch_change operation modifies part of a string field using the diff
format. It allows you to specify the changes to be applied to the field.
#![allow(unused)] fn main() { let created_weapon = create().content(weapon).get_one(db.clone()).await.unwrap(); let updated_weapon = update::<Weapon>(created_weapon.clone().id) .patch(name.patch_change("@@ -1,4 +1,4 @@\n te\n-s\n+x\n t\n")) .get_one(db.clone()) .await?; }
In the above example, the patch_change operation modifies the name field of
the created_weapon object by changing "test" to "text".
Updating Multiple Objects
To update multiple objects, you can use the update statement with a filter to
select the objects to update.
#![allow(unused)] fn main() { let filter = cond(strength.greater_than(5)).and(strength.less_than_or_equal(15)); let update_weapons_with_filter = update::<Weapon>(Weapon::table()) .content(Weapon { name: "Oyelowo".to_string(), created: Utc::now(), ..Default::default() }) .where_(filter) .return_many(db.clone()) .await?; }
In the above example, the update statement updates all Weapon objects that
meet the specified filter condition with the new values.
Please note that the above code snippets are for illustration purposes and may need to be adapted to your specific use case.
You have now learned how to use the update statement to modify existing
records in your SurrealDB database. Use the various operations and methods
provided by the update statement to perform precise updates and incremental
changes to your data.