Introduction

The Rust Logo

The landscape of software development is both vast and intricate, dotted with countless tools and frameworks that rise as answers to the ever-present challenges faced by developers. Yet, amidst this vastness, finding a tool that truly resonates with one's philosophies can be like searching for a needle in a haystack. Such was the experience that led to the creation of surreal_orm.

After delving into over ten ORM solutions in Rust alone and journeying through more than twenty across languages such as TypeScript, Python, Java, Ruby, Elixir, and more, it became starkly evident: while many tools cater to the basic needs, very few strike the harmonious chord of simplicity, power, and expressiveness. This realization wasn't just a mere observation—it was the catalyst that inspired surreal_orm.

This book is not just an introduction to an ORM library; it's a narrative of a journey, a testament to a set of deeply-held philosophies, and an exploration of groundbreaking innovations:

  1. Expressive Yet Intuitive API: Traditional ORMs often make complex queries convoluted and unreadable. surreal_orm challenges this norm. At the heart of surreal_orm lies an unwavering commitment to clarity. The belief is straightforward: if a query can be articulated in raw string format, it should be just as elegantly expressible within the ORM. This ensures that even as queries grow intricate, they remain legible, empowering developers to write intuitive code without sacrificing capability.

  2. Compile-Time Excellence: Harnessing the full might of Rust's compile-time error checking, surreal_orm emphasizes robustness from the onset. From model declarations to query constructions, the goal is to catch potential pitfalls even before the code springs to life. And in scenarios where compile-time checks aren't feasible, surreal_orm employs meticulous runtime validations, standing as a testament to its commitment to reliability and safety.

  3. Pioneering Features: surreal_orm introduces pioneering ideas that set it apart. From innovative macros such as block!, transaction, object!, object_partial!, and cond!, to compile-time validations of graph structures, and advanced features like deep graph access and auto-parametrized mathematical expressions—these are just a glimpse of the groundbreaking capabilities you'll encounter.

  4. Full Specification Support: Beyond its intuitive design and innovative features, surreal_orm stands tall with its comprehensive support for the full specification. It's not just another ORM or query builder; it's a beacon of compliance and expressiveness in the ORM landscape.

As you delve deeper into these pages, you'll journey beyond the mechanics, delving into the essence of surreal_orm—understanding its origins, the problems it seeks to solve, and the philosophies that molded its creation. It's a tale of refusing to settle, of reimagining boundaries, and of sculpting a solution when none seemed just right.

Each chapter, carefully crafted by the very creator of surreal_orm, promises a deep dive into its intricacies, philosophies, and innovations. So, whether you're a seasoned Rust developer, an ORM enthusiast, or a curious soul eager to explore the intersections of innovation and software development, this book promises a voyage into the heart of data management with a fresh perspective in Rust—a realm where convention meets innovation, culminating in the creation of something truly surreal.

-- © Oyelowo Oyedayo, 2023.

Quick Start

Surreal ORM Documentation

Introduction

Surreal ORM is an Object-Relational Mapping and query-building library for Rust that provides a high-level API for interacting with SurrealDB, a distributed graph database. This documentation will guide you through the usage and features of the Surreal ORM library.

Getting Started

To use Surreal ORM in your Rust project, you need to add it as a dependency in your Cargo.toml file:

[dependencies]
surreal_orm = "https://github.com/Oyelowo/surreal_orm"

After adding the dependency, you can import the necessary modules in your Rust code:

#![allow(unused)]
fn main() {
use surreal_orm::*;
}

Connecting to SurrealDB

Before interacting with SurrealDB, you need to establish a connection to the database. The following example demonstrates how to create a connection to a local SurrealDB instance:

use surrealdb::engine::local::Mem;
use surrealdb::Surreal;

#[tokio::main]
async fn main() {
    let db = Surreal::new::<Mem>(()).await.unwrap();
}

In this example, we create a new SurrealDB instance using the Surreal::new function with the local::Mem engine. The local::Mem engine represents a local in-memory database. You can replace it with other engine types according to your setup.

Defining a Model

A model in Surreal ORM represents a database table. You can define a model by creating a Rust struct and implementing the Node or Edge trait. Here's an example of defining a SpaceShip model:

#![allow(unused)]
fn main() {
use surreal_orm::*;

#[derive(Node, Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
#[orm(table = space_ship)]
pub struct SpaceShip {
    pub id: SurrealSimpleId<Self>,
    pub name: String,
    pub age: u8,
}
}

In this example, we define a SpaceShip struct and annotate it with the Model derive macro. The table attribute specifies the name of the corresponding database table.

Querying Data

Surreal ORM provides a fluent and expressive API for querying data from the database. You can use the select function to start a select statement and chain various methods to build the query. Here's an example:

#![allow(unused)]
fn main() {
use surreal_orm::statements::{select, All};

let space_ship::Schema { name, age, .. } = SpaceShip::schema();

let statement = select(All)
    .from(space_ship)
    .where_(name.equal("Millennium Falcon"))
    .order_by(age.desc())
    .limit(10);
}

In this example, we start a select statement using the select function and pass the All argument to select all fields. We specify the table name using the from method and add a condition using the where_ method. We can also use the order_by method to specify the sorting order and the limit method to limit the number of results.

Inserting Data

To insert data into the database, you can use the insert function and provide the data as a vector of structs. Here's an example:

#![allow(unused)]
fn main() {
use surreal_orm::statements::insert;

let spaceships = vec![
    SpaceShip {
        id: "1".to_string(),
        name: "Millennium Falcon".to_string(),
        age: 79,
    },
    SpaceShip {
        id: "2".to_string(),
        name: "Starship Enterprise".to_string(),
        age: 15,
    },
];

insert(spaceships).return_many(db.clone()).await?;
}

In this example, we define a vector of SpaceShip structs and pass it to the insert function. We then call the run method to execute the insertion operation.

Updating Data

To update data in the database, you can use the update function and provide the updated data as a struct. Here's an example:

#![allow(unused)]
fn main() {
use surreal_orm::statements::update;

let spaceship = SpaceShip {
    id: "1".to_string(),
    name: "Millennium Falcon".to_string(),
    age: 60
};

update(spaceship).run(db.clone()).await?;
}

In this example, we define a SpaceShip struct with the updated data and pass it to the update function. We then call the run method to execute the update operation.

Deleting Data

To delete data from the database, you can use the delete function and provide the condition for deletion. Here's an example:

#![allow(unused)]
fn main() {
use surreal_orm::statements::{delete, Field};

let space_ship::Schema { name, age, .. } = SpaceShip::schema();
let condition = name.eq("Millennium Falcon");

delete(space_ship)
    .where_(cond(name.equal("Millennium Falcon")).and(age.less_then(50)))
    .run(db.clone())
    .await?;
}

In this example, we use the delete function and specify the table name as a string. We add a condition using the where_ method, and then call the run method to execute the deletion operation.

Conclusion

This concludes the basic usage and features of the Surreal ORM library. You can explore more advanced features and methods in the API documentation. If you have any further questions or need assistance, feel free to reach out.

Comparision

Date Model

Data Types

Data Model in surreal_orm

In the surreal_orm, developers are provided with a comprehensive data model that mirrors the specifications laid out by the SurrealDB documentation. This ensures seamless integration with SurrealDB while also extending the capabilities to cater to more advanced use cases, such as supporting diverse value types in one unified representation.

Table of Contents

  1. Overview
  2. Record IDs
  3. Basic Types
  4. Record Links

Overview

The data model in surreal_orm allows for a flexible representation of different data types. By utilizing structures such as ValueType, the ORM can represent a wide array of types from basic values, fields, parameters, to complex operations and statements.

Record IDs

While the official SurrealDB documentation might detail how unique identifiers are managed for records, the ORM's handling of this might be implicit or handled in a way that abstracts the details away from the developer. You can read more on a dedicated chapter to Surreal Id where an abstraction is created to make it a easier, more intuitive and consistent to work with record ids in surrealdb.

Basic Types

Strings

In surreal_orm, strings are represented using the StrandLike structure:

#![allow(unused)]
fn main() {
pub struct StrandLike(..);
}

This struct can be used to represent a string value, field, or parameter, allowing it to be seamlessly integrated into various parts of a query.

Numbers

Numbers are represented using the NumberLike structure:

#![allow(unused)]
fn main() {
pub struct NumberLike(..);
}

Like StrandLike, it can be used to represent a numeric value, field, or parameter in a query.

Datetimes

Datetimes are encapsulated using the DatetimeLike structure:

#![allow(unused)]
fn main() {
pub struct DatetimeLike(..);
}

This allows for a clear representation of date and time values within the ORM.

Objects

Objects are complex data types that encapsulate key-value pairs. They are represented in surreal_orm using the ObjectLike structure:

#![allow(unused)]
fn main() {
pub struct ObjectLike(..);
}

Arrays

Arrays, which can contain multiple items of the same type, are represented using the ArrayLike structure:

#![allow(unused)]
fn main() {
pub struct ArrayLike(..);
}

And for function arguments, the ArgsList structure is used:

#![allow(unused)]
fn main() {
pub struct ArgsList(..);
}

Geometries

Geometries, which might represent spatial data, are encapsulated in the GeometryLike structure:

#![allow(unused)]
fn main() {
pub struct GeometryLike(..);
}

While the provided code does not show explicit handling for record links, it can be inferred that such links could be represented using SurrealId types.


This is a foundational overview of the data model in surreal_orm, with the aim of mirroring the SurrealDB specifications. The ORM extends the basic data types to provide a richer experience, supporting various operations and query constructs seamlessly.

Future

In surrealdb, futures provide a powerful mechanism to compute dynamic values when data is selected and returned to the client. Essentially, a future is a type of cast function that enables values to be dynamically evaluated upon retrieval.

Table of Contents

  1. Introduction to Futures
  2. Simple Futures
  3. Futures Depending on Other Fields
  4. Advanced Usage of Futures

Introduction to Futures

Futures are a unique feature of SurrealDB that allows for dynamic computation of values. Instead of storing a static value within a record, futures compute the value dynamically whenever the record is accessed. This ensures that you always get the most recent and relevant data.

Simple Futures

Any value or expression can be wrapped inside a future, ensuring it's evaluated upon every access.

** Example **

#![allow(unused)]
fn main() {
let result = create().set(object!(Person {
    accessed_date: future(time::now!())
});
assert_eq!(result.build(), "CREATE person SET accessed_date = <future> { time::now() }");
}

Futures Depending on Other Fields

Futures can also compute values based on other fields in the record. This allows for dynamic calculations that reflect the latest state of the record.

** Example **

#![allow(unused)]
fn main() {
let birthday = Person::schema().birthday;
let eighteen_years = Duration::from_secs(60 * 60 * 24 * 7 * 365 * 18);
let date_of_birth = chrono::Date::MIN_UTC;

let can_drive = future("time::now() > birthday + 18y");
let result = create().set(object!(Person {
    birthday: date_of_birth,
    can_drive: future(time::now!().gt(birthday).plus(eighteen_years))
}));
assert_eq!(result.build(), "CREATE person SET birthday = 2007-06-22, can_drive = <future> { time::now() > birthday + 18y }");
}

Advanced Usage of Futures

Futures offer much more than just simple dynamic calculations. They can dynamically access remote records, execute subqueries, and even traverse graphs.

** Example **

#![allow(unused)]
fn main() {
let friends = Person::schema().friends;
let id1 = Person::create_id("dayo");
let id2 = Person::create_id("yelow");
let friends = Person::schema().friends;

let result = create().set(object!(Person {
    name: String::from("Oyelowo"),
    friends: vec![id1, id2],
    adult_friends: future(friends(cond(age.gt(18))).name),
}));
assert_eq!(result.build(), "CREATE person SET name = 'Oyelowo', friends = [person:dayo, person:yelow], adult_friends = <future> { friends[WHERE age > 18].name }");
}

Utilizing futures in surreal_orm provides a dynamic layer to your data, ensuring that you always receive the most up-to-date calculations and evaluations when querying your records. Whether you're calculating age, fetching related records, or even performing complex graph operations, futures have got you covered.

Casting

Casting is an indispensable tool in data management, allowing developers to convert values from one type to another. This chapter provides an in-depth look into the casting functionality provided by surreal_orm, illuminating its power, elegance, and strict adherence to the SurrealDB specifications.

Table of Contents

  1. Introduction to Casting
  2. The Cast Structure in surreal_orm
  3. Cast Functions
  4. Conclusion

Introduction to Casting

In programming, casting is the practice of converting variables from one type to another, enabling more flexible data manipulation. Whether receiving input from a user, reading data from a file, or interfacing with databases, casting becomes a pivotal component.

Casting to Boolean

This function converts a value into a boolean. In raw queries, it's represented as <bool>.

#![allow(unused)]
fn main() {
let result = bool("true");
assert_eq!(result.build(), "<bool> true");
}

Casting to Integer

Convert a value into an integer. In raw queries, it's represented by <int>.

#![allow(unused)]
fn main() {
let result = int(13.572948467293847293841093845679289);
assert_eq!(result.build(), "<int> 13");
}

Casting to Float

Convert a value into a floating point number. In raw queries, it's represented by <float>.

#![allow(unused)]
fn main() {
let result = float(13.572948467293847293841093845679289);
assert_eq!(result.build(), "<float> 13.572948467293847");
}

Casting to String

Convert a value into a string. In raw queries, it's represented by <string>.

#![allow(unused)]
fn main() {
let result = string(true);
assert_eq!(result.build(), "<string> true");
}

Casting to Number

Convert a value into an infinite precision decimal number. In raw queries, it's represented by <number>.

#![allow(unused)]
fn main() {
let result = number(13.572948467293847293841093845679289);
assert_eq!(result.build(), "<number> 13.572948467293847293841093845679289");
}

Casting to Decimal

Convert a value into an infinite precision decimal number. In raw queries, it's represented by <decimal>.

#![allow(unused)]
fn main() {
let result = decimal(13.572948467293847293841093845679289);
assert_eq!(result.build(), "<decimal> 13.572948467293847293841093845679289");
}

Casting to DateTime

Convert a value into a datetime. In raw queries, it's represented by <datetime>.

#![allow(unused)]
fn main() {
let result = datetime("2022-06-07 will be parsed");
assert_eq!(result.build(), "<datetime> 2022-06-07");
}

Casting to Duration

Convert a value into a duration. In raw queries, it's represented by <duration>.

#![allow(unused)]
fn main() {
let result = duration("1h30m will be parsed");
assert_eq!(result.build(), "<duration> 1h30m");
}

Conclusion

Surreal Orm presents a powerful and user-friendly approach to casting, adhering closely to SurrealDB standards. Whether you're an experienced Rust developer or just starting, surreal_orm provides the tools for precise and effortless data manipulation.

Concepts

Model

In Surreal, a Model represents a blueprint of your data model consisting of various Nodes and Edges. A Model is a collection of various Nodes (entities) and their relationships (Edges), providing a comprehensive view of your data.

The Object struct is used to define a Model, and it has its own set of struct and field attributes. For instance, the rename_all struct attribute lets you define a case convention for all the fields in the Model. And the rename field attribute allows you to specify a different name for a field.

Node

In Surreal, your database is represented using Nodes, Edges, and Objects:

  • Nodes: These correspond to database tables, defined as Rust structs implementing the Node trait. Nodes can link to other Nodes and incorporate Objects for complex nested data structures.

  • Edges: Edges represent relationships between Nodes and are used for modeling many-to-many relationships or storing additional information about the relationship itself.

  • Objects: These are complex nested data structures embedded within Nodes. While they don't represent standalone tables, they facilitate complex data modeling within a Node.

Nodes are the heart of your database model in Surreal. They're Rust structs decorated with Node attributes for overall configuration and field-specific attributes for property definition. There are three types of links that you can use to define relationships between Nodes: LinkSelf, LinkOne, and LinkMany.

  • LinkSelf: This is a self-referential link within the same Node (table). For example, if an Alien can be friends with other aliens, you would use LinkSelf.

  • LinkOne: This creates a one-to-one relationship between two different Nodes. If every Alien has exactly one Weapon, you would use LinkOne.

  • LinkMany: This creates a one-to-many relationship between two Nodes. If an Alien can have multiple SpaceShips, you would use LinkMany.

For example:

#![allow(unused)]
fn main() {
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use surreal_orm::{LinkMany, LinkOne, LinkSelf, SurrealSimpleId, Node};

#[derive(Node, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase")]
#[orm(table = "alien")]
pub struct Alien {
    pub id: SurrealSimpleId<Self>,

    #[orm(link_self = "Alien")]
    pub friend: LinkSelf<Alien>,

    #[orm(link_one = "Weapon")]
    pub weapon: LinkOne<Weapon>,

    #[orm(link_many = "SpaceShip")]
    pub space_ships: LinkMany<SpaceShip>,
}

#[derive(Node, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase")]
#[orm(table = "weapon")]
pub struct Weapon {
    pub id: SurrealSimpleId<Self>,
    pub name: String,
    pub strength: u64,
}

#[derive(Node, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[orm(table = "space_ship")]
pub struct SpaceShip {
    pub id: SurrealId<Self, String>,
    pub name: String,
    pub created: DateTime<Utc>,
}
}

In this Alien Node, an alien can have an friend (another alien), a weapon (one-to-one relationship with Weapon Node), and multiple spaceships (one-to-many relationship with SpaceShip Node).

In summary, Nodes in Surreal provide a powerful way to model your database schema directly in Rust, with type safety, automatic serialization/deserialization, and the ability to define complex relationships between different tables.

Node Attributes on Struct

In Surreal ORM, node attributes provide a convenient mechanism to dictate the behavior and structure of database tables and their associated fields. These attributes are not only powerful tools for developers but also help in maintaining a consistent and clear database schema. This chapter will delve into the intricacies of node attributes, their application, and best practices for their usage.

Table of Contents

  1. Introduction to Node Attributes
  2. Working with Node Attributes
  3. Node Attributes: Examples
  4. Ensuring Valid Usage of Node Attributes
  5. Conclusion

Introduction to Node Attributes

Node attributes in Surreal ORM allow developers to:

  • Rename fields of a struct according to a naming convention.
  • Explicitly set or infer the table name.
  • Enforce schema structures.
  • Handle table drops and recreations.
  • Create table projections or views.
  • Set granular permissions for CRUD operations on tables.
  • Define the table structure either inline or through external functions.

Working with Node Attributes

Supported table attributes

Struct Attributes

AttributeDescriptionTypeOptional
rename_allRenames all the struct's fields according to the given case convention. The possible values are "lowercase", "UPPERCASE", "PascalCase", "camelCase", "snake_case", "SCREAMING_SNAKE_CASE".stringY
tableExplicitly define the table name. By default, it must correspond with the struct name in snake_case. Use relax_table if you want to opt out of this but not encouraged.OptionY
relax_tableDetermines whether the struct's name is matched to the table name as the snake case by default. This is not encouraged. Using your struct 1:1 to your database tables helps to ensure uniquness and prevent confusion.OptionY
schemafullMake the table enforce a schema struct.OptionY
dropDrop the table if it exists and create a new one with the same name.OptionY
asInline statement e.g select(All).from(user) for creating a projection using the DEFINE TABLE statement. This is useful for copying data from an existing table in the new table definition. This is similar to making a view in a RDBMS.A select statementY
as_fnSame as above as but defined as external function from the struct e.g select_reading_from_user for creating a projection using the DEFINE TABLE statement. This is useful for copying data from an existing table in the new table definition. This is similar to making a view in a RDBMS.A function nameY
permissionsSpecify permissions that apply to the table using the for statement.ForStatementY
permissions_fnSame as permission but as an external function from the struct. Specify permissions that apply to the table using the for statement.ForStatementY
defineGenerates a DEFINE TABLE statement for the table. This overrides other specific definitions to prevent confusion and collision. You can also invoke an external function directly rather than inlining the function e.g define = "define_student()"inline code stringY
define_fnGenerates a DEFINE TABLE statement for the table. This overrides other specific definitions to prevent confusion and collision. Same as define attribute but expects the function name instead rather than invocation i.e define_student instead of define_student(). You can also invoke an external function directly rather than inlining the function e.g `define = "def

Node Attributes: Examples

Auto-Inferred Table Name

By default, the ORM auto-infers the table name from the struct's name. For a struct named Alien, the table name would be inferred as alien.

#![allow(unused)]
fn main() {
#[derive(Node, Serialize, Deserialize)]
pub struct Alien {
    id: SurrealSimpleId<Self>,
}
}

The corresponding table definition would be:

DEFINE TABLE alien;

Explicit Table Name

You can explicitly set the table name using the table attribute. By default, the table name should be the snake case of the struct name. This is to ensure consistency and uniqueness of table model struct. If you want a name other than the snake case version, you need to add the attribute - relax_table:

#![allow(unused)]
fn main() {
#[derive(Node, Serialize, Deserialize)]
#[orm(table = "student_test")]
pub struct StudentTest {
    id: SurrealSimpleId<Self>,
}
}

The corresponding table definition would be:

DEFINE TABLE student_test;

Using define for Inline Table Definition

The define attribute allows for inline table definitions, either through an inline expression or an invoked external function.

#![allow(unused)]
fn main() {
#[derive(Node, Serialize, Deserialize)]
#[orm(table = "student_test_4", as_ = "select(All).from(Student::table())", define = "define_student()")]
pub struct StudentTest4 {
    id: SurrealSimpleId<Self>,
}
}

Using define_fn for External Function Definition

Alternatively, the define_fn attribute points to an external function to define the table:

#![allow(unused)]
fn main() {
#[derive(Node, Serialize, Deserialize)]
#[orm(table = "student_test_7", define_fn = "define_student")]
pub struct StudentTest7 {
    id: SurrealSimpleId<Self>,
}
}

Specifying Permissions

The permissions attribute allows you to set granular permissions for CRUD operations. This takes Permissions struct. Therefore, if you are using an external function, it has to return Permissions which is then invoked and passed in:

#![allow(unused)]
fn main() {
#[derive(Node, Serialize, Deserialize)]
#[orm(table = "student_test_5", permissions = "student_permissions()")]
pub struct StudentTest5 {
    id: SurrealSimpleId<Self>,
}
}

In the example above, the student_permissions() function would define permissions using the for statement from Surreal orm. for returns Permissions.

Ensuring Valid Usage of Node Attributes

While node attributes are powerful and flexible, their misuse can lead to unexpected behaviors. Thankfully, the ORM actively checks for invalid usages and ensures that developers don't misuse these attributes. Here are some guidelines and checks enforced by the ORM to avoid pitfalls:

Conflicting Definitions:

  • define vs define_fn: Using both define and define_fn attributes on the same struct is not allowed . Only one should be present to define the table.
#![allow(unused)]
fn main() {
#[derive(Node, Serialize, Deserialize)]
#[orm(table = "student_test_6", define_fn = "define_student", define = "define_student()")]
pub struct StudentTest6 {
    id: SurrealSimpleId<Self>,
}
}

The ORM will raise an error for such definitions, ensuring clarity and preventing conflicts.


  • as vs as_fn: Only one of these should be used to define projections or views.

  • permissions vs permissions_fn: These attributes shouldn't coexist on the same struct, choose one based on your need.

  • value vs value_fn and assert vs assert_fn: Similar to the above, only one of these pairs should be present on a struct.

Avoid Excessive Attributes with define or define_fn:

When using define or define_fn, ensure no other attributes are present except table and relax_table.

Consistent Table Naming:

By default, the table name should be the snake case of the struct name. This is to ensure consistency and uniqueness of table model struct. If you want a name other than the snake case version, you need to add the attribute - relax_table.

Using Functions for Attributes:

When using attributes that invoke functions, such as define = "define_student()", ensure that the invoked function returns the appropriate type. For instance, define_student() should return a DefineStatement struct, and student_permissions() should return Permissions.

Conclusion

By following these guidelines and the checks enforced by the ORM, developers can ensure a smooth and error-free database definition process. Remember, while the ORM provides these checks, it's always a good practice for developers to validate and review their implementations to guarantee best practices and avoid potential pitfalls.

Chapter: Node Field Attributes

Table of Contents

  1. Introduction
  2. Basic Annotations
  3. Granular Attributes
  4. Defining Attributes with Functions
  5. Field Definitions
  6. Links and Relationships
  7. Customizing Behavior with Inline Expressions
  8. Invalid Usages
  9. Summary and Conclusion

1. Introduction

Field attributes in Surreal orm allow developers to fine-tune the behavior and characteristics of each field within a database node. As you've already seen in the table of attributes, each attribute serves a specific purpose. In this chapter, we'll delve deeper into each attribute, providing examples and clarifying common misconceptions.


Attributes Table

Field Attributes

AttributeDescriptionTypeOptional
renameRenames the field.stringY
link_oneSpecifies a relationship to a singular record in another node table in the database.model=NodeEdgeNode, connection ->edge->nodeY
link_selfSpecifies a relationship to a singular record in the same node table in the database.NodeY
link_manySpecifies a relationship to multiple records in another node table in the database.`Vec<S
relateGenerates the relation helpers for the Current Node struct to an edge and destination node. The corresponding field name is merely used as an alias in code generation and is read only and not serializable. e.g student:1->writes->book:2
typeSpecify the valid surrealdb field's type. One of any, array, bool, datetime, decimal, duration, float, int, number, object, string, record.surrealdb field typeY
assertAssert the field's value meets a certain criteria using the an filter using value() function as an operation (e.g value().is_not(NONE)) or in cond helper function for more complex filter assertion. e.g cond(value().is_not(NONE)).and(value().like("@codebreather")).inline code stringY
assert_fnProvide a function to assert the field's value meets a certain criteria. This is similar to assert but is intended for an already created external function which is useful when reusing an assertion e.g is_email.function name stringY
item_typeOnly when for nested array. Specifies the type of the items of the array.Option<FieldTypeWrapper>Y
item_assertOnly used for nested array. Asserts a condition on the content.Option<syn::LitStr>Y
item_assert_fnOnly used for nested array. Specifies the function to assert a condition on the content.Option<syn::Path>Y
defineGenerates a DEFINE FIELD statement for the table. This overrides other specific definitions to prevent confusion and collision. You can also invoke an external function directly rather than inlining the function e.g define = "define_age()"inline code stringY
define_fnGenerates a DEFINE FIELD statement for the table. This overrides other specific definitions to prevent confusion and collision. Same as define attribute but expects the function name instead rather than invocation i.e define_age instead of define_age(). You can also invoke an external function directly rather than inlining the function e.g `define = "def
skip_serializingWhen true, this field will be omitted when serializing the struct.boolY

2. Basic Annotations

Let's begin with a basic example. The Student struct below uses minimal annotations:

#![allow(unused)]
fn main() {
#[orm(table = "student")]
pub struct Student {
    id: SurrealId<Student, String>,
    first_name: String,
    last_name: String,
    age: u8,
}
}

Here:

  • table determines the name of the table in the database that corresponds to this struct.

3. Granular Attributes

For a more detailed configuration of a field, you can use granular attributes. The Student struct provides various usages:

#![allow(unused)]
fn main() {
#[orm(
    table = "student",
    permissions = "student_permissions()",
)]
pub struct Student {
    id: SurrealId<Student, String>,
    first_name: String,
    last_name: String,
    #[orm(
        type_ = "int",
        value = "18",
        assert = "cond(value().is_not(NONE)).and(value().gte(18))",
        permissions = "age_permissions()"
    )]
    age_inline_expr: u8,
    // ... other fields ...
}
}

Here:

  • type specifies the data type of the field in the database.
  • value sets a default value for the field.
  • assert provides a condition that the field value must satisfy.
  • permissions specifies what operations can be performed on the field and under what conditions.

4. Defining Attributes with Functions

You can externalize the logic for defining attributes by using external functions. This aids in reusability and cleaner code:

#![allow(unused)]
fn main() {
#[orm(
    table = "student_with_define_fn_attr",
    define_fn = "define_student_with_define_attr"
)]
pub struct StudentWithDefineFnAttr {
    // ... fields ...
    #[orm(type_ = "int", define_fn = "age_define_external_fn_path")]
    age_define_external_fn_path: u8,
}
}

Here:

  • define_fn allows you to specify an external function that returns the definition of the table or field.

5. Field Definitions

Fields can be defined in multiple ways using surreal_orm:

Inline Definitions:

#![allow(unused)]
fn main() {
#[orm(type_ = "int", value = "18")]
age: u8,
}

External Function Invoked:

#![allow(unused)]
fn main() {
#[orm(type_ = "int", value = "get_age_default_value()")]
age_default_external_function_invoked_expr: u8,
}

Using External Function Attributes:

#![allow(unused)]
fn main() {
#[orm(type_ = "int", value_fn = "get_age_default_value")]
age_external_fn_attrs: u8,
}

Mixing and Matching:

#![allow(unused)]
fn main() {
#[orm(type_ = "int", value = "get_age_default_value()", assert_fn = "get_age_assertion")]
age_mix_and_match_external_fn_inline_attrs: u8,
}

You can define relationships between different structs (representing tables in the database). Relationships can be one-to-one, one-to-many, or many-to-many.

For instance:

#![allow(unused)]
fn main() {
#[orm(link_one = "Book")]
fav_book: LinkOne<Book>,
}

This indicates a one-to-one relationship between a student and a book.


7. Customizing Behavior with Inline Expressions

In surreal_orm, you can use inline expressions to add custom behavior:

#![allow(unused)]
fn main() {
#[orm(
    type_ = "int",
    value = "get_age_by_group_default_value(AgeGroup::Teen)",
    assert = "get_age_assertion()",
)]
age_teen_external_function_invoked_expr: u8,
}

Here, the default value of age_teen_external_function_invoked_expr is determined by the get_age_by_group_default_value function with AgeGroup::Teen as a parameter.


8. Invalid Usages

When using surreal_orm, it's essential to be cautious about the attributes you combine. Certain combinations are considered invalid and will result in compilation errors.

1. Mixing value and value_fn:

These two attributes are mutually exclusive. You can't define a default value using both a direct expression and a function at the same time.

#![allow(unused)]
fn main() {
#[orm(
    type_ = "int",
    value = "get_age_default_value()",
    value_fn = "get_age_default_value"
)]
age: u8,
}

2. Mixing assert and assert_fn:

Similarly, you can't use both an inline assertion and an external function for the same purpose.

#![allow(unused)]
fn main() {
#[orm(
    type_ = "int",
    assert = "get_age_assertion()",
    assert_fn = "get_age_assertion"
)]
age: u8,
}

3. Mixing permissions and permissions_fn:

Permissions should be defined either inline or through an external function, but not both.

#![allow(unused)]
fn main() {
#[orm(
    type_ = "int",
    permissions = "age_permissions()",
    permissions_fn = "age_permissions"
)]
age: u8,
}

4. Combining define and define_fn:

These attributes are also mutually exclusive. When specifying a custom definition, you should use either an inline expression or an external function.

#![allow(unused)]
fn main() {
#[orm(
    type_ = "int",
    define = "define_age()",
    define_fn = "define_age"
)]
age: u8,
}

5. Using other attributes with define or define_fn:

When you use either the define or define_fn attribute, you cannot use any other attributes (except for type). This is because the definition provided should be comprehensive and not require additional modifiers.

For example, the following combinations are invalid:

#![allow(unused)]
fn main() {
#[orm(
    type_ = "int",
    value = "18",
    define = "define_age()"
)]
age: u8,
}
#![allow(unused)]
fn main() {
#[orm(
    type_ = "int",
    assert = "cond(value().is_not(NONE)).and(value().gte(18))",
    define = "define_age()"
)]
age: u8,
}
#![allow(unused)]
fn main() {
#[orm(
    type_ = "int",
    permissions = "for_permission([CrudType::Create, CrudType::Delete]).where_(StudentTest3::schema().firstName.is(\"Oyelowo\"))",
    define = "define_age()"
)]
age: u8,
}

By being aware of these restrictions and avoiding the invalid combinations, you can ensure that your code remains consistent, clear, and free from compilation errors.


9. Summary and Conclusion

With surreal_orm, you can easily map Rust structs to database tables, customize field properties, define relationships, and more. This provides a powerful way to interact with databases in a type-safe manner while keeping the codebase clean and maintainable.

For a hands-on illustration, consider the following code snippet which provides a comprehensive overview of the various annotations:

#![allow(unused)]
fn main() {
#[derive(Node, TypedBuilder, Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
#[orm(
    table = "student_with_granular_attributes",
    drop,
    schemafull,
    as_ = "select(All).from(Student::table())",
    permissions = "student_permissions()",
)]
pub struct StudentWithGranularAttributes {
    id: SurrealId<StudentWithGranularAttributes, String>,
    first_name: String,
    last_name: String,
    #[orm(
        type_ = "int",
        value = "18",
        assert = "cond(value().is_not(NONE)).and(value().gte(18))",
        permissions = "for_permission([CrudType::Create, CrudType::Delete]).where_(StudentWithGranularAttributes::schema().firstName.is(\"Oyelowo\"))"
    )]
    age_inline_expr: u8,
    // ... other fields ...
}
}

This chapter is a starting point to dive deeper into surreal_orm. With this foundation, you can explore more advanced features and best practices to make the most of this powerful ORM crate in Rust.

Edge

Edges in Surreal represent relationships between Nodes. They are useful when you want to model many-to-many relationships or when you want to store additional information about the relationship itself. Edges can be seen as "relationship tables" in a relational database context, holding metadata about the relationship between two entities. Edges are defined by a Rust struct that implements the Edge trait.

Here's a detailed example:

#[derive(Node, Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
#[orm(table = "alien")]
pub struct Alien {
    pub id: SurrealSimpleId<Self>,
    pub name: String,

    // This is a read-only field
    #[orm(relate(model = "AlienVisitsPlanet", connection = "->visits->planet"))]
    #[serde(skip_serializing, default)]
    pub planets_to_visit: Relate<Planet>,
}

#[derive(Node, Serialize, Deserialize, Debug, Clone, Default)]
#[serde(rename_all = "camelCase")]
#[orm(table = "planet")]
pub struct Planet {
    pub id: SurrealSimpleId<Self>,
    pub population: u64,
}

// Visits
#[derive(Edge, Serialize, Deserialize, Debug, Clone, Default)]
#[serde(rename_all = "camelCase")]
#[orm(table = "visits")]
pub struct Visits<In: Node, Out: Node> {
    pub id: SurrealSimpleId<Self>,
    #[serde(rename = "in")]
    pub in_: LinkOne<In>,
    pub out: LinkOne<Out>,
    pub time_visited: Duration,
}

// Connects Alien to Planet via Visits
pub type AlienVisitsPlanet = Visits<Alien, Planet>;

The Alien Node has a field planets_to_visit which is of type Relate<Planet>. This field doesn't represent a direct link from Alien to Planet. Instead, it represents an indirect relationship via the Visits Edge. This indirect relationship is defined by the Relate annotation on the planets_to_visit field in the Alien Node.

The #[orm(relate(model = "AlienVisitsPlanet", connection = "->visits->planet"))] attribute on the planets_to_visit field in the Alien Node tells Surreal that this field represents the Planet Nodes that are connected to the Alien Node via the AlienVisitsPlanet Edge. The connection = "->visits->planet" part defines the path of the relationship from the Alien Node, through the Visits Edge (represented by "visits"), and finally to the Planet Node.

The Visits Edge struct defines the structure of this relationship. It implements Edge and specifies two type parameters: In and Out which represent the source and target Node types of the relationship, respectively. In this example, Alien is the source and Planet is the target. The Visits Edge also has a time_visited field, which can store additional information about each visit.

In summary, Surreal Edges provide a flexible way to model complex relationships between Nodes, such as when an Alien visits a Planet. They allow for relationships to be modeled with additional information (like the time_visited field in the Visits Edge) and can represent both direct and indirect connections between Nodes.

Struct Attributes

AttributeDescriptionTypeOptional
rename_allRenames all the struct's fields according to the given case convention. The possible values are "lowercase", "UPPERCASE", "PascalCase", "camelCase", "snake_case", "SCREAMING_SNAKE_CASE".stringY
tableExplicitly define the table name. By default, it must correspond with the struct name in snake_case. Use relax_table if you want to opt out of this but not encouraged.OptionY
relax_tableDetermines whether the struct's name is matched to the table name as the snake case by default. This is not encouraged. Using your struct 1:1 to your database tables helps to ensure uniquness and prevent confusion.OptionY
schemafullMake the table enforce a schema struct.OptionY
dropDrop the table if it exists and create a new one with the same name.OptionY
asInline statement e.g select(All).from(user) for creating a projection using the DEFINE TABLE statement. This is useful for copying data from an existing table in the new table definition. This is similar to making a view in a RDBMS.A select statementY
as_fnSame as above as but defined as external function from the struct e.g select_reading_from_user for creating a projection using the DEFINE TABLE statement. This is useful for copying data from an existing table in the new table definition. This is similar to making a view in a RDBMS.A function nameY
permissionsSpecify permissions that apply to the table using the for statement.ForStatementY
permissions_fnSame as permission but as an external function from the struct. Specify permissions that apply to the table using the for statement.ForStatementY
defineGenerates a DEFINE TABLE statement for the table. This overrides other specific definitions to prevent confusion and collision. You can also invoke an external function directly rather than inlining the function e.g define = "define_student()"inline code stringY
define_fnGenerates a DEFINE TABLE statement for the table. This overrides other specific definitions to prevent confusion and collision. Same as define attribute but expects the function name instead rather than invocation i.e define_student instead of define_student(). You can also invoke an external function directly rather than inlining the function e.g `define = "def

Field Attributes

AttributeDescriptionTypeOptional
renameRenames the field.stringY
link_oneSpecifies a relationship to a singular record in another node table in the database.model=NodeEdgeNode, connection ->edge->nodeY
link_selfSpecifies a relationship to a singular record in the same node table in the database.NodeY
link_manySpecifies a relationship to multiple records in another node table in the database.`Vec<S
typeSpecify the valid surrealdb field's type. One of any, array, bool, datetime, decimal, duration, float, int, number, object, string, record.surrealdb field typeY
assertAssert the field's value meets a certain criteria using the an filter using value() function as an operation (e.g value().is_not(NONE)) or in cond helper function for more complex filter assertion. e.g cond(value().is_not(NONE)).and(value().like("@codebreather")).inline code stringY
assert_fnProvide a function to assert the field's value meets a certain criteria. This is similar to assert but is intended for an already created external function which is useful when reusing an assertion e.g is_email.function name stringY
item_typeOnly when for nested array. Specifies the type of the items of the array.Option<FieldTypeWrapper>Y
item_assertOnly used for nested array. Asserts a condition on the content.Option<syn::LitStr>Y
item_assert_fnOnly used for nested array. Specifies the function to assert a condition on the content.Option<syn::Path>Y
defineGenerates a DEFINE FIELD statement for the table. This overrides other specific definitions to prevent confusion and collision. You can also invoke an external function directly rather than inlining the function e.g define = "define_age()"inline code stringY
define_fnGenerates a DEFINE FIELD statement for the table. This overrides other specific definitions to prevent confusion and collision. Same as define attribute but expects the function name instead rather than invocation i.e define_age instead of define_age(). You can also invoke an external function directly rather than inlining the function e.g `define = "def
skip_serializingWhen true, this field will be omitted when serializing the struct.boolY

Object

In Surreal, an Object is a complex nested data structure that can be embedded within Nodes, modeled by the Object trait in Rust. Unlike Nodes, which represent database tables, Objects do not represent tables on their own. However, they are crucial in modeling more complex data within a Node. They can be used directly as a field type or as an element within an array, enabling you to encapsulate and manage more intricate data structures within your database models.

Here's an example of a node named Alien that has a nested Rocket object and an array of Rocket objects:

#![allow(unused)]
fn main() {
use serde::{Deserialize, Serialize};
use surreal_orm::{SurrealSimpleId, Node};

#[derive(Node, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[orm(table = "alien")]
pub struct Alien {
    pub id: SurrealSimpleId<Self>,

    #[orm(nest_object = "Rocket")]
    pub favorite_rocket: Rocket,

    #[orm(nest_array = "Rocket")]
    pub strong_rockets: Vec<Rocket>,
}

#[derive(Object, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Rocket {
    pub name: String,
    pub strength: u64,
}
}

Objects in Surreal can be used in two ways: as nested objects (nest_object) and as arrays of nested objects (nest_array). For instance, in an Alien Node, a Rocket Object can be a single favorite rocket (nest_object) or a collection of strong rockets (nest_array). This powerful feature allows for more complex nested data to be directly embedded in your models, thus offering a more nuanced representation of real-world entities in your database.

Notably, the use of nest_object or nest_array is validated at compile time. This ensures that nest_object is used correctly for the specific Object and nest_array corresponds to a vector of that Object, providing a guarantee of the validity of your data structures before your program runs.

Struct Attributes

AttributeDescriptionTypeOptional
rename_allRenames all the struct's fields according to the given case convention. The possible values are "lowercase", "UPPERCASE", "PascalCase", "camelCase", "snake_case", "SCREAMING_SNAKE_CASE".stringY

Field Attributes

AttributeDescriptionTypeOptional
renameRenames the field.stringY

Record Ids

The SurrealId is a wrapper struct that extends the capabilities of surrealdb::sql::Thing and provides a more ergonomic interface. It's a static type representing the id of a model in the Surreal ORM and is a combination of the model's table name and the id, where the id can be anything that can be converted into a surrealdb::sql::Id.

Let's explore how to utilize these ID types both implicitly (through auto-generation via the Default trait) and explicitly (by creating them manually).

  1. SurrealSimpleId:

This ID type auto-generates a unique identifier when a new instance of the struct is created, thanks to the implementation of the Default trait. But you can also manually generate it using the create_simple_id() function directly on the struct.

Example struct:

#![allow(unused)]
fn main() {
#[derive(Node, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase")]
#[orm(table = "alien")]
pub struct Alien {
    pub id: SurrealSimpleId<Self>,
    // other fields
}
}

Creating an instance of Alien with an auto-generated ID (implicit):

#![allow(unused)]
fn main() {
let alien = Alien {
    // other fields
    ..Default::default()
};
}

Creating an instance of Alien with a manually generated ID (explicit):

#![allow(unused)]
fn main() {
let alien = Alien {
    id: Alien::create_simple_id(),
    // other fields
};
}
  1. SurrealUuid:

SurrealUuid<Self> auto-generates a UUID when a new instance of the struct is created. You can also manually generate it using the create_uuid() function on the struct.

Example struct:

#![allow(unused)]
fn main() {
#[derive(Node, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase")]
#[orm(table = "account")]
pub struct Account {
    pub id: SurrealUuid<Self>,
    // other fields
}
}

Creating an instance of Account with an auto-generated UUID (implicit):

#![allow(unused)]
fn main() {
let account = Account {
    // other fields
    ..Default::default()
};
}

Creating an instance of Account with a manually generated UUID (explicit):

#![allow(unused)]
fn main() {
let account = Account {
    id: Account::create_uuid(),
    // other fields
};
}
  1. SurrealUlid:

SurrealUlid<Self> auto-generates a ULID when a new instance of the struct is created. You can also manually generate it using the create_ulid() function on the struct.

Example struct:

#![allow(unused)]
fn main() {
#[derive(Node, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase")]
#[orm(table = "spaceship")]
pub struct SpaceShip {
    pub id: SurrealUlid<Self>,
    // other fields
}
}

Creating an instance of SpaceShip with an auto-generated ULID (implicit):

#![allow(unused)]
fn main() {
let spaceship = SpaceShip {
    // other fields
    ..Default::default()
};
}

Creating an instance of SpaceShip with a manually generated ULID (explicit):

#![allow(unused)]
fn main() {
let spaceship = SpaceShip {
    id: SpaceShip::create_ulid(),
    // other fields
};
}
  1. SurrealId<Self, T>:

This is the most flexible ID type, allowing for any arbitrary serializable type T as the ID. However, it doesn't implement the Default trait, which means you must manually create instances of this type using the create_id() function.

Example struct:

#![allow(unused)]
fn main() {
#[derive(Node, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[orm(table = "weapon")]
pub struct Weapon {
    pub id: SurrealId<Self, String>,
    // other fields
}
}

Creating an

instance of Weapon with a manually created ID (explicit):

#![allow(unused)]
fn main() {
let weapon = Weapon {
    id: Weapon::create_id("sword".into()),
    // other fields
};
}

These ID types provide various options for users to meet the needs of different scenarios when working with entities in SurrealDB. Whether you want auto-generated identifiers or prefer to create them manually, there's an ID type to suit your requirements.

The SurrealID types in SurrealDB are designed to be flexible and accommodating to various needs for entity identification and linking within the database.

Model Schema

This guide covers the SchemaGetter trait in SurrealDB, a Rust crate, and provides examples on how to use it.

The SchemaGetter Trait

This trait is defined as follows:

#![allow(unused)]
fn main() {
pub trait SchemaGetter {
    type Schema;
    fn schema() -> Self::Schema;
    fn schema_prefixed(prefix: impl Into<ValueLike>) -> Self::Schema;
}
}

This trait is used for defining schemas for different entities in your database. It contains two associated functions:

  1. schema(): Returns a schema for an entity. This is used for defining the structure and constraints of the entity.
  2. schema_prefixed(prefix: impl Into<ValueLike>): This is similar to schema(), but it allows the schema to be prefixed with a custom value. This can be useful when working with entities that may share similar fields but have different schemas.

The SchemaGetter trait's primary use is to allow types to be used as a 'Schema' - a representation of the structure of the data you're storing or retrieving from the database. It's particularly useful in constructing complex queries with strong type safety.

Examples

The examples below demonstrate the different methods you can utilize in SurrealDB, leveraging the SchemaGetter trait:

Creating and Retrieving Entities:

This piece of code uses the schema() function of SchemaGetter to create and retrieve entities in the database:

#![allow(unused)]
fn main() {
let _simple_relation = Student::schema()
    .writes__(Empty)
    .book(Book::schema().id.equal(Thing::from(("book", "blaze"))))
    .title;
}

This creates a relation between the Student and Book entities. It uses the writes__ method to create a relation indicating the Student writes a Book. The book call then specifies that the book's id equals a specific Thing entity.

Pattern Selection:

SurrealDB also allows the pattern-like selection of entities:

#![allow(unused)]
fn main() {
let student_id = Student::create_id("oyelowo");
let book_id = Book::create_id("2");
let likes = StudentLiksBook::table();
let writes = StudentWritesBook::table();
let writes::Schema { timeWritten, .. } = StudentWritesBook::schema();

let aliased_connection = Student::with(student_id)
    .writes__(Empty)
    .writes__(Empty)
    .writes__(any_other_edges(&[writes, likes]).where_(timeWritten.less_than_or_equal(50)))
    .book(book_id)
    .__as__(Student::aliases().writtenBooks);
}

In this case, we are selecting all the books that a specific student wrote where the timeWritten is less than or equal to 50. This query is an example of how you can combine different methods and concepts provided by SurrealDB to form complex, yet understandable, queries.

Modifying and Updating Entities:

The following example illustrates how to modify and update entities:

#![allow(unused)]
fn main() {
let ref id = created_weapon.clone().id;
let weapon::Schema { strength, .. } = Weapon::schema();

update::<Weapon>(id)
    .set(strength.increment_by(5u64))
    .run(db.clone())
    .await?;

let updated = update::<Weapon>(id)
    .set(strength.decrement_by(2u64))
    .return_one(db.clone())
    .await?;

let selected: Option<Weapon> = select(All)
    .from(Weapon::table())
    .return_one(db.clone())
    .await?;
assert_eq!(updated.unwrap().strength, 8);
assert_eq!(selected.unwrap().strength, 8);
}

Links, Nestings and Relations

  • link_one: It is an attribute used to define a one-to-one relationship between two Nodes. For example, consider the Alien struct with the field weapon:
#![allow(unused)]
fn main() {
use surreal_orm::{Serialize, Deserialize,LinkOne, SurrealSimpleId, Node};

#[derive(Node, Serialize, Deserialize, Debug)]
#[orm(table = "alien")]
pub struct Alien {
    pub id: SurrealSimpleId<Self>,

    // #[orm(link_one = "Weapon", type_ = "record(weapon)")]
    #[orm(link_one = "Weapon")]
    pub best_weapon: LinkOne<Weapon>,
}


#[derive(Node, Serialize, Deserialize, Debug)]
#[orm(table = "weapon")]
pub struct Weapon {
    pub id: SurrealSimpleId<Self>,
}
}

This attribute indicates that an Alien can have a single best Weapon. The relationship is represented by a foreign key in the database table, and the type attribute specifies the database type for the relationship.

  • link_many: It is an attribute used to define a one-to-many relationship between two Nodes. For instance, in the Alien struct, we have the space_ships field:
#![allow(unused)]
fn main() {
use surreal_orm::{Serialize, Deserialize, LinkMany, SurrealSimpleId, Node};

#[derive(Node, Serialize, Deserialize, Debug)]
#[orm(table = "alien")]
pub struct Alien {
    pub id: SurrealSimpleId<Self>,
    // #[orm(link_many = "SpaceShip", type_ = "array", item_type = "record(space_ship)")]
    #[orm(link_many = "SpaceShip")]
    pub space_ships: LinkMany<SpaceShip>,
}

#[derive(Node, Serialize, Deserialize, Debug)]
#[orm(table = "space_ship")]
pub struct SpaceShip {
    pub id: SurrealSimpleId<Self>,
}
}

This attribute indicates that an Alien can have multiple SpaceShip instances associated with it. The relationship is represented by a foreign key or a join table in the database, and the type attribute specifies the database type for the relationship.

  • nest_object: It is an attribute used to embed a single Object within a Node. In the Alien struct, we have the weapon field:
#![allow(unused)]
fn main() {
use surreal_orm::{Serialize, Deserialize,SurrealSimpleId, Node, Object};

#[derive(Node, Serialize, Deserialize, Debug)]
#[orm(table = "alien")]
pub struct Alien {
    pub id: SurrealSimpleId<Self>,
    #[orm(nest_object = "Rocket")]
    pub favorite_rocket: Rocket,
}

#[derive(Object, Serialize, Deserialize, Debug)]
#[orm(table = "rocket")]
pub struct Rocket {
}
}

This attribute specifies that an Alien2 has a nested Rocket object representing its weapon. The Rocket object is stored as part of the Alien2 Node in the database.

  • nest_array: It is an attribute used to embed multiple Objects within a Node. Although not explicitly used in the provided code examples, it would be similar to NestObject, but with a collection type field (e.g., Vec<Rocket>).
#![allow(unused)]
fn main() {
use surreal_orm::{Serialize, Deserialize,SurrealSimpleId, Node, Object};

#[derive(Node, Serialize, Deserialize, Debug)]
#[orm(table = "alien")]
pub struct Alien {
    pub id: SurrealSimpleId<Self>,

    #[orm(nest_array = "Rocket")]
    pub big_rockets: Vec<Rocket>,
}

#[derive(Node, Serialize, Deserialize, Debug)]
#[orm(table = "rocket")]
pub struct Rocket {
}
}
  • relate: It is an attribute used to define a read-only relationship between two Nodes. In the Alien struct, we have the planets_to_visit field:
#![allow(unused)]
fn main() {
use surreal_orm::{Serialize, Deserialize, SurrealSimpleId, Node, Edge, Relate};

#[derive(Node, Serialize, Deserialize, Debug)]
#[orm(table = "alien")]
pub struct Alien {
    pub id: SurrealSimpleId<Self>,

    #[orm(relate(model = "AlienVisitsPlanet", connection = "->visits->planet"))]
    #[serde(skip_serializing, default)]
    pub planets_to_visit: Relate<Planet>,
}

#[derive(Node, Serialize, Deserialize, Debug, Clone, Default)]
#[serde(rename_all = "camelCase")]
#[orm(table = "planet")]
pub struct Planet {
    pub id: SurrealSimpleId<Self>,
    pub population: u64,
}

// Visits
#[derive(Edge, Serialize, Deserialize, Debug, Clone, Default)]
#[serde(rename_all = "camelCase")]
#[orm(table = "visits")]
pub struct Visits<In: Node, Out: Node> {
    pub id: SurrealSimpleId<Self>,
    #[serde(rename = "in")]
    pub in_: LinkOne<In>,
    pub out: LinkOne<Out>,
    pub time_visited: Duration,
}

// Connects Alien to Planet via Visits
pub type AlienVisitsPlanet = Visits<Alien, Planet>;
}

This attribute specifies that an Alien has a read-only relationship with Planet through the AlienVisitsPlanet model. The connection attribute describes the relationship path between the Nodes. The relationship is read-only because the serde(skip_serializing) attribute is used to prevent it from being serialized.

These attributes provide additional information to Surreal for modeling relationships and embedding Objects within Nodes, allowing for more complex and flexible database schema designs.

Field Traversal

The surreal_orm library equips developers with powerful field traversal capabilities, allowing for seamless querying and navigation through the surrealdb graph database. This chapter provides an in-depth exploration into the different traversal methods available and how to harness them effectively.

Basics of Field Traversal

Field traversal is the mechanism used to navigate through a data structure, pinpointing specific fields or relationships. The design of the surreal_orm makes traversal not only intuitive but also direct, offering methods to navigate fields, relationships, and even to apply specific conditions.

To get started, let's set up our environment:

#![allow(unused)]
fn main() {
use pretty_assertions::assert_eq;
use surreal_models::{student, Student};
use surreal_orm::{index, this, where_, All, Buildable, Operatable, SchemaGetter, ToRaw, E};
}

Root Object: The Starting Point

Every traversal starts with the root object. The this() function is your gateway, representing the current node or object you're working on.

#![allow(unused)]
fn main() {
fn basic() {
let param_with_path = this();
assert_eq!(param_with_path.to_raw().build(), "$this");
}
}

In the code snippet above, the this() function signifies a reference to the root object or the primary context of the traversal. When executed, this will produce "$this".

Traversing the Path

Navigating relationships between nodes is where the real power of a graph database shines. The with_path::<T>(index_or_clause) method allows you to specify this path. Here T is the type of node you're targeting, while index_or_clause can either be an index or a clause, such as WHERE age > 18 or E (an alias for Empty).

For instance, to get the firstName of a Student at index 2:

#![allow(unused)]
fn main() {
let param_with_path = this().with_path::<Student>([2]).firstName;
}

This traversal, when executed, will output "$this[2].firstName".

Direct Field Access within an Object

Sometimes, all you want is to directly access a field within an object. Here's how you can achieve that:

#![allow(unused)]
fn main() {
fn test_param_simple_clause() {
let param_with_path = this().with_path::<Student>(E).lastName;
assert_eq!(param_with_path.to_raw().build(), "$this.lastName");
}
}

In this example, the alias E (standing for Empty) is employed to directly traverse to the lastName field of the Student object.

Direct Field Access within an Array

At other times, you might want to directly access a field within an array:

#![allow(unused)]
fn main() {
fn test_param_with_path_simple() {
let param_with_path = this().with_path::<Student>([2]).firstName;
assert_eq!(param_with_path.to_raw().build(), "$this[2].firstName");
}
}

Here, the code fetches the firstName of the Student located at index 2.

Deep Relationship Traversal

The true essence of a graph database is revealed when traversing deep relationships. Consider this test:

#![allow(unused)]
fn main() {
fn test_param_with_path() {
let param_with_path = this()
    .with_path::<Student>([2])
    .bestFriend()
    .bestFriend()
    .course()
    .title;
assert_eq!(param_with_path.to_raw().build(), "$this[2].bestFriend.bestFriend.course.title");
}
}

This test showcases how to navigate through a Student's best friend's best friend's course title.

Index function for Indexing

An alternate to square bracket notation [2] is the index helper function e.g(index(2)):

#![allow(unused)]
fn main() {
fn test_param_with_path_with_index_square_bracket_variation() {
let param_with_path = this()
    .with_path::<Student>(index(2))
    .bestFriend()
    .bestFriend()
    .course()
    .title;
assert_eq!(param_with_path.to_raw().build(), "$this[2].bestFriend.bestFriend.course.title");
}
}

Traversal with Conditional Clauses

You can also traverse paths with conditions, allowing for more refined querying:

#![allow(unused)]
fn main() {
fn test_param_with_path_with_clause() {
let student::Schema { age, .. } = Student::schema();
let param_with_path = this()
    .with_path::<Student>(where_(age.greater_than(18)))
    .bestFriend()
    .allSemesterCourses([5])
    .title;
assert_eq!(param_with_path.to_raw().build(), "$this[WHERE age > 18].bestFriend.allSemesterCourses[5].title");
}
}

This traversal fetches the title of the fifth semester course of the best friends of students older than 18.

Using the All Wildcard

For scenarios where you want to traverse all items or elements of a certain relationship or field, the All wildcard is invaluable:

#![allow(unused)]
fn main() {
fn test_param_with_path_with_all_wildcard() {
let param_with_path = this()
    .with_path::<Student>(All)
    .bestFriend()
    .allSemesterCourses([5])
    .title;
assert_eq!(param_with_path.to_raw().build(), "$this[*].bestFriend.allSemesterCourses[5].title");
}
}

In the traversal above, All is a wildcard that represents every instance of the Student type. The traversal then specifies the fifth course title of all students' best friends.

Multiple Indexes in Path

There are scenarios where traversing multiple indexed fields or relationships becomes necessary:

#![allow(unused)]
fn main() {
fn test_param_with_path_multiple_indexes() {
let param_with_path = this()
    .with_path::<Student>([2])
    .bestFriend()
    .allSemesterCourses([5])
    .title;
assert_eq!(param_with_path.to_raw().build(), "$this[2].bestFriend.allSemesterCourses[5].title");
}
}

Here, the traversal first targets the Student at index 2 and then fetches the title of the fifth semester course of that student's best friend.


Conclusion

Field traversal in surreal_orm equips developers with a versatile and powerful toolset, enabling effective navigation and querying within

Loaders

Loaders in the Surreal ORM are functions that fetch different kinds of related records (links) from the database. These loaders provide different ways of handling these related records, based on their type and their existence in the database. Here, we discuss some of the "load" types that are part of the ReturnableStandard trait.

The load_links function sets the return type to projections and fetches all record links. It defaults values to null for referenced records that do not exist.

For instance, if you have a User model that has a Posts link (i.e., each User can have multiple Posts), you can use load_links to fetch all the Posts linked to a User. If a Post does not exist, the function defaults its value to null.

#![allow(unused)]
fn main() {
let user = User::find(1).load_links(vec!["posts"]).unwrap();
}
#![allow(unused)]
fn main() {
    let db = Surreal::new::<Mem>(()).await.unwrap();
    db.use_ns("test").use_db("test").await.unwrap();

    #[derive(Node, Serialize, Deserialize, Debug, Clone)]
    #[serde(rename_all = "camelCase")]
    #[orm(table = "alien")]
    pub struct Alien {
        pub id: SurrealSimpleId<Self>,

        #[orm(link_self = "Alien")]
        pub ally: LinkSelf<Alien>,

        #[orm(link_one = "Weapon")]
        pub weapon: LinkOne<Weapon>,

        // Again, we dont have to provide the type attribute, it can auto detect
        #[orm(link_many = "SpaceShip")]
        pub space_ships: LinkMany<SpaceShip>,

        // This is a read only field
        #[orm(relate(model = "AlienVisitsPlanet", connection = "->visits->planet"))]
        #[serde(skip_serializing, default)]
        pub planets_to_visit: Relate<Planet>,
    }

    #[derive(Node, Serialize, Deserialize, Debug, Clone, Default)]
    #[serde(rename_all = "camelCase")]
    #[orm(table = "weapon")]
    pub struct Weapon {
        pub id: SurrealSimpleId<Self>,
        pub name: String,
        // pub strength: u64,
        #[orm(type_ = "int")]
        pub strength: Strength,
        pub created: DateTime<Utc>,
        #[orm(nest_object = "Rocket")]
        pub rocket: Rocket,
    }
    type Strength = u64;


    #[derive(Node, Serialize, Deserialize, Debug, Clone)]
    #[serde(rename_all = "camelCase")]
    #[orm(table = "space_ship")]
    pub struct SpaceShip {
        pub id: SurrealId<Self, String>,
        pub name: String,
        pub created: DateTime<Utc>,
    }

    let weapon = || Weapon {
        name: "Laser".to_string(),
        created: Utc::now(),
        ..Default::default()
    };
    let weapon1 = weapon();
    let weapon2 = weapon();

    let space_ship = SpaceShip {
        id: SpaceShip::create_id("gbanda".into()),
        name: "SpaceShip1".to_string(),
        created: Utc::now(),
    };

    let space_ship2 = SpaceShip {
        id: SpaceShip::create_id("halifax".into()),
        name: "SpaceShip2".to_string(),
        created: Utc::now(),
    };

    let space_ship3 = SpaceShip {
        id: SpaceShip::create_id("alberta".into()),
        name: "Oyelowo".to_string(),
        created: Utc::now(),
    };

    assert_eq!(weapon1.clone().id.to_thing().tb, "weapon");

    // create first record to weapon table
    let created_weapon = create()
        .content(weapon1.clone())
        .get_one(db.clone())
        .await?;
    assert_eq!(created_weapon.id.to_thing(), weapon1.id.to_thing());

    let select1: Vec<Weapon> = select(All)
        .from(Weapon::table())
        .return_many(db.clone())
        .await?;
    // weapon table should have one record
    assert_eq!(select1.len(), 1);

    //  Create second record
    let created_weapon = create()
        .content(weapon2.clone())
        .return_one(db.clone())
        .await?;

    let select2: Vec<Weapon> = select(All)
        .from(Weapon::table())
        .return_many(db.clone())
        .await?;
    // weapon table should have two records after second creation
    assert_eq!(select2.len(), 2);

    let created_spaceship1 = create()
        .content(space_ship.clone())
        .get_one(db.clone())
        .await?;
    let created_spaceship2 = create()
        .content(space_ship2.clone())
        .get_one(db.clone())
        .await?;
    let created_spaceship3 = create()
        .content(space_ship3.clone())
        .get_one(db.clone())
        .await?;

    let point = point! {
        x: 40.02f64,
        y: 116.34,
    };

    let territory = line_string![(x: 40.02, y: 116.34), (x: 40.02, y: 116.35), (x: 40.03, y: 116.35), (x: 40.03, y: 116.34), (x: 40.02, y: 116.34)];
    let polygon = polygon![(x: 40.02, y: 116.34), (x: 40.02, y: 116.35), (x: 40.03, y: 116.35), (x: 40.03, y: 116.34), (x: 40.02, y: 116.34)];
    let unsaved_alien = Alien {
        id: Alien::create_simple_id(),
        ally: LinkSelf::null(),
        weapon: LinkOne::from(created_weapon.unwrap()),
        space_ships: LinkMany::from(vec![
            created_spaceship1.clone(),
            created_spaceship2.clone(),
            created_spaceship3.clone(),
        ]),
        planets_to_visit: Relate::null(),
    };

    assert!(unsaved_alien.weapon.get_id().is_some());
    assert!(unsaved_alien.weapon.value().is_none());

    // Check fields value fetching
    let alien::Schema { weapon, .. } = Alien::schema();
    let created_alien = create()
        .content(unsaved_alien.clone())
        .load_links(vec![weapon])?
        .get_one(db.clone())
        .await?;

    let ref created_alien = created_alien.clone();
    // id is none  because ally field is not created.
    assert!(created_alien.ally.get_id().is_none());
    // .value() is None because ally is not created.
    assert!(created_alien.ally.value().is_none());

    // Weapon is created at weapon field and also loaded.
    // get_id  is None because weapon is loaded.
    assert!(created_alien.weapon.get_id().is_none());
    // .value() is Some because weapon is loaded.
    assert!(created_alien.weapon.value().is_some());

    // Spaceships created at weapon field and also loaded.
    assert_eq!(created_alien.space_ships.is_empty(), false);

    assert_eq!(created_alien.space_ships.len(), 3);
    assert_eq!(
        created_alien
            .space_ships
            .iter()
            .map(|x| x.get_id().unwrap().to_string())
            .collect::<Vec<_>>(),
        vec![
            created_spaceship1.id.to_string(),
            created_spaceship2.id.to_string(),
            created_spaceship3.id.to_string(),
        ]
    );



    let created_alien_with_fetched_links = create()
        .content(unsaved_alien.clone())
        .load_link_manys()?
        .return_one(db.clone())
        .await?;

    let ref created_alien_with_fetched_links = created_alien_with_fetched_links.unwrap();
    let alien_spaceships = created_alien_with_fetched_links.space_ships.values();

    assert_eq!(created_alien_with_fetched_links.space_ships.keys().len(), 3);
    assert_eq!(
        created_alien_with_fetched_links
            .space_ships
            .keys_truthy()
            .len(),
        0
    );
}

The load_all_links function sets the return type to projections and fetches all record link values. For link_one and link_self types, it returns null if the link is null or if the reference does not exist. For link_many type, it returns None for items that are null or the references that do not exist.

Assume you have a User model with link_one type Profile, link_self type Friends, and link_many type Posts. You can use load_all_links to fetch all these linked records.

#![allow(unused)]
fn main() {
let user = User::find(1).load_all_links().unwrap();
}

The load_link_manys function sets the return type to projections and fetches all record link values for link_many fields, including the null record links. So, if a User has multiple Posts, this function fetches all Posts including the ones that are null.

#![allow(unused)]
fn main() {
let user = User::find(1).load_link_manys().unwrap();
}

The load_link_ones function sets the return type to projections and fetches all record link values for link_one fields. It defaults to null if the reference does not exist.

#![allow(unused)]
fn main() {
let user = User::find(1).load_link_ones().unwrap();
}

The load_line_selfs function sets the return type to projections and fetches all record link values for link_self fields. It defaults to null if the reference does not exist.

#![allow(unused)]
fn main() {
let user = User::find(1).load_line_selfs().unwrap();
}

In conclusion, loaders provide a flexible way to handle linked records in your database. Whether you want to fetch all links, fetch links of a specific type, or handle null references in a certain way, loaders have got you covered. They are a powerful tool in the Surreal ORM, simplifying complex database operations.

Return Types

Return types in the Surreal ORM define how database operations result in returned data. They allow the specification of the format and structure of the data returned after running a database operation. In this chapter, we will discuss some of the "return" types that are part of the ReturnableStandard trait.

return_one

The return_one function runs a statement against the database and returns a single result. If the result contains more than one record, it throws an error.

Consider a scenario where you want to fetch a single user from your database. You can use return_one to get the User record:

#![allow(unused)]
fn main() {
let user = User::find(1).return_one(db).await.unwrap();
}

return_many

The return_many function runs a statement against the database and returns multiple results.

For instance, if you want to fetch all users from your database, you can use return_many to get the User records:

#![allow(unused)]
fn main() {
let users = User::all().return_many(db).await.unwrap();
}

return_none

The return_none function runs a statement against the database and returns no result.

This is particularly useful when you perform operations that don't require a return value. For example, deleting a user:

#![allow(unused)]
fn main() {
User::delete(1).return_none(db).await.unwrap();
}

return_first

The return_first function runs a statement against the database and returns the first result.

For example, to get the first user in the database:

#![allow(unused)]
fn main() {
let user = User::all().return_first(db).await.unwrap();
}

return_many_before

The return_many_before function runs a statement against the database and returns the many results before the change.

This is useful when you want to compare the state of the records before and after a database operation. For instance, updating a user's profile:

#![allow(unused)]
fn main() {
let users_before_update = User::all().return_many_before(db).await.unwrap();
User::update(1, new_profile_data).return_none(db).await.unwrap();
}

In conclusion, return types provide a way to control the data returned by database operations. Whether you want a single record, multiple records, the first record, or no record at all, return types allow you to specify the outcome.

Helper Methods in surreal_orm

The surreal_orm library offers a set of utility functions encapsulated in the SurrealCrud and SurrealCrudNode traits. These methods provide a high-level abstraction over raw database statements, simplifying CRUD operations.

Preparations

Before we dive into the helper methods, let's set up our environment:

#![allow(unused)]
fn main() {
use surreal_models::{space_ship, weapon, SpaceShip, Weapon};
use surreal_orm::{
    statements::{insert, select, select_value},
    *,
};
use surrealdb::{
    engine::local::{Db, Mem},
    Surreal,
};

async fn create_test_data(db: Surreal<Db>) {
    let space_ships = (0..1000)
        .map(|i| Weapon {
            name: format!("weapon-{}", i),
            strength: i,
            ..Default::default()
        })
        .collect::<Vec<Weapon>>();
    insert(space_ships).run(db.clone()).await.unwrap();
}
}

1. save Method

The save method can either create a new record or update an existing one in the database. You can think of it as an upsert method.

#![allow(unused)]
fn main() {
#[tokio::test]
async fn test_save() -> SurrealOrmResult<()> {
    let db = Surreal::new::<Mem>(()).await.unwrap();
    db.use_ns("test").use_db("test").await.unwrap();

    let ss_id = SpaceShip::create_id("num-1".into());
    let spaceship = SpaceShip {
        id: ss_id.clone(),
        name: "spaceship-1".into(),
        created: chrono::Utc::now(),
    };

    let spaceship = spaceship.save().get_one(db.clone()).await?;

    let saved_spaceship = SpaceShip::find_by_id(ss_id.clone())
        .get_one(db.clone())
        .await?;

    assert_eq!(spaceship.id.to_thing(), saved_spaceship.id.to_thing());
    assert_eq!(spaceship.name, saved_spaceship.name);
    Ok(())
}
}

2. find_by_id Method

Retrieve a record by its ID:

#![allow(unused)]
fn main() {
#[tokio::test]
async fn test_find_by_id() -> SurrealOrmResult<()> {
    let db = Surreal::new::<Mem>(()).await.unwrap();
    db.use_ns("test").use_db("test").await.unwrap();

    let spaceship = SpaceShip {
        id: SpaceShip::create_id("num-1".into()),
        name: "spaceship-1".into(),
        created: chrono::Utc::now(),
    };

    spaceship.clone().save().run(db.clone()).await?;

    let found_spaceship = SpaceShip::find_by_id(spaceship.id.clone())
        .get_one(db.clone())
        .await?;

    assert_eq!(spaceship.id.to_thing(), found_spaceship.id.to_thing());
    Ok(())
}
}

3. find_where Method

Retrieve records based on specific conditions:

#![allow(unused)]
fn main() {
#[tokio::test]
async fn test_find_where() -> SurrealOrmResult<()> {
    let db = Surreal::new::<Mem>(()).await.unwrap();
    db.use_ns("test").use_db("test").await.unwrap();

    let spaceship = SpaceShip {
        id: SpaceShip::create_id("num-1".into()),
        name: "spaceship-1".into(),
        created: chrono::Utc::now(),
    };
    let _spaceship2 = SpaceShip {
        id: SpaceShip::create_id("num-2".into()),
        name: "spaceship-2".into(),
        created: chrono::Utc::now(),
    }
    .save()
    .run(db.clone())
    .await?;

    let _spaceschip = spaceship.clone().save().get_one(db.clone()).await?;
    let space_ship::Schema { name, id, .. } = SpaceShip::schema();

    let found_spaceships = SpaceShip::find_where(id.is_not(NULL))
        .return_many(db.clone())
        .await?;
    assert_eq!(found_spaceships.len(), 2);

    let found_spaceships = SpaceShip::find_where(name.equal("spaceship-1"))
        .return_many(db.clone())
        .await?;

    assert_eq!(found_spaceships.len(), 1);
    assert_eq!(found_spaceships[0].id.to_thing(), spaceship.id.to_thing());

    let found_spaceship = SpaceShip::find_where(name.equal("spaceship-1"))
        .get_one(db.clone())
        .await?;

    assert_eq!(found_spaceship.id.to_thing(), spaceship.id.to_thing());
    Ok(())
}
}

4. count_where Method

Count records based on specific conditions:

#![allow(unused)]
fn main() {
#[tokio::test]
async fn test_count_where() -> SurrealOrmResult<()> {
    let db = Surreal::new::<Mem>(()).await.unwrap();
    db.use_ns("test").use_db("test").await.unwrap();

    create_test_data(db.clone()).await;
    let weapon::Schema { strength, .. } = &Weapon::schema();

    let weapons_query = Weapon::count_where(strength.gte(500));
    let weapons_count = weapons_query.get(db.clone()).await?;

    assert_eq!(
        weapons_query.to_raw().build(),
        "SELECT VALUE count FROM (SELECT count(strength >= 500) FROM weapon GROUP ALL);"
    );

    assert_eq!(weapons_count, 500);

    Ok(())
}
}

5. count_all Method

Count all records:

#![allow(unused)]
fn main() {
#[tokio::test]
async fn test_count_all() -> SurrealOrmResult<()> {
    let db = Surreal::new::<Mem>(()).await.unwrap();
    db.use_ns("test").use_db("test").await.unwrap();

    create_test_data(db.clone()).await;

    let weapons_query = Weapon::count_all();
    let weapons_count = weapons_query.get(db.clone()).await?;

    assert_eq!(
        weapons_query.to_raw().build(),
        "SELECT VALUE count FROM (SELECT count() FROM weapon GROUP ALL);"
    );

    assert_eq!(weapons_count, 1000);

    Ok(())
}
}

6. delete Method

This method deletes the current record instance from the database.

#![allow(unused)]
fn main() {
#[tokio::test]
async fn test_delete() -> SurrealOrmResult<()> {
    let db = Surreal::new::<Mem>(()).await.unwrap();
    db.use_ns("test").use_db("test").await.unwrap();

    let spaceship = SpaceShip {
        id: SpaceShip::create_id("num-1".into()),
        name: "spaceship-1".into(),
        created: chrono::Utc::now(),
    };

    let spaceship = spaceship.save().get_one(db.clone()).await?;

    let found_spaceship = SpaceShip::find_by_id(spaceship.id.clone())
        .return_many(db.clone())
        .await?;
    assert_eq!(found_spaceship.len(), 1);

    spaceship.clone().delete().run(db.clone()).await?;

    let found_spaceship = SpaceShip::find_by_id(spaceship.id.clone())
        .return_many(db.clone())
        .await?;

    assert!(found_spaceship.is_empty());
    assert_eq!(found_spaceship.len(), 0);
    Ok(())
}
}

7. delete_by_id Method

This method deletes a record by its ID.

#![allow(unused)]
fn main() {
#[tokio::test]
async fn test_delete_by_id() -> SurrealOrmResult<()> {
    let db = Surreal::new::<Mem>(()).await.unwrap();
    db.use_ns("test").use_db("test").await.unwrap();

    let spaceship = SpaceShip {
        id: SpaceShip::create_id("num-1".into()),
        name: "spaceship-1".into(),
        created: chrono::Utc::now(),
    };

    let spaceship = spaceship.save().get_one(db.clone()).await?;
    let found_spaceships = SpaceShip::find_by_id(spaceship.id.clone())
        .return_many(db.clone())
        .await?;
    assert_eq!(found_spaceships.len(), 1);

    SpaceShip::delete_by_id(spaceship.id.clone())
        .run(db.clone())
        .await?;

    let found_spaceships = SpaceShip::find_by_id(spaceship.id.clone())
        .return_many(db.clone())
        .await?;
    assert_eq!(found_spaceships.len(), 0);
    Ok(())
}
}

8. delete_where Method

This method deletes records based on a specific condition.

#![allow(unused)]
fn main() {
#[tokio::test]
async fn test_delete_where() -> SurrealOrmResult<()> {
    let db = Surreal::new::<Mem>(()).await.unwrap();
    db.use_ns("test").use_db("test").await.unwrap();

    let spaceship = SpaceShip {
        id: SpaceShip::create_id("num-1".into()),
        name: "spaceship-1".into(),
        created: chrono::Utc::now(),
    };

    spaceship.save().run(db.clone()).await.unwrap();
    let space_ship::Schema { name, .. } = SpaceShip::schema();

    let found_spaceships = SpaceShip::find_where(name.like("spaceship-1"))
        .return_many(db.clone())
        .await?;
    assert_eq!(found_spaceships.len(), 1);

    SpaceShip::delete_where(name.like("spaceship"))
        .run(db.clone())
        .await?;

    let found_spaceships = SpaceShip::find_where(name.like("spaceship-1"))
        .return_many(db.clone())
        .await?;
    assert_eq!(found_spaceships.len(), 0);
    Ok(())
}
}

9. create Method

This method creates a new record in the database. It's specifically for nodes.

#![allow(unused)]
fn main() {
#[tokio::test]
async fn test_create() -> SurrealOrmResult<()> {
    let db = Surreal::new::<Mem>(()).await.unwrap();
    db.use_ns("test").use_db("test").await.unwrap();

    let ss_id = SpaceShip::create_id(format!("num-{}", 1));
    let spaceship = SpaceShip {
        id: ss_id.clone(),
        name: format!("spaceship-{}", 1),
        created: chrono::Utc::now(),
    };

    let spaceship = spaceship.create().get_one(db.clone()).await?;
    // Second attempt should fail since it will be duplicate.
    spaceship
        .clone()
        .create()
        .get_one(db.clone())
        .await
        .expect_err("should fail");

    let saved_spaceship = SpaceShip::find_by_id(ss_id.clone())
        .get_one(db.clone())
        .await?;

    assert_eq!(spaceship.id.to_thing(), saved_spaceship.id.to_thing());
    assert_eq!(spaceship.name, saved_spaceship.name);
    Ok(())
}
}

This wraps up the explanations and demonstrations for all the helper methods in surreal_orm.

Introduction to Utility Functions

In surreal_orm, utility functions are designed to simplify complex database operations by abstracting them into easy-to-use methods. They help streamline CRUD operations, reducing the need for verbose database statements.

Read Methods

Read methods allow for the retrieval of data from the database. They cater to various use cases, from fetching a single record using its unique identifier to obtaining multiple records based on specific conditions.

find_by_id

2. find_by_id Method

The find_by_id method provides a straightforward way to fetch a record from the database using its unique ID.

#![allow(unused)]
fn main() {
#[tokio::test]
async fn test_find_by_id() -> SurrealOrmResult<()> {
    let db = Surreal::new::<Mem>(()).await.unwrap();
    db.use_ns("test").use_db("test").await.unwrap();

    let spaceship = SpaceShip {
        id: SpaceShip::create_id("num-1".into()),
        name: "spaceship-1".into(),
        created: chrono::Utc::now(),
    };

    spaceship.clone().save().run(db.clone()).await?;

    let found_spaceship = SpaceShip::find_by_id(spaceship.id.clone())
        .get_one(db.clone())
        .await?;

    assert_eq!(spaceship.id.to_thing(), found_spaceship.id.to_thing());
    Ok(())
}
}

find_where

3. find_where Method

For more complex data retrieval needs, the find_where method allows you to specify conditions to determine which records to fetch.

#![allow(unused)]
fn main() {
#[tokio::test]
async fn test_find_where() -> SurrealOrmResult<()> {
    let db = Surreal::new::<Mem>(()).await.unwrap();
    db.use_ns("test").use_db("test").await.unwrap();

    let spaceship = SpaceShip {
        id: SpaceShip::create_id("num-1".into()),
        name: "spaceship-1".into(),
        created: chrono::Utc::now(),
    };
    let _spaceship2 = SpaceShip {
        id: SpaceShip::create_id("num-2".into()),
        name: "spaceship-2".into(),
        created: chrono::Utc::now(),
    }
    .save()
    .run(db.clone())
    .await?;

    let _spaceschip = spaceship.clone().save().get_one(db.clone()).await?;
    let space_ship::Schema { name, id, .. } = SpaceShip::schema();

    let found_spaceships = SpaceShip::find_where(id.is_not(NULL))
        .return_many(db.clone())
        .await?;
    assert_eq!(found_spaceships.len(), 2);

    let found_spaceships = SpaceShip::find_where(name.equal("spaceship-1"))
        .return_many(db.clone())
        .await?;

    assert_eq!(found_spaceships.len(), 1);
    assert_eq!(found_spaceships[0].id.to_thing(), spaceship.id.to_thing());

    let found_spaceship = SpaceShip::find_where(name.equal("spaceship-1"))
        .get_one(db.clone())
        .await?;

    assert_eq!(found_spaceship.id.to_thing(), spaceship.id.to_thing());
    Ok(())
}
}

Create, Update Methods

Creating and updating records are fundamental operations in any database. surreal_orm provides methods to easily handle both these tasks.

save

1. save Method

The save method is versatile—it can either create a new record or update an existing one, making it an "upsert" function.

#![allow(unused)]
fn main() {
#[tokio::test]
async fn test_save() -> SurrealOrmResult<()> {
    let db = Surreal::new::<Mem>(()).await.unwrap();
    db.use_ns("test").use_db("test").await.unwrap();

    let ss_id = SpaceShip::create_id("num-1".into());
    let spaceship = SpaceShip {
        id: ss_id.clone(),
        name: "spaceship-1".into(),
        created: chrono::Utc::now(),
    };

    let spaceship = spaceship.save().get_one(db.clone()).await?;

    let saved_spaceship = SpaceShip::find_by_id(ss_id.clone())
        .get_one(db.clone())
        .await?;

    assert_eq!(spaceship.id.to_thing(), saved_spaceship.id.to_thing());
    assert_eq!(spaceship.name, saved_spaceship.name);
    Ok(())
}
}

create

9. create Method

While the save method is versatile, the create method is specialized for creating new records. It's specifically for nodes. Unlike the save method, rather than updating the existing record by its id, it throws and error when a record already exists.

#![allow(unused)]
fn main() {
#[tokio::test]
async fn test_create() -> SurrealOrmResult<()> {
    let db = Surreal::new::<Mem>(()).await.unwrap();
    db.use_ns("test").use_db("test").await.unwrap();

    let ss_id = SpaceShip::create_id(format!("num-{}", 1));
    let spaceship = SpaceShip {
        id: ss_id.clone(),
        name: format!("spaceship-{}", 1),
        created: chrono::Utc::now(),
    };

    let spaceship = spaceship.create().get_one(db.clone()).await?;
    // Second attempt should fail since it will be duplicate.
    spaceship
        .clone()
        .create()
        .get_one(db.clone())
        .await
        .expect_err("should fail");

    let saved_spaceship = SpaceShip::find_by_id(ss_id.clone())
        .get_one(db.clone())
        .await?;

    assert_eq!(spaceship.id.to_thing(), saved_spaceship.id.to_thing());
    assert_eq!(spaceship.name, saved_spaceship.name);
    Ok(())
}
}

Delete Methods

Deletion is another crucial CRUD operation, and surreal_orm offers methods to delete records in various ways.

delete

This method facilitates the deletion of a specific record instance

#![allow(unused)]
fn main() {
#[tokio::test]
async fn test_delete() -> SurrealOrmResult<()> {
    let db = Surreal::new::<Mem>(()).await.unwrap();
    db.use_ns("test").use_db("test").await.unwrap();

    let spaceship = SpaceShip {
        id: SpaceShip::create_id("num-1".into()),
        name: "spaceship-1".into(),
        created: chrono::Utc::now(),
    };

    let spaceship = spaceship.save().get_one(db.clone()).await?;

    let found_spaceship = SpaceShip::find_by_id(spaceship.id.clone())
        .return_many(db.clone())
        .await?;
    assert_eq!(found_spaceship.len(), 1);

    spaceship.clone().delete().run(db.clone()).await?;

    let found_spaceship = SpaceShip::find_by_id(spaceship.id.clone())
        .return_many(db.clone())
        .await?;

    assert!(found_spaceship.is_empty());
    assert_eq!(found_spaceship.len(), 0);
    Ok(())
}
}

delete_by_id

7. delete_by_id Method

If you know the ID of a record you wish to delete, the delete_by_id method makes the task straightforward.

#![allow(unused)]
fn main() {
#[tokio::test]
async fn test_delete_by_id() -> SurrealOrmResult<()> {
    let db = Surreal::new::<Mem>(()).await.unwrap();
    db.use_ns("test").use_db("test").await.unwrap();

    let spaceship = SpaceShip {
        id: SpaceShip::create_id("num-1".into()),
        name: "spaceship-1".into(),
        created: chrono::Utc::now(),
    };

    let spaceship = spaceship.save().get_one(db.clone()).await?;
    let found_spaceships = SpaceShip::find_by_id(spaceship.id.clone())
        .return_many(db.clone())
        .await?;
    assert_eq!(found_spaceships.len(), 1);

    SpaceShip::delete_by_id(spaceship.id.clone())
        .run(db.clone())
        .await?;

    let found_spaceships = SpaceShip::find_by_id(spaceship.id.clone())
        .return_many(db.clone())
        .await?;
    assert_eq!(found_spaceships.len(), 0);
    Ok(())
}
}

delete_where

8. delete_where Method

For scenarios where you need to delete multiple records based on a condition, the delete_where method comes in handy.

#![allow(unused)]
fn main() {
#[tokio::test]
async fn test_delete_where() -> SurrealOrmResult<()> {
    let db = Surreal::new::<Mem>(()).await.unwrap();
    db.use_ns("test").use_db("test").await.unwrap();

    let spaceship = SpaceShip {
        id: SpaceShip::create_id("num-1".into()),
        name: "spaceship-1".into(),
        created: chrono::Utc::now(),
    };

    spaceship.save().run(db.clone()).await.unwrap();
    let space_ship::Schema { name, .. } = SpaceShip::schema();

    let found_spaceships = SpaceShip::find_where(name.like("spaceship-1"))
        .return_many(db.clone())
        .await?;
    assert_eq!(found_spaceships.len(), 1);

    SpaceShip::delete_where(name.like("spaceship"))
        .run(db.clone())
        .await?;

    let found_spaceships = SpaceShip::find_where(name.like("spaceship-1"))
        .return_many(db.clone())
        .await?;
    assert_eq!(found_spaceships.len(), 0);
    Ok(())
}
}

Count and Aggregation Methods

Counting records and performing aggregate operations are common tasks when working with databases. surreal_orm provides methods to make these tasks efficient and easy.

count_where

4. count_where Method

To count records based on a condition, you can use the count_where method.

#![allow(unused)]
fn main() {
#[tokio::test]
async fn test_count_where() -> SurrealOrmResult<()> {
    let db = Surreal::new::<Mem>(()).await.unwrap();
    db.use_ns("test").use_db("test").await.unwrap();

    create_test_data(db.clone()).await;
    let weapon::Schema { strength, .. } = &Weapon::schema();

    let weapons_query = Weapon::count_where(strength.gte(500));
    let weapons_count = weapons_query.get(db.clone()).await?;

    assert_eq!(
        weapons_query.to_raw().build(),
        "SELECT VALUE count FROM (SELECT count(strength >= 500) FROM weapon GROUP ALL);"
    );

    assert_eq!(weapons_count, 500);

    Ok(())
}
}

count_all

5. count_all Method

When you need a count of all records in a table, the count_all method is your go-to.

#![allow(unused)]
fn main() {
#[tokio::test]
async fn test_count_all() -> SurrealOrmResult<()> {
    let db = Surreal::new::<Mem>(()).await.unwrap();
    db.use_ns("test").use_db("test").await.unwrap();

    create_test_data(db.clone()).await;

    let weapons_query = Weapon::count_all();
    let weapons_count = weapons_query.get(db.clone()).await?;

    assert_eq!(
        weapons_query.to_raw().build(),
        "SELECT VALUE count FROM (SELECT count() FROM weapon GROUP ALL);"
    );

    assert_eq!(weapons_count, 1000);

    Ok(())
}
}

Statements

Use Statement

The use statement in Surreal ORM is used to switch the active namespace and database. This documentation provides an overview of the use statement and its usage.

Table of Contents

Introduction

The use statement in Surreal ORM allows you to switch the active namespace and database. By specifying the desired namespace and/or database, you can focus your queries and operations on specific areas of your database.

Syntax

The basic syntax of the use statement is as follows:

#![allow(unused)]
fn main() {
use_()
    .namespace(namespace)
    .database(database);
}

The use statement supports the following methods:

  • .namespace(namespace): Specifies the namespace to use.
  • .database(database): Specifies the database to use.
  • .build(): Builds the use statement.

Examples

Using the use Statement with Namespace

To switch the active namespace using the use statement, you can use the following code:

#![allow(unused)]
fn main() {
use surreal_orm::statements::use_;
use surreal_orm::models::Namespace;

let use_statement = use_()
    .namespace(Namespace::from("mars".to_string()));

assert_eq!(use_statement, "USE NS mars;");
}

In the above example, the use statement is used to switch the active namespace to "mars". The resulting use statement is "USE NS mars;".

Using the use Statement with Database

To switch the active database using the use statement, you can use the following code:

#![allow(unused)]
fn main() {
use surreal_orm::statements::use_;
use surreal_orm::models::Database;

let use_statement = use_()
    .database(Database::from("root".to_string()));

assert_eq!(use_statement, "USE DB root;");
}

In the above example, the use statement is used to switch the active database to "root". The resulting use statement is "USE DB root;".

Using the use Statement with Namespace and Database

You can also switch both the active namespace and database using the use statement. Here's an example:

#![allow(unused)]
fn main() {
use surreal_orm::statements::use_;
use surreal_orm::models::{Namespace, Database};

let use_statement = use_()
    .namespace(Namespace::from("mars".to_string()))
    .database(Database::from("root".to_string()));

assert_eq!(use_statement, "USE DB root NS mars;");
}

In the above example, the use statement is used to switch the active namespace to "mars" and the active database to "root". The resulting use statement is "USE DB root NS mars;".

You have now learned how to use the use statement in Surreal ORM to switch the active namespace and database. This allows you to focus your queries and operations on specific areas of your database.

Let Statement

The let statement in Surreal ORM allows you to bind variables within a code block. It simplifies complex queries and enables parameter handling.

Table of Contents

In the recommended approach, you can use the let statement within the block! macro. This approach provides a natural syntax that handles variable bindings and parameter references automatically.

Using let or LET Statement within block! Macro

To define variables and bind them within a code block, you can use the let statement (or LET statement) within the block! macro. This approach offers simplicity and automation in handling variable bindings and parameter references. Let's take a look at an example:

#![allow(unused)]
fn main() {
let alien = Table::new("alien");
let metrics = Table::new("metrics");
let strength = Field::new("strength");

let code_block = block! {
    let strengths = select_value(strength).from(alien);
    let total = math::sum!(strengths);
    let count = count!(strengths);
    let name = "Oyelowo";
};

// This is equivalent to the above. Note: This is not to be confused with actual Rust's native `let` keyword.

let code_block = block! {
    LET strengths = select_value(strength).from(alien);
    LET total = math::sum!(strengths);
    LET count = count!(strengths);
    LET name = "Oyelowo";
};
}

In the code snippet above, the let (or LET) statements bind the variables strengths, total, count, and name within the code block. These variables are automatically handled by the ORM, simplifying the query construction process.

The generated SQL query for this code block would look like:

LET $strengths = (SELECT VALUE strength FROM alien);

LET $total = math::sum($strengths);

LET $count = count($strengths);

LET $name = 'Oyelowo';

The recommended approach using the let statement (or LET statement) within the block! macro is preferred because it provides a clean and concise syntax, handles variable bindings and parameter referencing automatically, and promotes code readability.

The less recommended approach involves using the let_! macro to bind variables manually within a code block. Although it provides flexibility, it requires more manual handling of parameters and can be error-prone.

Using let_! Macro

Here's an example of using the let_! macro to define variables within a code block:

#![allow(unused)]
fn main() {
let_!(strengths = select_value(strength).from(alien));
let_!(total = math::sum!(strengths));
let_!(count = count!(strengths));
let_!(name = "Oyelowo");
chain(strengths).chain(total).chain(count).chain(name)
}

In the code snippet above, the let_!macro is used to bind variablesstrengths, total, count, and namewithin the code block. The variables are manually defined and then chained together using thechain function.

The generated SQL query for this code block would look like:

LET $strengths = (SELECT VALUE strength FROM alien);

LET $total = math::sum($strengths);

LET $count = count($strengths);

LET $name = 'Oyelowo';

The less recommended approach using the let_! macro requires explicit definition and chaining of variables, making the code more complex and error-prone compared to the recommended approach.

The least recommended approach involves using the let statements with the let_ function to bind variables manually within a code block. This approach requires even more manual handling of parameters and is prone to errors.

Using let Statements with let_ Function

Here's another example of using the let statements with the let_ function to bind variables within a code block:

#![allow(unused)]
fn main() {
let strengths = let_("strengths").equal_to(select_value(strength).from(alien));
let total = let_("total").equal_to(math::sum!(strengths));
let count = let_("count").equal_to(count!(strengths));
let name = let_("name").equal_to("Oyelowo");
chain(strengths).chain(total).chain(count).chain(name);
}

In this example, the let_ function is used to define variables strengths, total, count, and name within the code block. The variables are manually defined and then chained together using the chain function.

The generated SQL query for this code block would look like:

LET $strengths = (SELECT VALUE strength FROM alien);

LET $total = math::sum($strengths);

LET $count = count($strengths);

LET $name = 'Oyelowo';

Similar to the previous approach, the use of let statements with the let_ function in the least recommended approach requires explicit variable definition and chaining, making the code more complex and error-prone.

It is generally recommended to use the recommended approach with the let statement (or LET statement) within the block! macro for better readability, automation of variable bindings, and parameter handling.

That concludes the documentation for the let statement in Surreal ORM. Use the recommended approach to simplify complex queries and handle variable bindings effortlessly.

Begin

Cancel Statement

The cancel statement in Surreal ORM is used to cancel and rollback a transaction, discarding any changes made within the transaction. It ensures that the database remains unaffected by the transaction.

Table of Contents

Using block! Macro with Cancel Statement also Within Block for Chaining Multiple Statements

To perform a transaction and cancel it, discarding any changes made within the transaction, you can use the block! macro to chain multiple statements together. The cancel_transaction statement is used within the block! macro to explicitly indicate the cancellation of the transaction. Let's take a look at an example:

#![allow(unused)]
fn main() {
let db = Surreal::new::<Mem>(()).await.unwrap();
db.use_ns("test").use_db("test").await.unwrap();

let ref id1 = Account::create_id("one".into());
let ref id2 = Account::create_id("two".into());
let acc = Account::schema();

let amount_to_transfer = 300.00;

block! {
    BEGIN TRANSACTION;

    LET acc1 = create().content(Account {
        id: id1.clone(),
        balance: 135_605.16,
    });
    LET acc2 = create().content(Account {
        id: id2.clone(),
        balance: 91_031.31,
    });

    LET updated1 = update::<Account>(id1).set(acc.balance.increment_by(amount_to_transfer));
    LET update2 = update::<Account>(id2).set(acc.balance.decrement_by(amount_to_transfer));

    CANCEL TRANSACTION;
};

Ok(())
}

In the code snippet above, the block! macro is used to define a transaction with multiple statements. The LET statement is used to bind variables acc1, acc2, updated1, and update2 to the respective statements. The BEGIN TRANSACTION statement marks the start of the transaction, and the CANCEL TRANSACTION statement explicitly cancels the transaction.

The generated SQL query for this code block would look like:

BEGIN TRANSACTION;

LET acc1 = CREATE account CONTENT { balance: 135605.16, id: account:one };
LET acc2 = CREATE account CONTENT { balance: 91031.31, id: account:two };
LET updated1 = UPDATE account:one SET balance += 300.0;
LET update2 = UPDATE account:two SET balance -= 300.0;

CANCEL TRANSACTION;

Using the block! macro with the cancel_transaction statement within the block provides a clear and concise way to define a transaction and cancel it.

Using block! Macro for Chaining Multiple Statements

Another recommended approach is to use the block! macro to chain multiple statements together within a transaction. The cancel_transaction statement is called separately after the block! macro to explicitly cancel the transaction. Let's see an example:

#![allow(unused)]
fn main() {
let db = Surreal::new::<Mem>(()).await.unwrap();
db.use_ns("test").use_db

("test").await.unwrap();

let ref id1 = Account::create_id("one".into());
let ref id2 = Account::create_id("two".into());
let acc = Account::schema();

let amount_to_transfer = 300.00;

let transaction_query = begin_transaction()
    .query(block! {
        LET acc1 = create().content(Account {
            id: id1.clone(),
            balance: 135_605.16,
        });
        LET acc2 = create().content(Account {
            id: id2.clone(),
            balance: 91_031.31,
        });

        LET updated1 = update::<Account>(id1).set(acc.balance.increment_by(amount_to_transfer));
        LET update2 = update::<Account>(id2).set(acc.balance.decrement_by(amount_to_transfer));
    })
    .cancel_transaction();

transaction_query.run(db.clone()).await?;

// Assertions and other code...

Ok(())
}

In this approach, the block! macro is used to define a transaction with multiple statements. The LET statement is used to bind variables to the statements within the block. After the block! macro, the cancel_transaction statement is called separately to cancel the transaction.

The generated SQL query for this code block would be the same as the previous approach.

Using the block! macro for chaining multiple statements and explicitly canceling the transaction provides a structured and organized way to handle complex transactions.

The less recommended approach involves chaining multiple statements directly without using the block! macro. Although functional, this approach may feel less ergonomic, especially when there is a need to bind and share variables within the statements.

Chaining Multiple Statements Directly

Here's an example of chaining multiple statements directly without using the block! macro:

#![allow(unused)]
fn main() {
#[tokio::test]
async fn test_transaction_cancel_increment_and_decrement_update() -> SurrealOrmResult<()> {
    let db = Surreal::new::<Mem>(()).await.unwrap();
    db.use_ns("test").use_db("test").await.unwrap();

    let ref id1 = Account::create_id("one".into());
    let ref id2 = Account::create_id("two".into());
    let amount_to_transfer = 300.00;

    let acc = Account::schema();

    begin_transaction()
        .query(create().content(Account {
            id: id1.clone(),
            balance: 135_605.16,
        }))
        .query(create().content(Account {
            id: id2.clone(),
            balance: 91_031.31,
        }))
        .query(update::<Account>(id1).set(acc.balance.increment_by(amount_to_transfer)))
        .query(update::<Account>(id2).set(acc.balance.decrement_by(amount_to_transfer)))
        .cancel_transaction()
        .run(db.clone())
        .await?;

    // Assertions and other code...

    Ok(())
}
}

In this approach, multiple statements are chained directly within the transaction. The create and update statements are used to perform operations on the Account table.

The generated SQL query for this code block would be the same as the previous approaches.

The less recommended approach of chaining multiple statements directly can be less ergonomic, especially when dealing with complex transactions that require variable bindings and subqueries.

It is generally recommended to use the recommended approaches with the block! macro for better readability, automation of variable bindings, and subquery handling.

That concludes the documentation for the cancel statement in Surreal ORM. Use the recommended approaches to perform transaction cancellation effectively.

Commit Statement

The commit statement in Surreal ORM is used to commit a transaction and save the changes made within the transaction. It ensures that the changes are durable and permanent in the database.

Table of Contents

Using block! Macro with Commit Statement also Within Block for Chaining Multiple Statements

To perform a transaction and commit the changes, you can use the block! macro to chain multiple statements together. The commit_transaction statement is used within the block! macro to explicitly indicate the commitment of the transaction. Let's take a look at an example:

#![allow(unused)]
fn main() {
let db = Surreal::new::<Mem>(()).await.unwrap();
db.use_ns("test").use_db("test").await.unwrap();

let ref id1 = Account::create_id("one".into());
let ref id2 = Account::create_id("two".into());
let acc = Account::schema();

let amount_to_transfer = 300.00;

block! {
    BEGIN TRANSACTION;

    LET acc1 = create().content(Account {
        id: id1.clone(),
        balance: 135_605.16,
    });
    LET acc2 = create().content(Account {
        id: id2.clone(),
        balance: 91_031.31,
    });

    LET updated1 = update::<Account>(id1).set(acc.balance.increment_by(amount_to_transfer));
    LET update2 = update::<Account>(id2).set(acc.balance.decrement_by(amount_to_transfer));

    COMMIT TRANSACTION;
};

Ok(())
}

In the code snippet above, the block! macro is used to define a transaction with multiple statements. The LET statement is used to bind variables acc1, acc2, updated1, and update2 to the respective statements. The BEGIN TRANSACTION statement marks the start of the transaction, and the COMMIT TRANSACTION statement explicitly commits the transaction.

The generated SQL query for this code block would look like:

BEGIN TRANSACTION;

LET acc1 = CREATE account CONTENT { balance: 135605.16, id: account:one };
LET acc2 = CREATE account CONTENT { balance: 91031.31, id: account:two };
LET updated1 = UPDATE account:one SET balance += 300.0;
LET update2 = UPDATE account:two SET balance -= 300.0;

COMMIT TRANSACTION;

Using the block! macro with the commit_transaction statement within the block provides a clear and concise way to define a transaction and commit the changes.

Using block! Macro for Chaining Multiple Statements

Another recommended approach is to use the block! macro to chain multiple statements together within a transaction. The commit_transaction statement is called separately after the block! macro to explicitly commit the transaction. Let's see an example:

#![allow(unused)]
fn main() {
let db = Surreal::new::<Mem>(()).await.unwrap();
db.use_ns("test").use_db("test").await.unwrap();

let ref id1 = Account

::create_id("one".into());
let ref id2 = Account::create_id("two".into());
let acc = Account::schema();

let amount_to_transfer = 300.00;

let transaction_query = begin_transaction()
    .query(block! {
        LET acc1 = create().content(Account {
            id: id1.clone(),
            balance: 135_605.16,
        });
        LET acc2 = create().content(Account {
            id: id2.clone(),
            balance: 91_031.31,
        });

        LET updated1 = update::<Account>(id1).set(acc.balance.increment_by(amount_to_transfer));
        LET update2 = update::<Account>(id2).set(acc.balance.decrement_by(amount_to_transfer));
    })
    .commit_transaction();

transaction_query.run(db.clone()).await?;

Ok(())
}

In this approach, the block! macro is used to define a transaction block that includes multiple statements. The BEGIN TRANSACTION and COMMIT TRANSACTION statements mark the start and end of the transaction, respectively. The LET statement is used to bind variables to the statements within the block.

The generated SQL query for this code block would be the same as the previous approach.

Using the block! macro for chaining multiple statements and explicitly committing the transaction provides a more structured and organized way to handle complex transactions.

The less recommended approach involves chaining multiple statements directly without using the block! macro. Although functional, this approach may feel less ergonomic, especially when there is a need to bind and share variables within the statements.

Chaining Multiple Statements Directly

Here's an example of chaining multiple statements directly without using the block! macro:

#![allow(unused)]
fn main() {
#[tokio::test]
async fn test_transaction_commit_increment_and_decrement_update() -> SurrealOrmResult<()> {
    let db = Surreal::new::<Mem>(()).await.unwrap();
    db.use_ns("test").use_db("test").await.unwrap();

    let ref id1 = Account::create_id("one".into());
    let ref id2 = Account::create_id("two".into());
    let amount_to_transfer = 300.00;

    let acc = Account::schema();

    begin_transaction()
        .query(create().content(Account {
            id: id1.clone(),
            balance: 135_605.16,
        }))
        .query(create().content(Account {
            id: id2.clone(),
            balance: 91_031.31,
        }))
        .query(update::<Account>(id1).set(acc.balance.increment_by(amount_to_transfer)))
        .query(update::<Account>(id2).set(acc.balance.decrement_by(amount_to_transfer)))
        .commit_transaction()
        .run(db.clone())
        .await?;

    // Assertions and other code...

    Ok(())
}
}

In this approach, multiple statements are chained directly within the transaction. The create and update statements are used to perform operations on the Account table.

The generated SQL query for this code block would be the same as the previous approaches.

The less recommended approach of chaining multiple statements directly can be less ergonomic, especially when dealing with complex transactions that require variable bindings and subqueries.

It is generally recommended to use the recommended approaches with the block! macro for better readability, automation of variable bindings, and subquery handling.

That concludes the documentation for the commit statement in Surreal ORM. Use the recommended approaches to perform transactions and commit changes effectively.

IfElse Statement

The ifelse statement is used to create conditional branching in SurrealDB. It allows you to execute different expressions or statements based on specified conditions. Here are some examples and usage scenarios for the ifelse statement.

Table of Contents

Statement Syntax

The syntax for the ifelse statement is as follows:

if_(condition)
    .then(expression)
    .else_if(condition)
    .then(expression)
    .else_if(condition)
    .then(expression)
    .else_(expression)
    .end();

Creating an If Statement

You can create a simple if statement using the if_ function. Here's an example:

#![allow(unused)]
fn main() {
use surreal_orm::*;

let age = Field::new("age");

let if_statement = if_(age.greater_than_or_equal(18))
    .then("Valid".to_string())
    .end();
}

Adding Else If Statements

You can add multiple else if statements to the ifelse statement. Here's an example:

#![allow(unused)]
fn main() {
let name = Field::new("name");
let age = Field::new("age");

let if_statement = if_(age.greater_than_or_equal(18))
    .then("Valid")
    .else_if(name.like("Oyelowo Oyedayo"))
    .then("The Alien!")
    .end();
}

Adding an Else Statement

You can add an else statement to the ifelse statement to handle cases when none of the previous conditions are met. Here's an example:

#![allow(unused)]
fn main() {
let age = Field::new("age");

let if_statement = if_(age.greater_than_or_equal(18))
    .then("Valid")
    .else_("Invalid")
    .end();
}

Nested If Else Statements

You can nest ifelse statements within each other to create complex conditional logic. Here's an example:

#![allow(unused)]
fn main() {
let name = Field::new("name");
let age = Field::new("age");
let country = Field::new("country");

let if_statement = if_(age.greater_than_or_equal(18))
    .then("Valid")
    .else_if(name.like("Oyelowo Oyedayo"))
    .then("The Alien!")
    .else_if(cond(country.is("Canada")).or(country.is("Norway")))
    .then("Cold")
    .else_("Hot")
    .end();
}

Using Subqueries in If Else Statements

You can use subqueries in the ifelse statement to execute more complex expressions or statements. Here's an example:

#![allow(unused)]
fn main() {
let name = Field::new("name");
let age = Field::new("age");
let country = Field::new("country");
let city = Field::new("city");
let fake_id = sql::Thing::from(("user".to_string(), "oyelowo".to_string()));
let fake_id2 = sql::Thing::from(("user".to_string(), "oyedayo".to_string()));

let statement1 = select(All)
    .from(fake_id)
    .where_(
        cond(city.is("Prince Edward Island"))
            .and(city.is("NewFoundland"))
            .or(city.like("Toronto")),
    )
    .order_by(order(&age).numeric())
    .limit(153)
    .start(10)
    .parallel();

let statement2 = select(All)
    .from(fake_id2)
    .where_(country.is("INDONESIA"))
    .order_by(order(&age).numeric())
    .limit(20)
    .start(5);

let if_statement = if_(age.greater_than_or_equal(18).less_than_or_equal(120))
    .then(statement1)
    .else_if(name.like("Oyelowo Oyedayo"))
    .then(statement2)
    .else_if(cond(country.is("Canada"))
            .or(country.is("Norway")))
    .then("Cold")
    .else_("Hot")
    .end();
}

Surreal ORM Documentation

Table of Contents

  1. Introduction
  2. Defining Your Data
  3. Advanced Schema Definitions
  4. Select Statements
  5. Advanced Select Queries
  6. Select Value Statements
  7. Advanced Select Value Queries
  8. Running Select Statements
  9. Running and Returning from a Select Statement

1. Introduction

This document focuses on defining models and using select and select_value statements for data retrieval.

2. Defining Your Data

Start by defining a User struct representing a user in your application.

#![allow(unused)]
fn main() {
extern crate surreal_orm;
use surreal_orm::*;

#[derive(Node, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase")]
#[orm(table = "user")]
pub struct User {
    pub id: SurrealSimpleId<Self>,
    pub account: String,
    pub friend: String,
}
}

3. Advanced Schema Definitions

Surreal ORM supports more complex data types including links between different models. Here's a detailed example using a Student and a Book:

#![allow(unused)]
fn main() {
#[derive(Node, Serialize, Deserialize)]
#[orm(table = "student")]
pub struct Student {
    id: SurrealSimpleId<Self>,
    first_name: String,
    last_name: String,
    age: u8,

    #[orm(link_self = "Student")]
    best_friend: LinkSelf<Student>,

    #[orm(link_one = "Book")]
    fav_book: LinkOne<Book>,

    #[orm(link_one = "Book")]
    course: LinkOne<Book>,

    #[orm(link_many = "Book")]
    sem_courses: LinkMany<Book>,
}

#[derive(Node, Serialize, Deserialize)]
#[orm(table = "book")]
pub struct Book {
    id: SurrealSimpleId<Self>,
    content: String,
}
}

4. Select Statements

select allows you to construct a SELECT statement to fetch records.

#![allow(unused)]
fn main() {
use surreal_orm::{*, statements::{order, select}};

let student::Schema {
    id,
    first_name,
    last_name,
    best_friend,
    uno_book,
    course,
    sem_courses,
    ref age,
    ..
} = &Student::schema();

let book::Schema { ref content, .. } = Book::schema();

let mut statement = select(arr![age, last_name, content])
    .from(Book::table())
    .where_(
        cond(content.like("lowo"))
            .and(age.greater_than_or_equal(600))
            .or(first_name.equal("Oyelowo"))
            .and(last_name.equal("Oyedayo")),
    )
    .order_by(last_name.desc()
    .limit(50)
    .start(20)
    .timeout(Duration::from_secs(9))
    .parallel();

let is_lowo = true;
if is_lowo {
    statement = statement.limit(55).order_by(age.desc());
}
}

Using the cond! Macro

In Surreal ORM, while the cond function provides an elegant way to construct filters, there's also a macro alternative called cond!. This macro can offer more concise and readable representations, especially for complex conditions.

The cond! macro provides a convenient syntax for constructing filters, similar to standard Rust conditional expressions. It can handle various operations like equalities, inequalities, and logical combinations.

Here's a simple example:

#![allow(unused)]
fn main() {
use surreal_query_builder as surreal_orm;
use surreal_orm::*;
let age = Field::new("age");
let name = Field::new("name");
let title = Field::new("title");

let filter_simple = cond!(age > 18);
let filter_compound = cond!((age > 18) AND (name ~ "%Oyelowo%") OR (title == "Professor"));
let filter_mixed = cond!((age.or(4).or(545).or(232)) OR (title = "Professor") AND (age < 100));
}

This macro provides a more intuitive way of writing conditions, especially when compared to chaining methods. The full definition and capabilities of the cond! macro are documented within the Surreal ORM codebase.

5. Advanced Select Queries

You can perform complex queries including nested select statements and conditional query generation. Here is an example:

#![allow(unused)]
fn main() {
use surreal_orm::{*, statements::{order, select}};

let student::Schema {
    id,
    firstName,
    lastName,
    bestFriend,
    unoBook,
    course,
    semCoures,
    ref age,
    ..
} = &Student::schema();

let book::Schema { ref content, .. } = Book::schema();
let ref student_table = Student::get_table();
let ref book_table = Book::get_table();
let ref book_id = thing("book:1").unwrap();

let mut query1 = select(arr![age, lastName, content])
    .from(Book::get_table())
    .where_(
        cond(content.like("lowo"))
            .and(age.greater_than_or_equal(600))
            .or(firstName.equal("Oyelowo"))
            .and(lastName.equal("Oyedayo")),
    )
    .order_by(lastName.desc())
    .limit(50)
    .start(20)
    .timeout(Duration::from_secs(9))
    .parallel();

let statement = select(All)
    .from(student_table)
    // .from(&[student_table, book_table])
    // .from(book_id)
    // .from(query1)
    .where_(
        cond(
            (((age + 5) - 6) * 10).greater_then(5) // You can even use raw mathematical operators directly.
        )
        .and(bestFriend.exactly_equal("Oyelowo"))
        .or(firstName.equal("Oyedayo"))
        .and(age.greater_than_or_equal(150)),
    )
    .order_by(firstName.rand().desc())
    // .order_by(lastName.collate().asc())
    // .order_by(id.numeric().desc())
    // .group_by(course)
    // .group_by(firstName)
    // .group_by(arr![lastName, unoBook])
    .start(5)
    .limit(400)
    .fetch(firstName)
    // .fetch(lastName)
    // .fetch(arr![age, unoBook])
    .split(lastName)
    // .split(firstName)
    // .split(arr![firstName, semCoures])
    .timeout(Duration::from_secs(8))
    .parallel();

let is_oyelowo = true;
if is_oyelowo {
    query = query.group_by(arr![age, bestFriend, &Field::new("dayo")]);
}
}

6. Select Value Statements

select_value is similar to select but it only returns the first column from the result. Here is a basic usage:

#![allow(unused)]
fn main() {
let statement = select_value(account)
    .from(user)
    .where_(account.is("abc"));
}

7. Advanced Select Value Queries

You can perform complex value queries as well. Here is an example:

#![allow(unused)]
fn main() {
let statement = select_value(account)
    .from(user)
    .where_(
        and(
            account.is("abc"),
            or(
                friend.is("xyz"),
                friend.is("lmn"),
            ),
        ),
    );

let statement = select_value(account)
    .from(user)
    .where_(
        not(account.is("def")),
    );
}

8. Running Select Statements

Executing a select statement is straightforward. Here's an example that uses return_many:

#![allow(unused)]
fn main() {
extern crate surreal_orm;
use surreal_orm::{*, statements::{select, insert}};

#[derive(Node, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase")]
#[orm(table = "weapon")]
pub struct Weapon {
    pub name: String,
    pub strength: i32,
    pub created: chrono::DateTime<chrono::Utc>,
}

let db = Surreal::new::<Mem>(()).await.unwrap();
db.use_ns("test").use_db("test").await.unwrap();

let generated_weapons = (1..=10)
    .map(|i| Weapon {
        name: format!("Weapon {}", i),
        strength: i * 10,
        created: chrono::Utc::now(),
        ..Default::default()
    })
    .collect::<Vec<_>>();
insert(generated_weapons.clone()).run(db.clone()).await?;

let ref weapon = Weapon::table();
let weapon::Schema { ref strength, .. } = &Weapon::schema();

let statement = select(All)
    .from(weapon)
    .where_(
        strength.inside(
            select_value(strength)
                .from(weapon)
                .order_by(strength.asc())
                .limit(6),
        ),
    )
    .order_by(strength.desc())
    .start(2)
    .limit(10);

assert_eq!(
    statement.to_raw().build(),
    "SELECT * FROM weapon WHERE strength INSIDE \
        (SELECT VALUE strength FROM weapon ORDER BY strength LIMIT 6) \
        ORDER BY strength DESC LIMIT 10 START AT 2;"
);
let result = statement.return_many::<Weapon>(db.clone()).await?;

assert_eq!(&result[0].name, "Weapon 4");
assert_eq!(&result[1].name, "Weapon 3");
assert_eq!(&result[2].name, "Weapon 2");
assert_eq!(&result[3].name, "Weapon 1");

assert_eq!(result.len(), 4);
assert!(result[0].id.to_string().starts_with("weapon:"));
Ok(())
}

This example first inserts generated weapon data into the database. Then it constructs a select statement and retrieves the weapons whose strength is in the top 6, ordered by strength in descending order, and returns the results from the third entry. The return_many function is used to run the statement and get the result.

9. Running and Returning from a Select Statement

The Surreal ORM package provides the ReturnableSelect trait that defines several functions to run a select statement and return results in different ways. These functions include return_none, return_first, return_one, return_one_unchecked, and return_many.

All these functions run the statement against the SurrealDB database and return results:

  • return_none: Returns no result.
  • return_first: Returns the first result.
  • return_one: Returns one result.
  • return_one_unchecked: Returns one result without checking if it's successful.
  • return_many: Returns many results.
  • run: Runs the query and provide more flexible deserialization just like surrealdb native drive e.g .run(db).take::<T>(0).

Surreal ORM - Insertion Operations

Surreal ORM provides various options to perform data insertion operations in your database. This guide focuses on three main operations:

Table of Contents

  1. Inserting Single Record
  2. Inserting Multiple Records
  3. Inserting from Another Table

Inserting Single Record

The ORM allows for inserting a single record into a database table. Below is an example of this:

#![allow(unused)]
fn main() {
// Required imports
use surrealdb::Surreal;
use surrealdb::engine::local::Mem;
use surreal_models::Weapon;
use surreal_orm::statements::insert;
use chrono::Utc;

// Initialize SurrealDB with the in-memory engine
let db = Surreal::new::<Mem>(()).await.unwrap();
db.use_ns("test").use_db("test").await.unwrap();

// Define a single weapon
let weapon = Weapon {
    name: String::from("Excalibur"),
    created: Utc::now(),
    strength: 1000,
    ..Default::default()
};

// Insert the weapon into the database
let created_weapon = insert(weapon).return_one(db.clone()).await.unwrap();

// Verify the inserted record
assert_eq!(created_weapon.name, "Excalibur");
assert_eq!(created_weapon.strength, 1000);
}

This code creates a single Weapon record with the name "Excalibur" and a strength of 1000.


Inserting Multiple Records

In addition to inserting single records, Surreal ORM also supports inserting multiple records at once. Here is an example:

#![allow(unused)]
fn main() {
// Required imports
use surrealdb::Surreal;
use surrealdb::engine::local::Mem;
use surreal_models::Weapon;
use surreal_orm::statements::insert;
use chrono::Utc;

// Initialize SurrealDB with the in-memory engine
let db = Surreal::new::<Mem>(()).await.unwrap();
db.use_ns("test").use_db("test").await.unwrap();

// Define a list of weapons
let weapons = (0..1000)
    .into_iter()
    .map(|i| Weapon {
        name: format!("Weapon{}", i),
        created: Utc::now(),
        strength: i,
        ..Default::default()
    })
    .collect::<Vec<_>>();

// Insert the weapons into the database
let created_weapons = insert(weapons).return_many(db.clone()).await.unwrap();

// Verify the inserted records
assert_eq!(created_weapons.len(), 1000);
assert_eq!(created_weapons[0].name, "Weapon0");
assert_eq!(created_weapons[0].strength, 0);
}

This code creates 1000 Weapon records with sequential names and strength values.


Inserting from Another Table

Surreal ORM allows you to copy data from one table to another using the insert statement. This is similar to creating a view in PostgreSQL, but instead of just a projection, it's copying the data to a new table.

#![allow(unused)]
fn main() {
// Required imports
use surrealdb::Surreal;
use surrealdb::engine::local::Mem;
use surreal_models::{Weapon, StrongWeapon};
use surreal_orm::statements::{insert, select, All, cond, order};
use chrono::Utc;

// Initialize SurrealDB with the in-memory engine
let db = Surreal::new::<Mem>(()).await.unwrap();
db.use_ns("test").use_db("test").await.unwrap();

// Define a list of weapons
let weapons = (

0..1000)
    .into_iter()
    .map(|i| Weapon {
        name: format!("Weapon{}", i),
        created: Utc::now(),
        strength: i,
        ..Default::default()
    })
    .collect::<Vec<_>>();

// Insert the weapons into the database
let created_weapons = insert(weapons).return_many(db.clone()).await.unwrap();

// Define a SELECT statement for weapons with strength values between 800 and 950
let weapon::Schema { strength, .. } = Weapon::schema();
let select_statement = select(All)
    .from(Weapon::table())
    .where_(cond(strength.greater_than_or_equal(800)).and(strength.less_than(950)));

// Insert the selected weapons into the StrongWeapon table
let strong_weapons = insert::<StrongWeapon>(select_statement)
    .return_many(db.clone())
    .await
    .unwrap();

// Verify the copied records
assert_eq!(strong_weapons.len(), 150);
assert_eq!(strong_weapons[0].strength, 800);
}

This script inserts 1000 Weapon records, selects those with strength values between 800 and 950, and copies them into the StrongWeapon table.

Create Statement

The create statement is used to add new entries to the SurrealDB database. It allows you to create records with specified content and set additional properties for the query. This documentation provides an overview of the syntax and usage of the create statement.

Table of Contents

Syntax

The basic syntax of the create statement is as follows:

#![allow(unused)]
fn main() {
create()
    .content(record_content)
    .set(set_statements)
    .return_type(return_types)
    .timeout(seconds)
    .parallel();
}

The create statement supports the following methods:

  • .content(record_content): Specifies the content of the record to be created.
  • .set(set_statements): Sets the values of the fields to be updated in the record.
  • .return_type(return_types): Specifies the return type for the query.
  • .timeout(seconds): Sets the timeout duration for the query.
  • .parallel(): Indicates whether the query should be executed in parallel.

Examples

Basic Create Statement with Content Method

To create a basic record using the create statement, you can use the following code:

#![allow(unused)]
fn main() {
let space_ship1 = create()
    .content(space_ship1.clone())
    .get_one(db.clone())
    .await?;
}

This code will create a new entry for space_ship1 in the database.

Creating Linked Entities

The create statement allows you to create entries that have links to other entities. Here's an example of creating a linked entity:

#![allow(unused)]
fn main() {
let unsaved_alien = Alien {
    ...
    space_ships: LinkMany::from(vec![
        created_spaceship1.clone(),
        created_spaceship2.clone(),
        space_ship3.clone(),
    ]),
    ...
};

let created_alien_with_fetched_links = create()
    .content(unsaved_alien.clone())
    .load_link_manys()?
    .return_one(db.clone())
    .await?;
}

In this example, unsaved_alien is being created with links to three different spaceships. The .load_link_manys() method loads the linked entities in a single statement.

Create Using the object! Macro

The object! macro provides a concise and type-safe way to specify values when creating or updating records when using the set method. It acts as syntactic sugar for an array of setters but provides stricter field checking compared to using a basic struct.

Here's an example showcasing its usage:

#![allow(unused)]
fn main() {
let spaceship_id_1 = SpaceShip::create_id("spaceship1".to_string());

let space_ship1 = create::<SpaceShip>()
    .set(object!(SpaceShip {
        id: spaceship_id_1,
        name: "SpaceShip1",
        created: Utc::now(),
    }))
    .get_one(db.clone())
    .await?;
assert_eq!(space_ship1.name, "SpaceShip1");
}

Using the object! macro ensures all fields are provided and belong to the specified struct. It also allows for using parameter or field as values. This is recommended over using array of setters as shown next because of the extra checks this provides.

Additionally, the object_partial! macro functions similarly but allows for omitting some fields. This is particularly useful for update statements where only a subset of fields need to be changed.

#![allow(unused)]
fn main() {
let updated = update::<Weapon>(id)
    .set(object_partial!(Weapon { strength: 923u64 }))
    .return_one(db.clone())
    .await?;
}

Create with Set Method

You can use the set method with the create statement to set specific fields of the record being created. The set method supports multiple approaches for specifying the setter statements:

  1. Using an array const (&[T]):
#![allow(unused)]
fn main() {
let space_ship2 = create::<SpaceShip>()
    .set([
        id.equal_to(spaceship_id_2),
        name.equal_to("SpaceShip2".to_string()),
        created.equal_to(Utc::now()),
    ])
    .get_one(db.clone())
    .await?;
}
  1. Using a Vec of setter statements:
#![allow(unused)]
fn main() {
let space_ship1 = create::<SpaceShip>()
    .set(vec![
        id.equal_to(spaceship_id_1),
        name.equal_to("SpaceShip1".to_string()),
        created.equal_to(Utc::now()),
    ])
    .get_one(db.clone())
    .await?;
}

In these examples, we demonstrate different ways to use the set method. You can use an array const ([T] or &[T]) or a Vec to provide a list of setter statements.

This concludes the documentation for the create statement. Use this statement to add new entries to the SurrealDB database with desired content and additional properties.

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

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:

  1. Type-safety: Both macros ensure that all fields provided belong to the specified struct.
  2. Parameters and Fields: They allow the use of parameters or fields as values, providing flexibility in constructing dynamic update statements.
  3. 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

  1. 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?;
}
  1. 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.

  1. 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?;
}
  1. 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.

Relate Statement

The relate statement is used to create relationships between different entities in SurrealDB. It allows you to establish connections and associate data between tables. Here are some examples and usage scenarios for the relate statement.

Table of Contents

Getting Relations

You can retrieve the relations and aliases for a specific field in a struct using the get_fields_relations_aliased method. This example demonstrates how to retrieve the relations and aliases for the Student struct:

#![allow(unused)]
fn main() {
let relations_aliases = Student::get_fields_relations_aliased();
}

Valid ID Usage

To create a relationship between entities using valid IDs, you can use the relate statement. Here's an example of how to relate a student to a book:

#![allow(unused)]
fn main() {
let student_id = Student::create_id("1");
let book_id = Book::create_id("2");

let write_book = StudentWritesBook {
    time_written: Duration::from_secs(343),
    // other fields...
};

let relation = relate(Student::with(&student_id).writes__(Empty).book(&book_id))
    .content(write_book)
    .parallel();
}

Invalid ID Usage

When using invalid IDs in the relate statement, errors will be generated. Here's an example of relating entities with invalid IDs:

#![allow(unused)]
fn main() {
let student_id = Student::create_id("oye");
let book_id = Book::create_id("mars");

let write = StudentWritesBook {
    time_written: Duration::from_secs(343),
    // other fields...
};

let relate_statement = relate(Student::with(&book_id).writes__(Empty).book(&student_id))
    .content(write.clone())
    .return_type(ReturnType::Before)
    .parallel();
}

Relate Subquery to Subquery

You can also use subqueries in the relate statement to establish relationships between subquery results. Here's an example:

#![allow(unused)]
fn main() {
let write = StudentWritesBook {
    time_written: Duration::from_secs(52),
    // other fields...
};

let statement = relate(
    Student::with(select(All).from(Student::get_table()))
        .writes__(E)
        .book(
            select(All).from(Book::get_table()),
        ),
)
.content(write.clone());
}

Any Edge Filter

The any_other_edges function allows you to filter relationships based on multiple edge types. Here's an example:

#![allow(unused)]
fn main() {
let aliased_connection = Student::with(student_id)
    .writes__(any_other_edges([visits, likes]).where_(timeWritten.less_than_or_equal(50)))
    .book(book_id)
    .__as__(Student::aliases().writtenBooks);
}

Recursive Edge-to-Edge Connection

You can create recursive edge-to-edge connections using the relate statement. This allows you to select and relate entities at multiple levels. Here's an example:

#![allow(unused)]
fn main() {
let aliased_connection = Student::with(student_id)
    .writes__(Empty)
    .writes__(Empty)
    .writes__(any_other_edges(&[writes, likes]).where_(timeWritten.less_than_or_equal(50)))
    .book(book_id)
    .__as__(Student::aliases().writtenBooks);
}

Relate Query

The relate statement can be used to execute a query and return the result. Here's an example:

#![allow(unused)]
fn main() {
let relate_simple = relate(Student::with(student_id).writes__(E).book(book_id)).content(write);
let relate_simple_object = relate_simple.return_one(db.clone()).await?;
let relate_simple_array = relate_simple.return_many(db.clone()).await?;
}

Relate Query with Subquery

You can also use subqueries in the relate statement to execute more complex queries. Here's an example:

#![allow(unused)]
fn main() {
let statement = relate(
    Student::with(select(All).from(Student::get_table()))
        .writes__(E)
        .book(
            select(All).from(Book::get_table()),
        ),
)
.content(write.clone());
}

Using set Method with object! Macro in the relate Statement

The relate statement supports the use of the set method, serving as an alternative to the content method for specifying data when creating relationships between entities.

The set method, when combined with the object! macro, offers a concise, type-safe, and robust way to define the fields to be set during the relation creation. Using the object! macro ensures that all fields are present, which is crucial for avoiding serialization/deserialization issues arising from missing fields or schema mismatches.

Example: Using object! Macro with set in relate

#![allow(unused)]
fn main() {
let student_id = Student::create_id("1");
let book_id = Book::create_id("2");

relate(Student::with(&student_id).writes__(Empty).book(&book_id))
    .set(object!(StudentWritesBook {
        time_written: Duration::from_secs(343),
        // other fields...
    }))
    .parallel();
}

The Importance of the object! Macro in the relate Statement

In the context of the relate statement, the object! macro provides significant advantages:

  1. Type Safety: The object! macro ensures type safety, drastically reducing the risk of type mismatches during compile-time.
  2. Full Field Coverage: Ensures that all fields are present, protecting against potential issues during serialization/deserialization due to missing fields or schema mismatches.
  3. Readability and Clarity: Using the object! macro leads to cleaner code. By explicitly defining fields and their corresponding values, the code becomes more understandable.
  4. Parameterized Fields: Supports the inclusion of parameters and fields, making it especially valuable in transactional contexts within the block! macro.

Given these benefits, it's strongly recommended to utilize the object! macro in the relate statement:

#![allow(unused)]
fn main() {
let relation_with_macro = relate(Student::with(&student_id).writes__(Empty).book(&book_id))
    .set(object!({
        timeWritten: Utc::now(),
        someOtherField: "Some Value",
        anotherField: "Another Value"
    }))
    .parallel();
}

Prioritizing the use of the object! macro ensures a combination of safety, clarity, and robustness in your development process.

Delete Operations

Table of Contents

  1. Setup and Test Data Creation
  2. Delete by ID Using Helper Functions
  3. Delete by ID
  4. Delete Using Model Instance
  5. Delete Using Conditions with Model Helper Functions
  6. Delete Multiple Records Based on Conditions
  7. Conclusion

Setup and Test Data Creation

Before diving into the deletion methods, let's set up the necessary environment and generate some test data.

#![allow(unused)]
fn main() {
use pretty_assertions::assert_eq;
use surreal_models::{weapon, Weapon};
use surreal_orm::{
    statements::{delete, insert},
    *,
};
use surrealdb::{
    engine::local::{Db, Mem},
    Surreal,
};

async fn create_test_data(db: Surreal<Db>) -> Vec<Weapon> {
    let space_ships = (0..1000)
        .map(|i| Weapon {
            name: format!("weapon-{}", i),
            strength: i,
            ..Default::default()
        })
        .collect::<Vec<Weapon>>();
    insert(space_ships).return_many(db.clone()).await.unwrap()
}
}

Delete by ID Using Helper Functions

The surreal_orm library provides helper functions on model instances for common operations. Here's how you can delete a record using the delete_by_id helper function:

#![allow(unused)]
fn main() {
#[tokio::test]
async fn test_delete_by_id_helper_function() -> SurrealOrmResult<()> {
    let db = Surreal::new::<Mem>(()).await.unwrap();
    db.use_ns("test").use_db("test").await.unwrap();

    let weapons = create_test_data(db.clone()).await;
    let weapon1 = weapons.first().unwrap();
    let ref weapon1_id = weapon1.id.clone();

    let weapon::Schema { id, .. } = &Weapon::schema();

    let deleted_weapon_count = || async {
        Weapon::count_where(id.eq(weapon1_id))
            .get(db.clone())
            .await
            .unwrap()
    };
    assert_eq!(deleted_weapon_count().await, 1);

    Weapon::delete_by_id(weapon1_id).run(db.clone()).await?;

    assert_eq!(deleted_weapon_count().await, 0);

    Ok(())
}
}

Delete by ID

Another approach to delete a record is by directly using its ID. This method is efficient for deleting a single record:

#![allow(unused)]
fn main() {
#[tokio::test]
async fn test_delete_one_by_id() -> SurrealOrmResult<()> {
    let db = Surreal::new::<Mem>(()).await.unwrap();
    db.use_ns("test").use_db("test").await.unwrap();

    let weapons = create_test_data(db.clone()).await;
    let weapon1 = weapons.first().unwrap();
    let ref weapon1_id = weapon1.id.clone();

    let weapon::Schema { id, .. } = &Weapon::schema();

    let deleted_weapon_count = || async {
        Weapon::count_where(id.eq(weapon1_id))
            .get(db.clone())
            .await
            .unwrap()
    };
    assert_eq!(deleted_weapon_count().await, 1);

    delete::<Weapon>(weapon1_id).run(db.clone()).await?;

    assert_eq!(deleted_weapon_count().await, 0);

    Ok(())
}
}

Delete Using Model Instance

Rather than specifying an ID or condition, surreal_orm allows developers to delete records directly using a model instance. This approach can be useful when the developer already has a reference to the model instance they want to delete:

#![allow(unused)]
fn main() {
#[tokio::test]
async fn test_delete_one_by_model_instance() -> SurrealOrmResult<()> {
    let db = Surreal::new::<Mem>(()).await.unwrap();
    db.use_ns("test").use_db("test").await.unwrap();

    let weapons = create_test_data(db.clone()).await;
    let weapon1 = weapons.first().unwrap();
    let ref weapon1_id = weapon1.id.clone();

    let weapon::Schema { id, .. } = &Weapon::schema();

    let deleted_weapon_count = || async {
        Weapon::count_where(id.eq(weapon1_id))
            .get(db.clone())
            .await
            .unwrap()
    };
    let deleted_weapon = || async {
        Weapon::find_by_id(weapon1_id)
            .return_one(db.clone())
            .await
            .unwrap()
    };

    assert_eq!(deleted_weapon().await.is_some(), true);
    assert_eq!(deleted_weapon_count().await, 1);

    weapon1.delete().run(db.clone()).await?;

    assert_eq!(deleted_weapon().await.is_some(), false);
    assert_eq!(deleted_weapon_count().await, 0);

    Ok(())
}
}

Delete Using Conditions with Model Helper Functions

Sometimes, developers may need to delete a group of records based on a particular condition. Model helper functions can also facilitate such operations:

#![allow(unused)]
fn main() {
#[tokio::test]
async fn test_delete_where_model_helper_function() -> SurrealOrmResult<()> {
    let db = Surreal::new::<Mem>(()).await.unwrap();
    db.use_ns("test").use_db("test").await.unwrap();

    create_test_data(db.clone()).await;

    let weapon::Schema { strength, .. } = &Weapon::schema();

    let weapons_count = || async { Weapon::count_all().get(db.clone()).await.unwrap() };
    assert_eq!(weapons_count().await, 1000);

    Weapon::delete_where(cond(strength.gte(500)).and(strength.lt(600)))
        .run(db.clone())
        .await?;

    assert_eq!(weapons_count().await, 900);

    Ok(())
}
}

Delete Multiple Records Based on Conditions

The ORM also provides direct deletion methods for multiple records based on specific conditions. This is particularly useful when the developer knows the exact criteria they want to match for the deletion:

#![allow(unused)]
fn main() {
#[tokio::test]
async fn test_delete_many_query_by_condition() -> SurrealOrmResult<()> {
    let db = Surreal::new::<Mem>(()).await.unwrap();
    db.use_ns("test").use_db("test").await.unwrap();

    create_test_data(db.clone()).await;

    let weapon::Schema { strength, .. } = &Weapon::schema();

    let weapons_count = || async { Weapon::count_all().get(db.clone()).await.unwrap() };
    assert_eq!(weapons_count().await, 1000);

    delete::<Weapon>(Weapon::table())
        .where_(cond(strength.gte(500)).and(strength.lt(600)))
        .run(db.clone())
        .await?;

    assert_eq!(weapons_count().await, 900);

    Ok(())
}
}

Conclusion

The delete operations in surreal_orm offer a flexible and comprehensive mechanism to remove records from the surrealdb database. Whether it's deleting a single record using its ID, removing multiple records based on conditions, or even utilizing model instances for deletions, the ORM provides an arsenal of tools to help developers manage their data efficiently.

For Statement (Permissions)

The for statement is used to define permissions for a specific action or CRUD operation in SurrealDB. It allows you to specify the desired permissions and conditions for the action. This statement is commonly used when defining tables or fields in SurrealDB, but it may also be used for access control for other objects in the future. This documentation provides an overview of the syntax and usage of the for statement.

Table of Contents

Syntax

The basic syntax of the for statement is as follows:

#![allow(unused)]
fn main() {
for_permission(permission_type)
    .where_(condition);
}
  • permission_type: The type of permission or action for which you want to define permissions. It can be a single permission type or an array of permission types.
  • condition: The condition or criteria for the permission. It specifies the conditions under which the permission should be granted.

The for statement supports the following methods:

  • .where_(condition): Specifies the condition or criteria for the permission.

Permission Types

SurrealDB uses permission types to define different actions or CRUD operations that can be performed on tables or fields. Here are the available permission types:

  • Create: Grants permission to create new records or objects.
  • Read (or Select): Grants permission to read or retrieve data from records or objects.
  • Update: Grants permission to modify or update existing records or objects.
  • Delete: Grants permission to delete records or objects.

These permission types allow you to define fine-grained access control for different actions in your database.

Using the cond! Macro

The cond! macro provides an alternative way to the cond function way to define conditions for the for statement when specifying the condition for the permissions. With the cond! macro, you can easily specify conditions that determine when permissions are granted.

For instance:

#![allow(unused)]
fn main() {
let condition = cond!((field_name OR  "value") OR (age > 18));
}

The above code checks if the field named "field_name" equals the string "value". For more details on the cond! macro, refer to the dedicated chapter on helper macros.

Examples

Define Permission for Single Action

To define permissions for a single action, you can use the following code:

#![allow(unused)]
fn main() {
use CrudType::*;
let name = Field::new("name");

let for_res = for_permission(Create).where_(name.like("Oyelowo"));
println!("{}", for_res.to_raw().build());
}

The above code will generate the following raw statement:

FOR create
    WHERE name ~ 'Oyelowo'

In the example above, the for statement defines permissions for the Create action. It specifies the condition that the field "name" should be matched with the pattern "Oyelowo". This means that the permission to create records will be granted only when the field "name" matches the pattern.

Define Permissions for Multiple Actions (Individual)

To define permissions for multiple actions individually, you can use the following code:

#![allow(unused)]
fn main() {
use CrudType::*;
let name = Field::new("name");

let for_res = for_permission(Select).where_(age.greater_than_or_equal(18))
    .permissions(for_permission(Create).where_(name.is("Oyedayo")))
    .permissions(for_permission(Update).where_(age.less_than_or_equal(130)));
println!("{}", for_res.to_raw().build());
}

The above code will generate the following raw statement:

FOR select
    WHERE age >= 18
PERMISSIONS
    FOR create
        WHERE name IS 'Oyedayo'
    FOR update


 WHERE age <= 130

In the example above, the for statement defines permissions for the Select action, as well as individual permissions for the Create and Update actions. It specifies different conditions for each action. This means that the permissions for these actions will be granted only when the specified conditions are met.

Define Permissions for Multiple Actions (Array)

To define permissions for multiple actions using an array, you can use the following code:

#![allow(unused)]
fn main() {
use CrudType::*;
let name = Field::new("name");

let for_res = for_permission(&[Create, Delete, Select, Update]).where_(name.is("Oyedayo"));
println!("{}", for_res.to_raw().build());
}

The above code will generate the following raw statement:

FOR create, delete, select, update
    WHERE name IS 'Oyedayo'

In the example above, the for statement defines permissions for multiple actions (Create, Delete, Select, and Update) using an array. It specifies a common condition for all the actions. This means that the permissions for these actions will be granted only when the field "name" is equal to "Oyedayo".

Define Permissions for Multiple Actions (Mixed)

To define permissions for multiple actions using a mix of individual permissions and an array, you can use the following code:

#![allow(unused)]
fn main() {
use CrudType::*;
let name = Field::new("name");

let for_res = for_permission(&[Create, Delete]).where_(name.is("Oyedayo"))
    .permissions(for_permission(Update).where_(age.less_than_or_equal(130)));
println!("{}", for_res.to_raw().build());
}

The above code will generate the following raw statement:

FOR create, delete
    WHERE name IS 'Oyedayo'
PERMISSIONS
    FOR update
        WHERE age <= 130

In the example above, the for statement defines individual permissions for the Create and Delete actions, and an array of permissions for the Update action. It specifies different conditions for each action. This means that the permissions for these actions will be granted only when the specified conditions are met.

You have now learned how to define permissions using the for statement in SurrealDB. Use this statement to specify the desired access control for different actions or CRUD operations in your database. While it is commonly used when defining tables or fields, it may also be utilized for access control for other objects in the future.

Define Statement

The define statement in SurrealDB is a powerful tool that allows you to define various objects and configurations within the database. It provides a flexible and expressive way to create and manage entities such as tables, indexes, namespaces, tokens, logins, and more. This documentation provides an overview of the define statement and its usage.

Table of Contents

Introduction

The define statement serves as a declarative mechanism for defining and configuring various elements in SurrealDB. It enables you to specify the properties and characteristics of different objects, helping you define the structure, behavior, and access controls of your database components.

By using the define statement, you can create and manage objects such as tables, indexes, namespaces, tokens, logins, and more, all within a single comprehensive syntax. This provides a unified approach to defining and organizing your database entities, making it easier to maintain and modify them over time.

Syntax

The general syntax of the define statement is as follows:

#![allow(unused)]
fn main() {
define(object_name)
    .property1(value1)
    .property2(value2)
    .property3(value3)
    // ...
}

The specific properties and values depend on the type of object being defined. Each object may have different properties that can be set, such as names, types, constraints, configurations, and more. The define statement provides a fluent and chainable API to set these properties in a concise and readable manner.

Supported Objects

The define statement supports a variety of objects that can be defined within SurrealDB. Some of the commonly used objects include:

  • Tables: Define the structure and schema of tables within the database.
  • Indexes: Define indexes on tables to optimize data retrieval and querying.
  • Namespaces: Define logical containers to organize database objects.
  • Tokens: Define authentication and authorization tokens for access control.
  • Logins: Define user logins for authentication purposes.
  • Scopes: Define scopes to encapsulate and manage query execution environments.

These are just a few examples of the objects that can be defined using the define statement. SurrealDB provides a rich set of features and options for each object type, allowing you to customize and tailor the behavior of your database entities according to your specific requirements.

Examples

Here are a few examples of using the define statement to define different objects:

  • Defining a table:
#![allow(unused)]
fn main() {
let user = Table::from("user");
let statement = define_table(user).schemaless().permissions_full();
}
  • Defining an index:
#![allow(unused)]
fn main() {
let query = define_index("userEmailIndex")
    .on_table(User::table())
    .fields(email)
    .unique();
}
  • Defining a namespace:
#![allow(unused)]
fn main() {
let namespace_def = define_namespace("myapp");
}
  • Defining a token:
#![allow(unused)]
fn main() {
let token_def = define_token("access_token")
    .on_namespace()
    .type_(TokenType::HS256)
    .value("mysecretpassword");
}

These examples showcase the versatility and power of the define statement in SurrealDB. You can define and configure a wide range of objects using a consistent and intuitive syntax, enabling you to shape your database according to your desired structure and requirements.

This concludes the overview of the define statement in SurrealDB. You can now leverage its capabilities to define and manage various objects within your database, providing a solid foundation

for building robust and scalable applications.

Define Namespace Statement

The define_namespace statement is used to define a namespace in SurrealDB. A namespace is a logical container for organizing database objects, such as tables, indexes, and functions. This documentation provides an overview of the syntax and usage of the define_namespace statement.

Table of Contents

Syntax

The syntax of the define_namespace statement is as follows:

#![allow(unused)]
fn main() {
define_namespace(namespace_name: &str)
}
  • namespace_name: The name of the namespace to define.

Examples

Define a Namespace

To define a namespace, you can use the following code:

#![allow(unused)]
fn main() {
let statement = define_namespace("oyelowo");
}

In the example above, the define_namespace statement defines a namespace named "oyelowo".

This will generate the following SQL statement:

DEFINE NAMESPACE oyelowo;

You have now learned how to define a namespace using the define_namespace statement. Namespaces provide a way to organize and structure your database objects within SurrealDB, enabling better management and organization of your resources.

Define Database Statement

The define_database statement is used to define a database in SurrealDB. A database is a logical container for storing related data and organizing resources. This documentation provides an overview of the syntax and usage of the define_database statement.

Table of Contents

Syntax

The syntax of the define_database statement is as follows:

#![allow(unused)]
fn main() {
define_database(database_name: Database)
}
  • database_name: The name of the database to define.

Examples

Define a Database

To define a database, you can use the following code:

#![allow(unused)]
fn main() {
let statement = define_database("oyelowo");
}

In the example above, the define_database statement defines a database named "oyelowo".

This will generate the following SQL statement:

DEFINE DATABASE oyelowo;

You have now learned how to define a database using the define_database statement. Databases provide a way to organize and manage data within SurrealDB, allowing you to create distinct containers for your data resources.

Define Login Statement

The define_login statement is used to define a login in SurrealDB. Logins are used for authentication purposes, allowing users to authenticate and access protected resources. This documentation provides an overview of the syntax and usage of the define_login statement.

Table of Contents

Syntax

The basic syntax of the define_login statement is as follows:

#![allow(unused)]
fn main() {
define_login(login_name: Login)
    .on_namespace()
    .password(password: &str)

define_login(login_name: Login)
    .on_database()
    .password(password: &str)

define_login(login_name: Login)
    .on_namespace()
    .passhash(passhash: &str)
}
  • login_name: The name of the login to define.
  • password: The password associated with the login.
  • passhash: The password hash associated with the login.

The define_login statement supports the following options:

  • on_namespace(): Specifies that the login should be defined on the namespace level.
  • on_database(): Specifies that the login should be defined on the database level.

Examples

Define Login with Password

To define a login with a password, you can use the following code:

#![allow(unused)]
fn main() {
let username = Login::new("username");
let login_with_password = define_login(username)
    .on_database()
    .password("oyelowo");
}

In the example above, the define_login statement defines a login named "username" on the database level. The login is associated with a password "oyelowo".

This will generate the following SQL statement:

DEFINE LOGIN username ON DATABASE PASSWORD 'oyelowo';

Define Login with Passhash

To define a login with a password hash, you can use the following code:

#![allow(unused)]
fn main() {
let login_with_hash = define_login("username")
    .on_namespace()
    .passhash("reiiereroyedayo");
}

In the example above, the define_login statement defines a login named "username" on the namespace level. The login is associated with a password hash "reiiereroyedayo".

This will generate the following SQL statement:

DEFINE LOGIN username ON NAMESPACE PASSHASH 'reiiereroyedayo';

You have now learned how to define logins using the define_login statement. Logins are essential for authentication in SurrealDB, allowing users to securely access protected resources.

Define Token Statement

The define_token statement is used to define a token in SurrealDB. Tokens are used for authentication and authorization purposes, allowing users or applications to access protected resources. This documentation provides an overview of the syntax and usage of the define_token statement.

Table of Contents

Syntax

The basic syntax of the define_token statement is as follows:

#![allow(unused)]
fn main() {
define_token(token_name: Token)
    .on_namespace()
    .type_(token_type: TokenType)
    .value(token_value: &str)

define_token(token_name: Token)
    .on_database()
    .type_(token_type: TokenType)
    .value(token_value: &str)

define_token(token_name: Token)
    .on_scope(scope_name: Scope)
    .type_(token_type: TokenType)
    .value(token_value: &str)
}
  • token_name: The name of the token to define.
  • token_type: The type of the token, specified using the TokenType enum.
  • token_value: The value or secret associated with the token.

The define_token statement supports the following options:

  • on_namespace(): Specifies that the token should be defined on the namespace level.
  • on_database(): Specifies that the token should be defined on the database level.
  • on_scope(scope_name): Specifies that the token should be defined on a specific scope.

Examples

Define Token on Namespace

To define a token on the namespace level, you can use the following code:

#![allow(unused)]
fn main() {
let statement = define_token("oyelowo_token")
    .on_namespace()
    .type_(TokenType::PS512)
    .value("abrakradabra");
}

In the example above, the define_token statement defines a token named "oyelowo_token" on the namespace level. The token type is set to TokenType::PS512 and the value is set to "abrakradabra".

This will generate the following SQL statement:

DEFINE TOKEN oyelowo_token ON NAMESPACE TYPE PS512 VALUE 'abrakradabra';

Define Token on Database

To define a token on the database level, you can use the following code:

#![allow(unused)]
fn main() {
let statement = define_token("oyelowo_token")
    .on_database()
    .type_(TokenType::HS512)
    .value("anaksunamun");
}

In the example above, the define_token statement defines a token named "oyelowo_token" on the database level. The token type is set to TokenType::HS512 and the value is set to "anaksunamun".

This will generate the following SQL statement:

DEFINE TOKEN oyelowo_token ON DATABASE TYPE HS512 VALUE 'anaksunamun';

Define Token on Scope

To define a token on a specific scope, you can use the following code:

#![allow(unused)]
fn main() {
let statement = define_token("oyelowo_token")
    .on_scope("planet")
    .type_(TokenType::EDDSA)
    .value("abcde");
}

In the example above, the define_token statement defines a token named "oyelowo_token" on the scope "planet". The token type is set to TokenType::EDDSA and the value is set to "abcde".

This will generate the following SQL

statement:

DEFINE TOKEN oyelowo_token ON SCOPE planet TYPE EDDSA VALUE 'abcde';

Token Types

The TokenType enum represents the available token types in SurrealDB. Each token type corresponds to a specific algorithm or cryptographic scheme used for token generation and validation. The following token types are available:

  • EDDSA: EdDSA (Edwards-curve Digital Signature Algorithm)
  • ES256: ECDSA using P-256 and SHA-256
  • ES384: ECDSA using P-384 and SHA-384
  • ES512: ECDSA using P-521 and SHA-512
  • HS256: HMAC using SHA-256
  • HS384: HMAC using SHA-384
  • HS512: HMAC using SHA-512
  • PS256: RSASSA-PSS using SHA-256 and MGF1 with SHA-256
  • PS384: RSASSA-PSS using SHA-384 and MGF1 with SHA-384
  • PS512: RSASSA-PSS using SHA-512 and MGF1 with SHA-512
  • RS256: RSASSA-PKCS1-v1_5 using SHA-256
  • RS384: RSASSA-PKCS1-v1_5 using SHA-384
  • RS512: RSASSA-PKCS1-v1_5 using SHA-512

You can specify the desired token type when using the define_token statement by providing the corresponding TokenType enum variant.

You have now learned how to define tokens using the define_token statement. Tokens are essential for authentication and authorization in SurrealDB, allowing you to secure your data and control access to resources.

Define Scope Statement

The define_scope statement is used to define a scope in SurrealDB. Scopes provide a way to encapsulate a set of operations within a specific context or namespace. This documentation provides an overview of the syntax and usage of the define_scope statement.

Table of Contents

Syntax

The basic syntax of the define_scope statement is as follows:

#![allow(unused)]
fn main() {
define_scope(scope_name: &str) {
    // Scope definition
}
}
  • scope_name: The name of the scope to define.

The define_scope statement supports the following features:

  • Defining session duration for the scope.
  • Defining operations for the scope, such as signup and signin.

Examples

Define Scope on Namespace

To define a scope on a namespace with signup and signin operations, you can use the following code:

#![allow(unused)]
fn main() {
block! {
let user::Schema { email, pass } = &User::schema();
let email = "oyelowo@codebreather.com";
let password = "very-strong";

let token_def = define_scope("oyelowo_scope")
    .session(Duration::from_secs(45))
    .signup(
        create::<User>()
            .set(vec![
                email.equal_to(email),
                pass.equal_to(crypto::argon2::generate!(password)),
            ])
    )
    .signin(
        select(All).from(User::table()).where_(
            cond(email.equal(email))
                .and(crypto::argon2::compare!(pass, password)),
        ),
    );
}
}

In the example above, the define_scope statement defines a scope named "oyelowo_scope" on the namespace. The scope includes a session duration of 45 seconds. It also defines signup and signin operations within the scope. The signup operation uses the create statement with a non-raw query to create a new user record. The email and pass fields are set using parameter placeholders. The pass field is generated using the crypto::argon2::generate function. The signin operation performs a select query with conditions.

This will generate the following SQL statement:

DEFINE SCOPE oyelowo_scope SESSION 45s
    SIGNUP ( CREATE user SET email = $email, pass = crypto::argon2::generate($password) )
    SIGNIN ( SELECT * FROM user WHERE (email = email) AND (crypto::argon2::compare(pass, $password)) );

You can then use the defined scope in your queries by referencing the scope name.


Now you have learned how to define a scope using the define_scope statement. Scopes provide a way to encapsulate a set of operations within a specific context or namespace. Refer to the SurrealDB documentation for more information on scopes and their usage.

Define Table Statement

The define_table statement is used to define a table in SurrealDB. It allows you to specify various options and permissions for the table. This documentation provides an overview of the syntax and usage of the define_table statement.

Table of Contents

Syntax

The basic syntax of the define_table statement is as follows:

#![allow(unused)]
fn main() {
define_table(table)
    .drop()
    .as_(select_statement)
    .schemafull()
    .permissions(permission_statements);
}
  • table: The name of the table to define.

The define_table statement supports the following methods:

  • .drop(): Drops the existing table before defining it.
  • .as_(select_statement): Specifies a SELECT statement to populate the table.
  • .schemafull(): Defines the table with a schema.
  • .permissions(permission_statements): Specifies the permissions for the table.

Examples

Schemaless Table

To define a schemaless table with no permissions, you can use the following code:

#![allow(unused)]
fn main() {
let user = Table::from("user");
let statement = define_table(user).schemaless().permissions_none();
}

This will generate the following SQL statement:

DEFINE TABLE user SCHEMALESS PERMISSIONS NONE;

Schemaless Table with Permissions

To define a schemaless table with full permissions, you can use the following code:

#![allow(unused)]
fn main() {
let user = Table::from("user");
let statement = define_table(user).schemaless().permissions_full();
}

This will generate the following SQL statement:

DEFINE TABLE user SCHEMALESS PERMISSIONS FULL;

Define Table with Projection

A projection allows you to define a table based on a subset of columns from another table. It is similar to creating a view in a relational database. You can specify a projection using the as_ method and provide a SELECT statement as the projection definition. The selected columns and rows will be used to populate the defined table.

Here's an example that demonstrates how to define a table with a projection:

#![allow(unused)]
fn main() {
let user_table = Table::from("user");
let projection_statement = select(All).from(user_table).where_(age.greater_than(18));
let statement = define_table(user_table).as_(projection_statement);
}

This will generate the following SQL statement:

DEFINE TABLE user AS SELECT * FROM user WHERE age > 18;

In the example above, the define_table statement defines a table named "user" with a projection based on a SELECT statement. Only the rows that satisfy the condition age > 18 will be included in the table.

Define Table with Multiple Permissions

You can define a table with multiple permissions using the permissions method. The following example demonstrates various permission configurations:

#![allow(unused)]
fn main() {
let name = Field::new("name");
let user_table = Table::from("user");
let age = Field::new("age");
let country = Field::new("country");
let fake_id2 = sql::Thing::from(("user".to_string(), "oyedayo".to_string()));

let statement = define_table(user_table)
    .drop()
    .as_(
        select(All)
            .from(fake_id2)
            .where_(country.is("INDONESIA"))
            .order_by(order(&age).numeric().desc())
            .limit(20)
            .start(5),
    )
    .schemafull()
    .permissions(for_permission(Select).where_(age.greater_than_or_equal(18))) // Single works
    .permissions(for_permission([Create, Delete]).where_(name.is("Oyedayo"))) // Multiple
    .permissions([
        for_permission([Create, Delete]).where_(name.is("Oyedayo")),
        for_permission(Update).where_(age.less_than_or_equal(130)),
    ]);
}

This will generate the following SQL statement:

DEFINE TABLE user DROP SCHEMAFULL AS
    SELECT * FROM user:oyedayo
    WHERE country IS 'INDONESIA' ORDER BY age NUMERIC DESC
    LIMIT 20 START AT 5
PERMISSIONS
    FOR select
        WHERE age >= 18
    FOR create, delete
        WHERE name IS 'Oyedayo'
    FOR create, delete
        WHERE name IS 'Oyedayo'
    FOR update
        WHERE age <= 130;

In the example above, the define_table statement defines a table named "user". It drops the existing table, populates it with data from a SELECT statement, and sets various permissions based on conditions.

This concludes the documentation for the define_table statement. Use this statement to define tables in SurrealDB and specify the desired permissions, configurations, and projections.

Define Event Statement

The define_event statement is used to define an event in SurrealDB. It allows you to specify the conditions and actions associated with the event. This documentation provides an overview of the syntax and usage of the define_event statement.

Table of Contents

Syntax

The basic syntax of the define_event statement is as follows:

#![allow(unused)]
fn main() {
define_event(event_name)
    .on_table(table)
    .when(condition)
    .then(action);
}
  • event_name: The name of the event to define.
  • table: The name of the table where the event occurs.
  • condition: The condition that triggers the event.
  • action: The action to perform when the event is triggered.

The define_event statement supports the following methods:

  • .on_table(table): Specifies the table where the event occurs.
  • .when(condition): Specifies the condition that triggers the event.
  • .then(action): Specifies the action to perform when the event is triggered.

Using the cond! Macro

The cond! macro is a handy tool when defining conditions for the WHEN clause in the DEFINE EVENT statement. It provides a concise way to define conditions, enhancing readability while ensuring type safety.

Example:

#![allow(unused)]
fn main() {
let filter = cond!((strength > 5) && (strength < 15));
}

By using the cond! macro, you can effectively and expressively define conditions for the DEFINE EVENT statement.

For a more in-depth explanation and advanced usage of the cond! macro, refer to the dedicated chapter on helper macros.

Examples

Define Event with State Machine

To define an event with a state machine-like behavior, you can use the following code:

#![allow(unused)]
fn main() {
let age = Field::new("age");
let city = Field::new("city");
let fake_id = sql::Thing::from(("user".to_string(), "oyelowo".to_string()));
let user_table = Table::new("user");
let email_event = Event::new("email");

let query = define_event(email_event)
    .on_table(user_table)
    .when(cond(age.greater_than_or_equal(18)))
    .then(
        select(All)
            .from(fake_id)
            .where_(
                cond(city.is("Prince Edward Island"))
                    .and(city.is("NewFoundland"))
                    .or(city.like("Toronto")),
            )
            .limit(153)
            .start(10)
            .parallel(),
    );
}

This will generate the following SQL statement:

DEFINE EVENT email ON TABLE user WHEN age >= 18 THEN SELECT * FROM user:oyelowo WHERE (city IS 'Prince Edward Island') AND (city IS 'NewFoundland') OR (city ~ 'Toronto') LIMIT 153 START AT 10 PARALLEL;

In the example above, the define_event statement defines an event named "email" on the "user" table. It specifies that the event is triggered when the age is greater than or equal to 18. The action associated with the event is to perform a SELECT query on the "user:oyelowo" table with certain conditions and settings.

This concludes the documentation for the define_event statement. Use this statement to define events in SurrealDB and specify their conditions and actions.

Define Function Statement

The define_function! statement is used to define a custom function in SurrealDB. It allows you to define reusable logic that can be used within queries. This documentation provides an overview of the syntax and usage of the define_function! statement.

Table of Contents

Syntax

The basic syntax of the define_function! statement is as follows:

#![allow(unused)]
fn main() {
define_function!(function_name(parameter1: type1, parameter2: type2, ...) {
    // Function logic
});
}
  • function_name: The name of the function to define.
  • parameter1, parameter2, ...: The parameters of the function, along with their types.
  • function logic: The logic or operations to be performed by the function.

The define_function! statement supports the following features:

  • Defining function parameters and their types.
  • Writing custom logic or operations within the function body.
  • Returning values from the function.

Examples

Define Function with Parameters and Logic

To define a function with parameters and custom logic, you can use the following code:

#![allow(unused)]
fn main() {
define_function!(get_it(first: bool, last: string, birthday: string) {
    let person = "43";
    return person;
});
}

In the example above, the define_function! statement defines a function named "get_it" with three parameters: first, last, and birthday. The function body consists of assigning a value to the person variable and returning it.

This will generate the following SQL statement:

DEFINE FUNCTION get_it($first: bool, $last: string, $birthday: string) {
    LET $person = '43';

    RETURN $person;
};

You can then use the defined function in queries by calling it with the appropriate arguments.

Define Function with Complex Logic

Here's an example of defining a function with more complex logic and operations:

#![allow(unused)]
fn main() {
use surreal_models::SpaceShip;
use surreal_orm::{
    cond, index,
    statements::{create, define_function, if_, select},
    All, Buildable, Operatable, SchemaGetter, SetterAssignable, Model, ToRaw, NONE,
};

define_function!(get_person(first_arg: string, last_arg: string, birthday_arg: string) {
    let person = select(All)
        .from(SpaceShip::table())
        .where_(
            cond(SpaceShip::schema().id.equal(&first_arg))
                .and(SpaceShip::schema().name.equal(&last_arg))
                .and(SpaceShip::schema().created.equal(&birthday_arg)),
        );

    return if_(person.with_path::<SpaceShip>(index(0)).id.is_not(NONE))
                .then(person.with_path::<SpaceShip>(index(0)))
            .else_(
                create::<SpaceShip>().set(
                    vec![
                        SpaceShip::schema().id.equal_to(&first_arg),


 SpaceShip::schema().name.equal_to(&last_arg),
                        SpaceShip::schema().created.equal_to(&birthday_arg),
                    ]
                )
            ).end();
});
}

In the example above, the define_function! statement defines a function named "get_person" with three parameters: first_arg, last_arg, and birthday_arg. The function body consists of a complex logic that includes a SELECT statement, conditional checks, and the creation of a new record if the condition is not met.

This will generate the following SQL statement:

DEFINE FUNCTION get_person($first_arg: string, $last_arg: string, $birthday_arg: string) {
    LET $person = (SELECT * FROM space_ship WHERE (id = $first_arg) AND (name = $last_arg) AND (created = $birthday_arg));

    RETURN IF $person[0].id != NONE THEN $person[0] ELSE (CREATE space_ship SET id = $first_arg, name = $last_arg, created = $birthday_arg) END;
};

You can then use the defined function in queries by calling it with the appropriate arguments.

Using the Generated Function

To use the function defined using define_function!, you need to execute the generated statement before you can use the function in your queries. The generated statement is suffixed by _statement and contains the actual function definition. After executing the statement, you can use the function without the _statement suffix.

Here's an example of how to use the defined function:

#![allow(unused)]
fn main() {
// Define the function statement
let fn_statement = get_it_statement();

// Execute the statement to define the function
// This statement needs to be executed before the function can be used
fn_statement.run(db);

// Use the defined function in a query
let get_it_function = get_it(false, "3".to_string(), "3".to_string());

// Verify the generated function can be used in a query
assert_eq!(get_it_function.to_raw().build(), "get_it(false, '3', '3')");
assert_eq!(
    get_it_function.fine_tune_params(),
    "et_it($_param_00000001, $_param_00000002, $_param_00000003)"
);
}

In this example, we first define the function statement using the get_it_statement() macro. Then, we execute the generated statement using surreal_orm::execute() to define the function in SurrealDB. After that, we can use the defined function get_it() in our queries by calling it with the appropriate arguments.

Make sure to execute the statement to define the function before using it in your queries.


Now you have learned how to define custom functions using the define_function! macro, how to execute the generated statement to define the function, and how to use the defined function in your queries. Refer to the SurrealDB documentation for more information on custom functions and their usage.

Define Field Statement

The define_field statement is used to define a field in SurrealDB. It allows you to specify various options and permissions for the field. This documentation provides an overview of the syntax and usage of the define_field statement.

Table of Contents

Syntax

The basic syntax of the define_field statement is as follows:

#![allow(unused)]
fn main() {
define_field(field_name)
    .on_table(table)
    .type_(field_type)
    .value(default_value)
    .assert(assertion)
    .permissions(permission_statements);
}
  • field_name: The name of the field to define.
  • table: The name of the table where the field belongs.
  • field_type: The type of the field.
  • default_value (optional): The default value for the field.
  • assertion (optional): An assertion condition for the field.
  • permission_statements (optional): The permissions for the field.

The define_field statement supports the following methods:

  • .on_table(table): Specifies the table where the field belongs.
  • .type_(field_type): Specifies the type of the field.
  • .value(default_value): Specifies the default value for the field.
  • .assert(assertion): Specifies an assertion condition for the field.
  • .permissions(permission_statements): Specifies the permissions for the field.

Examples

Define Field with Full Configuration

To define a field with full configuration, including a default value, assertion condition, and permissions, you can use the following code:

#![allow(unused)]
fn main() {
let email = Field::new("email");
let user_table = Table::from("user");
let age = Field::new("age");
let statement = define_field(email)
    .on_table(user_table)
    .type_(String)
    .value("example@codebreather.com")
    .assert(cond(value().is_not(NONE)).and(value().like("is_email")))
    .permissions(for_permission(Permission::Select).where_(age.greater_than_or_equal(18))) // Single permission
    .permissions(for_permission(&[Permission::Create, Permission::Update]).where_(name.is("Oyedayo"))) // Multiple permissions
    .permissions(&[
        for_permission(&[Permission::Create, Permission::Delete]).where_(name.is("Oyedayo")),
        for_permission(Permission::Update).where_(age.less_than_or_equal(130)),
    ]);
}

This will generate the following SQL statement:

DEFINE FIELD email ON TABLE user TYPE string VALUE 'example@codebreather.com' \
    ASSERT ($value IS NOT NONE) AND ($value ~ 'is_email')
PERMISSIONS
    FOR select
        WHERE age >= 18
    FOR create, update
        WHERE name IS 'Oyedayo'
    FOR create, delete
        WHERE name IS 'Oyedayo'
    FOR update
        WHERE age <= 130;

In the example above, the define_field statement defines a field named "email" on the "user" table. It specifies the field type as String, sets a default value of 'example@codebreather.com', and adds an

assertion condition. It also sets different permissions for the field based on conditions.

Define Field with Simple Configuration

To define a field with a simple configuration, you can use the following code:

#![allow(unused)]
fn main() {
use FieldType::*;

let email = Field::new("email");
let user_table = Table::from("user");
let statement = define_field(email).on_table(user_table).type_(String);
}

This will generate the following SQL statement:

DEFINE FIELD email ON TABLE user TYPE string;

In the example above, the define_field statement defines a field named "email" on the "user" table. It specifies the field type as String without setting a default value, assertion condition, or permissions.

Field Types

The define_field statement supports various field types in SurrealDB. The available field types are:

  • any: Allows any data type supported by SurrealDB.
  • array: Represents a list.
  • bool: Represents true or false values.
  • datetime: Represents an ISO 8601 compliant date with time and time zone.
  • decimal: Represents any real number with arbitrary precision.
  • duration: Represents a length of time that can be added or subtracted from datetimes or other durations.
  • float: Represents a value stored in a 64-bit float.
  • int: Represents a value stored in a 64-bit integer.
  • number: Represents numbers without specifying the type, allowing SurrealDB to detect and store the number based on its minimal representation.
  • object: Represents formatted objects containing values of any supported type.
  • string: Represents a string value.
  • record: Represents a reference to another record in any table.
  • geometry: Represents a geometry type conforming to the GeoJSON format.

Geometry Types

The geometry field type allows you to define geometric fields in SurrealDB. The available geometry types are:

  • feature: Represents any geometric type.
  • point: Represents a point.
  • line: Represents a line.
  • polygon: Represents a polygon.
  • multipoint: Represents a multipoint.
  • multiline: Represents a multiline.
  • multipolygon: Represents a multipolygon.
  • collection: Represents a collection of geometry types.

Permission Types

The define_field statement allows you to define permissions for the field using permission types. The available permission types are:

  • Create: Allows creating new records with the field.
  • Read: Allows reading the field value from existing records.
  • Update: Allows updating the field value in existing records.
  • Delete: Allows deleting records that have the field.

These permission types can be used in the permissions method to define the desired access control for the field.

You have now learned how to define fields using the define_field statement, including different configuration options, field types, geometry types, and permission types. Use this statement to define fields in SurrealDB and specify their configurations and permissions.

Define Index Statement

The define_index statement is used to define an index in SurrealDB. Indexes are used to improve the performance of queries by creating data structures that allow for efficient lookup and retrieval of data. This documentation provides an overview of the syntax and usage of the define_index statement.

Table of Contents

Syntax

The basic syntax of the define_index statement is as follows:

#![allow(unused)]
fn main() {
define_index(index_name: Index)
    .on_table(table: Table)
    .fields(arr![fields: Field])
    .columns(arr![columns: Field])
    .unique()
}
  • index_name: The name of the index to define.
  • table: The name of the table on which the index is defined.
  • fields: An array of fields to include in the index.
  • columns: An array of columns to include in the index.
  • unique: Specifies that the index should enforce uniqueness.

The define_index statement supports the following features:

  • Defining indexes with fields or columns.
  • Specifying uniqueness for the index.

Examples

Define Index with Single Field

To define an index with a single field, you can use the following code:

#![allow(unused)]
fn main() {
let email = Field::new("email");

let query = define_index("userEmailIndex")
    .on_table("user")
    .fields(email)
    .unique();
}

In the example above, the define_index statement defines an index named "userEmailIndex" on the table "user" with the "email" field. The index is marked as unique.

This will generate the following SQL statement:

DEFINE INDEX userEmailIndex ON TABLE user FIELDS email UNIQUE;

Define Index with Single Column

To define an index with a single column, you can use the following code:

#![allow(unused)]
fn main() {
let email = Field::new("email");

let query = define_index("userEmailIndex")
    .on_table("user")
    .columns(email)
    .unique();
}

In the example above, the define_index statement defines an index named "userEmailIndex" on the table "user" with the "email" column. The index is marked as unique.

This will generate the following SQL statement:

DEFINE INDEX userEmailIndex ON TABLE user COLUMNS email UNIQUE;

Define Index with Multiple Fields

To define an index with multiple fields, you can use the following code:

#![allow(unused)]
fn main() {
let age = Field::new("age");
let name = Field::new("name");
let email = Field::new("email");
let dob = Field::new("dob");

let query = define_index("alien_index")
    .on_table("alien")
    .fields(arr![age, name, email, dob])
    .unique();
}

In the example above, the define_index statement defines an index named "alien_index" on the table "alien" with the "age", "name", "email", and "dob" fields. The index is marked as unique.

This will generate the following SQL statement:

DEFINE INDEX alien_index ON TABLE alien FIELDS age, name, email, dob UNIQUE;

Define Index with Multiple Columns

To define an index with multiple columns, you can use the

following code:

#![allow(unused)]
fn main() {
let age = Field::new("age");
let name = Field::new("name");
let email = Field::new("email");
let dob = Field::new("dob");

let query = define_index("alien_index")
    .on_table("alien")
    .columns(arr![age, name, email, dob])
    .unique();
}

In the example above, the define_index statement defines an index named "alien_index" on the table "alien" with the "age", "name", "email", and "dob" columns. The index is marked as unique.

This will generate the following SQL statement:

DEFINE INDEX alien_index ON TABLE alien COLUMNS age, name, email, dob UNIQUE;

You have now learned how to define indexes using the define_index statement. Indexes improve query performance by creating data structures that allow for efficient lookup and retrieval of data. Use indexes strategically to optimize the performance of your database queries.

Define Param Statement

The define_param statement is used to define a parameter in SurrealDB. Parameters provide a way to store and reuse values within queries. This documentation provides an overview of the syntax and usage of the define_param statement.

Table of Contents

Syntax

The basic syntax of the define_param statement is as follows:

#![allow(unused)]
fn main() {
define_param(param_name: Param) {
    // Parameter definition
}
}
  • param_name: The name of the parameter to define.

The define_param statement supports the following features:

  • Assigning a value to the parameter.

Examples

Define Param Statement Usage

To define a parameter with a specific value, you can use the following code:

#![allow(unused)]
fn main() {
// Define the parameter
fn endpoint_base() -> Param {
    Param::new("endpoint_base")
}

// Define the param definition itself. This must be run against the database first to use the param.
let statement = define_param(endpoint_base()).value("https://dummyjson.com");
}

In the example above, the define_param statement defines a parameter named "endpoint_base" with a value of "https://dummyjson.com".

Before using the defined parameter, it is important to run the define_param statement to register the parameter with the database. You can do this by calling the run method on the statement object, passing the SurrealDB instance as an argument:

#![allow(unused)]
fn main() {
statement.run(db);
}

Once the define_param statement has been executed, you can use the defined parameter ($endpoint_base) across your codebase in queries and other operations.

You can then reference the parameter name in your queries to utilize the stored value.

#![allow(unused)]
fn main() {
let query = select(All).from(User::table()).where_(endpoint_base().equal_to("https://dummyjson.com"));
}

Now you have learned how to define a parameter using the define_param statement. Parameters provide a way to store and reuse values within queries. Remember to execute the define_param statement to register the parameter with the database before using it in your codebase. Refer to the SurrealDB documentation for more information on parameters and their usage.

Remove Statement

The REMOVE statement in Surreal ORM is used to remove various elements from the database, such as databases, events, fields, indexes, logins, scopes, namespaces, tables, and tokens. This documentation covers the usage and examples of the REMOVE statement for each of these elements.

Table of Contents

Remove Database

The REMOVE DATABASE statement is used to remove a database from the SurrealDB. Here's an example:

#![allow(unused)]
fn main() {
assert_eq!(
    remove_database("oyelowo").build(),
    "REMOVE DATABASE oyelowo;"
);
}

The generated SQL query for this code block would be REMOVE DATABASE oyelowo;.

Remove Event

The REMOVE EVENT statement is used to remove an event from a table. Here's an example:

#![allow(unused)]
fn main() {
let user = Table::new("user");
let party = Event::new("party");

let statement = remove_event(party).on_table(user);
assert_eq!(statement.build(), "REMOVE EVENT party ON TABLE user;");
}

The generated SQL query for this code block would be REMOVE EVENT party ON TABLE user;.

Remove Field

The REMOVE FIELD statement is used to remove a field from a table. Here's an example:

#![allow(unused)]
fn main() {
let user = Table::new("user");
let name = Field::new("name");

let statement = remove_field(name).on_table(user);
assert_eq!(statement.build(), "REMOVE FIELD name ON TABLE user;");
}

The generated SQL query for this code block would be REMOVE FIELD name ON TABLE user;.

Remove Index

The REMOVE INDEX statement is used to remove an index from a table. Here's an example:

#![allow(unused)]
fn main() {
let user = Table::new("user");
let party = TableIndex::new("party");

let statement = remove_index(party).on_table(user);
assert_eq!(statement.build(), "REMOVE INDEX party ON TABLE user;");
}

The generated SQL query for this code block would be REMOVE INDEX party ON TABLE user;.

Remove Login

The REMOVE LOGIN statement is used to remove a login from either a namespace or a database. Here are examples for removing a login on a namespace and a database:

#![allow(unused)]
fn main() {
let login = Login::new("login");

// Remove login on a namespace
let statement = remove_login(login).on_namespace();
assert_eq!(statement.build(), "REMOVE LOGIN login ON NAMESPACE;");

// Remove login on a database
let statement = remove_login(login).on_database();
assert_eq!(statement.build(), "REMOVE LOGIN login ON DATABASE;");
}

The generated SQL queries for these code blocks would be REMOVE LOGIN login ON NAMESPACE; and REMOVE LOGIN login ON DATABASE; respectively.

Remove Scope

The REMOVE SCOPE statement is used to remove a scope from the SurrealDB. Here's an example:

#![allow(unused)]
fn main() {
let scope = Scope::new("scope");
let statement = remove_scope(scope);
assert_eq!(statement.build(), "REMOVE SCOPE scope;");
}

The generated SQL query for this code block would be REMOVE SCOPE scope;.

Remove Namespace

The REMOVE NAMESPACE statement is used to

remove a namespace from the SurrealDB. Here's an example:

#![allow(unused)]
fn main() {
let namespace = Namespace::new("namespace");
let statement = remove_namespace(namespace);
assert_eq!(statement.build(), "REMOVE NAMESPACE namespace;");
}

The generated SQL query for this code block would be REMOVE NAMESPACE namespace;.

Remove Table

The REMOVE TABLE statement is used to remove a table from the SurrealDB. Here's an example:

#![allow(unused)]
fn main() {
let table = Table::new("table");
let statement = remove_table(table);
assert_eq!(statement.build(), "REMOVE TABLE table;");
}

The generated SQL query for this code block would be REMOVE TABLE table;.

Remove Token

The REMOVE TOKEN statement is used to remove a token from either a namespace or a database. Here are examples for removing a token on a namespace and a database:

#![allow(unused)]
fn main() {
let token = Token::new("token");

// Remove token on a namespace
let statement = remove_token(token).on_namespace();
assert_eq!(statement.build(), "REMOVE TOKEN token ON NAMESPACE;");

// Remove token on a database
let statement = remove_token(token).on_database();
assert_eq!(statement.build(), "REMOVE TOKEN token ON DATABASE;");
}

The generated SQL queries for these code blocks would be REMOVE TOKEN token ON NAMESPACE; and REMOVE TOKEN token ON DATABASE; respectively.

That concludes the documentation for the REMOVE statement in Surreal ORM. Use the examples and explanations provided to effectively remove various elements from the database.

Info Statement

The INFO statement in Surreal ORM is used to retrieve information about various elements in the database, such as key-value pairs, namespaces, databases, scopes, and tables. This documentation covers the usage and examples of the INFO statement for each of these elements.

Table of Contents

Info for Key-Value (KV) Pairs

The INFO FOR KV statement is used to retrieve information about key-value pairs in the SurrealDB. Here's an example:

#![allow(unused)]
fn main() {
let statement = info_for().kv().build();
assert_eq!(statement, "INFO FOR KV;");
}

The generated SQL query for this code block would be INFO FOR KV;.

Info for Namespaces

The INFO FOR NS statement is used to retrieve information about namespaces in the SurrealDB. Here's an example:

#![allow(unused)]
fn main() {
let statement = info_for().namespace().build();
assert_eq!(statement, "INFO FOR NS;");
}

The generated SQL query for this code block would be INFO FOR NS;.

Info for Databases

The INFO FOR DB statement is used to retrieve information about databases in the SurrealDB. Here's an example:

#![allow(unused)]
fn main() {
let statement = info_for().database().build();
assert_eq!(statement, "INFO FOR DB;");
}

The generated SQL query for this code block would be INFO FOR DB;.

Info for Scopes

The INFO FOR SCOPE statement is used to retrieve information about a specific scope in the SurrealDB. Here's an example:

#![allow(unused)]
fn main() {
let statement = info_for().scope("test_scope").build();
assert_eq!(statement, "INFO FOR SCOPE test_scope;");
}

The generated SQL query for this code block would be INFO FOR SCOPE test_scope;.

Info for Tables

The INFO FOR TABLE statement is used to retrieve information about a specific table in the SurrealDB. Here's an example:

#![allow(unused)]
fn main() {
let statement = info_for().table("test_table").build();
assert_eq!(statement, "INFO FOR TABLE test_table;");
}

The generated SQL query for this code block would be INFO FOR TABLE test_table;.

That concludes the documentation for the INFO statement in Surreal ORM. Use the examples and explanations provided to retrieve information about key-value pairs, namespaces, databases, scopes, and tables effectively.

Sleep Statement

The SLEEP statement in Surreal ORM is used to introduce a delay or pause in the execution of a program or query. It allows you to control the timing of your operations by specifying a duration to wait before proceeding further. This documentation covers the usage and examples of the SLEEP statement.

Table of Contents

Sleep Statement Usage

The SLEEP statement is used to introduce a pause in the program or query execution. It takes a duration parameter to specify the length of the pause. Here's an example:

#![allow(unused)]
fn main() {
use std::time::Duration;

let statement = sleep(Duration::from_secs(43));
}

In the code snippet above, we create a Duration object with a duration of 43 seconds and pass it to the sleep function to create the SLEEP statement.

You can use the SLEEP statement to introduce delays or pauses in your program or query execution to control the timing of your operations effectively.

That concludes the documentation for the SLEEP statement in Surreal ORM. Use the provided examples and explanations to introduce pauses in your program or query execution as needed.

Operators

SQL Query Builder: Field Operators

All these operators can also be chained together to create more complex conditions. For instance:

#![allow(unused)]
fn main() {
age.outside(18, 65).and(age.not_equal(99));
p1.outside(18, 65).and(p1.not_equal(99));
}

The chaining of these operators is quite flexible and allows for the construction of complex query logic in a clear and concise manner.

Table of Contents

Introduction

This document provides an overview of the different SQL query field operators available in our SQL Query Builder. For each operator, a brief description, usage, and examples are provided.

Comparison Operators

equal, eq

The equal operator checks if the given field is equal to the provided value. It returns true if the condition is met, and false otherwise.

The eq operator is an alias for equal.

Usage:

#![allow(unused)]
fn main() {
use surreal_orm::*;
let field = Field::new("field");
field.equal(5);
field.eq(9);

## `not_equal`, `neq`

The `not_equal` operator checks if the given field is not equal to the provided value. It returns true if the condition is met, and false otherwise.

The `neq` operator is an alias for `not_equal`.

Usage:

```rust
use surreal_orm::*;
let ref price = Field::new("price");
price.not_equal(100);
price.neq(100);
}

exactly_equal

The exactly_equal operator checks if the given field is exactly equal to the provided value. It is generally used for fields that are binary or have specific precision requirements.

Usage:

#![allow(unused)]
fn main() {
use surreal_orm::*;
let name = Field::new("name");
let p1 = Field::new("p1");
name.exactly_equal("Oyelowo");
p1.exactly_equal(3.14);
}

any_equal

The any_equal operator checks if any value in a list of values is equal to the given field.

Usage:

#![allow(unused)]
fn main() {
use surreal_orm::*;
let status = Field::new("status");
let p1 = Field::new("p1");
status.any_equal(vec!["ACTIVE", "IN PROGRESS"]);
p1.any_equal(vec!["APPLE", "BANANA"]);
}

all_equal

The all_equal operator checks if all values in a list are equal to the given field. It's a niche operator and has limited use.

Usage:

#![allow(unused)]
fn main() {
use surreal_orm::*;
let id = Field::new("id");
let p1 = Field::new("p1");
id.all_equal([1, 3, 5]);
p1.all_equal([2, 2, 2]);
}

like

The like operator checks if the given field matches the provided pattern. % is used as a wildcard character.

Usage:

#![allow(unused)]
fn main() {
use surreal_orm::*;
let name = Field::new("name");
let p1 = Field::new("p1");
name.like("Jo");
p1.like("son");
}

not_like

The not_like operator checks if the given field does not match the provided pattern. % is used as a wildcard character.

Usage:

#![allow(unused)]
fn main() {
use surreal_orm::*;
let name = Field::new("name");
let p1 = Field::new("p1");
name.not_like("Jo%");
p1.not_like("%son");
}

any_like

The any_like operator checks if any value in a list of values matches the given field.

Usage:

#![allow(unused)]
fn main() {
use surreal_orm::*;
let status = Field::new("status");
let p1 = Field::new("p1");
status.any_like(vec!["ACTIVE", "IN PROGRESS"]);
p1.any_like(vec!["APPLE", "BANANA"]);
}

all_like

The all_like operator checks if all values in a list match the given field. It's a niche operator and has limited use.

Usage:

#![allow(unused)]
fn main() {
use surreal_orm::*;
let id = Field::new("id");
let p1 = Field::new("p1");
id.all_like(vec!["1", "2", "4"]);
p1.all_like(vec!["2", "2", "2"]);
}

less_than, lt

The less_than operator checks if the given field is less than the provided value.

Usage:

#![allow(unused)]
fn main() {
use surreal_orm::*;
let age = Field::new("age");
let p1 = Field::new("p1");
age.less_than(18);
p1.lt(100);
}

less_than_or_equal, lte

The less_than_or_equal operator checks if the given field is less than or equal to the provided value.

Usage:

#![allow(unused)]
fn main() {
use surreal_orm::*;
let age = Field::new("age");
let p1 = Field::new("p1");
age.less_than_or_equal(18);
p1.lte(100);
}

greater_than, gt

The greater_than operator checks if the given field is greater than the provided value.

Usage:

#![allow(unused)]
fn main() {
use surreal_orm::*;
let age = Field::new("age");
let p1 = Field::new("p1");
age.greater_than(18);
p1.gt(100);
}

greater_than_or_equal, gte

The greater_than_or_equal operator checks if the given field is greater than or equal to the provided value.

Usage:

#![allow(unused)]
fn main() {
use surreal_orm::*;
let age = Field::new("age");
let p1 = Field::new("p1");
age.greater_than_or_equal(18);
p1.gte(100);
}

add, plus, +

The add operator adds a value to the given field.

Usage:

#![allow(unused)]
fn main() {
use surreal_orm::*;
let salary = Field::new("salary");
let age = Field::new("age");
let p1 = Field::new("p1");
salary.add(500);
age + 500;
p1.plus(200);
}

subtract, minus, -

The subtract operator subtracts a value from the given field.

Usage:

#![allow(unused)]
fn main() {
use surreal_orm::*;
let salary = Field::new("salary");
let age = Field::new("age");
let p1 = Field::new("p1");
salary.subtract(500);
age - 500;
p1.subtract(200);
}

multiply, mul, *

The multiply operator multiplies the given field by the provided value.

Usage:

#![allow(unused)]
fn main() {
use surreal_orm::*;
let salary = Field::new("salary");
let quantity = Field::new("quantity");
let p1 = Field::new("p1");
quantity.multiply(price);
salary * 434;
p1.multiply(10);
}

This will generate the following SQL statement:

quantity * price
p1 * 10

divide, div, /

The divide operator divides the given field by the provided value.

Usage:

#![allow(unused)]
fn main() {
use surreal_orm::*;
let salary = Field::new("salary");
let quantity = Field::new("quantity");
let count = Field::new("count");
let param = Param::new("param");
salary.divide(343);
quantity / count;
param.div(2);
}

power, pow

The power operator raises the given field to the power of the provided value.

Usage:

#![allow(unused)]
fn main() {
use surreal_orm::*;
let length = Field::new("length");
let p1 = Field::new("p1");
length.power(2);
p1.power(3);
}

trthy_and

The truthy_and operator performs a logical AND operation between the field and the provided value.

Usage:

#![allow(unused)]
fn main() {
use surreal_orm::*;
let is_active = Field::new("is_active");
is_active.truthy_and(true);
}

truthy_or

The truthy_or operator performs a logical OR operation between the field and the provided value.

Usage:

#![allow(unused)]
fn main() {
use surreal_orm::*;
let is_active = Field::new("is_active");
is_active.truthy_or(is_paid);
}

This will generate the following SQL statement:

is_active OR is_paid
p1 OR p2

and

The and operator is used to combine multiple conditions in a WHERE clause to create more complex conditions. It returns true if all conditions are true.

Usage:

#![allow(unused)]
fn main() {
use surreal_orm::*;
let price = Field::new("price");
price.and(54).and(92);
}

or

The or operator is used to combine multiple conditions in a WHERE clause to create more complex conditions. It returns true if at least one of the conditions is true.

Usage:

#![allow(unused)]
fn main() {
use surreal_orm::*;
let  = Field::new("price");
is_active.or(is_paid);
p1.or(p2);
}

This will generate the following SQL statement:

is

The is operator compares if a field is equal to a specific value.

Usage:

#![allow(unused)]
fn main() {
use surreal_orm::*;
let age = Field::new("age");
let p1 = Field::new("p1");
age.is(21);
p1.is("John");
}

is_not

The is_not operator compares if a field is not equal to a specific value.

Usage:

#![allow(unused)]
fn main() {
use surreal_orm::*;
let age = Field::new("age");
age.is_not(21);
p1.is_not("John");
}

contains

The contains operator checks if a field contains a specific value.

Usage:

#![allow(unused)]
fn main() {
use surreal_orm::*;
let names = Field::new("names");
names.contains("John");
}

contains_not

The contains_not operator checks if a field does not contain a specific value.

Usage:

#![allow(unused)]
fn main() {
use surreal_orm::*;
let names = Field::new("names");
names.contains_not("John");
}

contains_all

The contains_all operator checks if a field contains all specified values.

Usage:

#![allow(unused)]
fn main() {
use surreal_orm::*;
let tags = Field::new("tags");
tags.contains_all(vec!["novel", "adventure"]);
}

contains_any

The contains_any operator checks if a field contains any of the specified values.

Usage:

#![allow(unused)]
fn main() {
use surreal_orm::*;
let tags = Field::new("tags");
tags.contains_any(vec!["novel", "adventure"]);
}

contains_none

The contains_none operator checks if a field does not contain any of the specified values.

Usage:

#![allow(unused)]
fn main() {
use surreal_orm::*;
let tags = Field::new("tags");
tags.contains_none(vec!["novel", "adventure"]);
}

inside and in_

The inside and in_ operators check if a field's value is within a specified array of values.

Usage:

#![allow(unused)]
fn main() {
use surreal_orm::*;
let scores = Field::new("scores");
let p1 = Field::new("p1");
scores.inside(vec![20, 30, 40]);
p1.inside(vec![20, 30, 40]);
}

not_inside

The not_inside operator checks if a field's value is not within a specified array of values.

Usage:

#![allow(unused)]
fn main() {
use surreal_orm::*;
let scores = Field::new("scores");
scores.not_inside(vec![20, 30, 40]);
}

all_inside

The all_inside operator checks if all values in a field are within a specified array of values.

Usage:

#![allow(unused)]
fn main() {
use surreal_orm::*;
let tags = Field::new("tags");
tags.all_inside(["novel", "adventure", "mystery"]);
}

any_inside

The any_inside operator checks if any value in a field is within a specified array of values.

Usage:

#![allow(unused)]
fn main() {
use surreal_orm::*;
let tags = Field::new("tags");
tags.any_inside(vec!["novel", "adventure", "mystery"]);
}

none_inside

The none_inside operator checks if none of the values in a field are within a specified array of values.

Usage:

#![allow(unused)]
fn main() {
use surreal_orm::*;
let tags = Field::new("tags");
tags.none_inside(["novel", "adventure", "mystery"]);
}

outside

The outside operator checks whether a geometry value is outside another geometry value.

Usage:

#![allow(unused)]
fn main() {
use surreal_orm::*;
let point = Field::new("point");
let area = Param::new("area");
point.outside(area);
}

intersects

The intersects operator checks whether a geometry value intersects annother geometry value.

Usage:

#![allow(unused)]
fn main() {
use surreal_orm::*;
let area1 = Field::new("area1");
let area2 = Field::new("area2");
area1.intersects(area2);
}

Also, note the distinction between Field and Param in the usage and examples. A Field represents a column in a database table, while a Param represents a parameter that could be used in the for value assignment. These are interchangeable in the context of these operators, meaning that you can apply the same operators whether you are comparing fields or parameters.

Conclusion

This document covers the complete list of SQL Query Builder field operators. Using these operators will help you build complex and robust SQL queries. Always ensure that you use the correct operator for your specific needs to prevent unexpected results or errors.

Parameters

Parameters in SurrealDB serve as essential tools for storing and manipulating data within queries. The ORM simplifies this process, making it intuitive and streamlined.

Table of Contents

Query Creation and Execution

The ORM abstracts away much of the complexity involved in crafting queries. To calculate the average strength of weapons, for instance:

#![allow(unused)]
fn main() {
let db = Surreal::new::<Mem>(()).await.unwrap();
db.use_ns("test").use_db("test").await.unwrap();

let ref weapon = Weapon::table();
let weapon::Schema { ref strength, .. } = Weapon::schema();
let weapon_stats::Schema {
    averageStrength, ..
} = WeaponStats::schema();

let generated_weapons = (0..=14)
    .map(|i| Weapon {
        name: format!("weapon_{}", i),
        strength: i,
        ..Default::default()
    })
    .collect::<Vec<_>>();

insert(generated_weapons).return_many(db.clone()).await?;


let created_stats_statement = create::<WeaponStats>().set(averageStrength.equal_to(block! {
    LET strengths = select_value(strength).from(weapon);
    LET total = math::sum!(strengths);
    LET count = count!(strengths);
    LET distance = 65;
    RETURN math::ceil!((((total / count) * (count * total)) / (total + 4)) * 100);
}));


assert_eq!(
    created_stats_statement.to_raw().build(),
    "CREATE weapon_stats SET averageStrength = {\n\
            LET $strengths = (SELECT VALUE strength FROM weapon);\n\n\
            LET $total = math::sum($strengths);\n\n\
            LET $count = count($strengths);\n\n\
            RETURN math::ceil(((($total / $count) * ($count * $total)) / ($total + 4)) * 100);\n\
            };"
);

assert_eq!(
    created_stats_statement.fine_tune_params(),
    "CREATE weapon_stats SET averageStrength = {\n\
            LET $strengths = $_param_00000001;\n\n\
            LET $total = math::sum($strengths);\n\n\
            LET $count = count($strengths);\n\n\
            RETURN math::ceil(((($total / $count) * ($count * $total)) / ($total + $_param_00000002)) * $_param_00000003);\n\
            };"
);
}

This block of code demonstrates the ORM's ability to define and utilize parameters within queries.

Native ORM Parameters

SurrealDB provides a set of predefined variables designed to simplify query development. While these predefined parameters can be utilized directly within your queries, it's crucial to note that you cannot declare new parameters with these specific names. The ORM is equipped with built-in functions that represent these standard SurrealDB parameters. A function like after() corresponds to the $after parameter in raw queries. These functions allow developers to interact with the database at a high level, abstracting away the complexity of raw queries.

To bridge this system with the ORM, these predefined variables are represented by functions in the ORM, each mimicking the name of the corresponding parameter:

Here's a list of some of the prominent parameters and their descriptions:

FunctionParameterDescription
auth()$authRepresents the currently authenticated scope user.
token()$tokenRepresents values held inside the JWT token used for the current session.
session()$sessionValues from session functions as an object.
before()$beforeValue before a field mutation.
after()$afterValue post field mutation.
value()$valuePost mutation value (identical to $after for events).
input()$inputInitially inputted value in a field definition; the value clause might have modified the $value variable.
parent()$parentParent record in a subquery.
event()$eventType of table event triggered on an event.

These native functions simplify the query-writing process, enabling developers to focus on the logic of their application without getting bogged down by the intricacies of the database language.

Advanced Parameter Name Creation

For those requiring further customization, the create_param_name_fn!() macro is available. This macro not only aids in generating custom parameter names but also supports field traversal using parameter paths. Typically though, you will use this with the define_param statement when you want to define a constant global variable. However, in a typical let statement (e.g used within the block! macro), this is automatically handled.

Suppose you want to create a custom parameter name for a user's age. Using the macro:

#![allow(unused)]
fn main() {
create_param_name_fn!(user_age);
}

If you would like to add a rust doc comment, you can do so as shown below:

#![allow(unused)]
fn main() {
create_param_name_fn!(
    /// $user_age represents the age of a user
    => userAge
);
}

To use the param name created above, you can invoke it as user_age

This means that any parameter name created with this macro can be used for field traversal. For more information on field traversal, refer to the Field Traversal chapter.

Transaction Management in Surreal ORM

Surreal ORM provides transaction management capabilities to ensure the integrity and consistency of database operations. This allows you to group multiple database operations into a single atomic unit that can be committed or canceled as a whole. This documentation covers the Begin Transaction, Commit Statement, and Cancel Transaction features in Surreal ORM.

Table of Contents

Begin Transaction

The begin_transaction statement in Surreal ORM marks the beginning of a transaction. It sets the context for a series of database operations that should be treated as a single atomic unit. By starting a transaction, you can ensure the integrity and consistency of your database operations.

To begin a transaction, you can use the begin_transaction statement. Let's see an example:

#![allow(unused)]
fn main() {
let db = Surreal::new::<Mem>(()).await.unwrap();
db.use_ns("test").use_db("test").await.unwrap();

begin_transaction()
...
// Perform database operations within the transaction
;

// or
block!{
    BEGIN TRANSACTION;
    ...
// Perform database operations within the transaction
}


Ok(())
}

In the code snippet above, the begin_transaction statement is used to start a transaction. This sets the context for the subsequent database operations.

Commit Statement

The commit statement in Surreal ORM is used to commit a transaction and save the changes made within the transaction. It ensures that the changes are durable and permanent in the database.

Using block! Macro with Commit Statement also Within Block for Chaining Multiple Statements

To perform a transaction and commit the changes, you can use the block! macro to chain multiple statements together. The commit_transaction statement is used within the block! macro to explicitly indicate the commitment of the transaction. Let's take a look at an example:

#![allow(unused)]
fn main() {
let db = Surreal::new::<Mem>(()).await.unwrap();
db.use_ns("test").use_db("test").await.unwrap();

let ref id1 = Account::create_id("one".into());
let ref id2 = Account::create_id("two".into());
let acc = Account::schema();

let amount_to_transfer = 300.00;

block! {
    BEGIN TRANSACTION;

    LET acc1 = create().content(Account {
        id: id1.clone(),
        balance: 135_605.16,
    });
    LET acc2 = create().content(Account {
        id: id2.clone(),
        balance: 91_031.31,
    });

    LET updated1 = update::<Account>(id1).set(acc.balance.increment_by(amount_to_transfer));
    LET update2 = update::<Account>(id2).set(acc.balance.decrement_by(amount_to_transfer));

    COMMIT TRANSACTION;
};

Ok(())
}

In the code snippet above, the block! macro is used to define a transaction with multiple statements. The LET statement is used to bind variables `

acc1,acc2,updated1, andupdate2to the respective statements. TheBEGIN TRANSACTIONstatement marks the start of the transaction, and theCOMMIT TRANSACTION` statement explicitly commits the transaction.

Using the block! macro with the commit_transaction statement within the block provides a clear and concise way to define a transaction and commit the changes.

Using begin_transaction function with block! Macro for Chaining Multiple Statements

Another recommended approach is to use the block! macro to chain multiple statements together within a transaction. The commit_transaction statement is called separately after the block! macro to explicitly commit the transaction. Let's see an example:

#![allow(unused)]
fn main() {
let db = Surreal::new::<Mem>(()).await.unwrap();
db.use_ns("test").use_db("test").await.unwrap();

let ref id1 = Account::create_id("one".into());
let ref id2 = Account::create_id("two".into());
let acc = Account::schema();

let amount_to_transfer = 300.00;

let transaction_query = begin_transaction()
    .query(block! {
        LET acc1 = create().content(Account {
            id: id1.clone(),
            balance: 135_605.16,
        });
        LET acc2 = create().content(Account {
            id: id2.clone(),
            balance: 91_031.31,
        });

        LET updated1 = update::<Account>(id1).set(acc.balance.increment_by(amount_to_transfer));
        LET update2 = update::<Account>(id2).set(acc.balance.decrement_by(amount_to_transfer));
    })
    .commit_transaction();

transaction_query.run(db.clone()).await?;

Ok(())
}

In this approach, the block! macro is used to define a transaction block that includes multiple statements. The BEGIN TRANSACTION and COMMIT TRANSACTION statements mark the start and end of the transaction, respectively. The LET statement is used to bind variables to the statements within the block.

Using the block! macro for chaining multiple statements and explicitly committing the transaction provides a more structured and organized way to handle complex transactions.

The less recommended approach involves chaining multiple statements directly without using the block! macro. Although functional, this approach may feel less ergonomic, especially when there is a need to bind and share variables within the statements.

Chaining Multiple Statements Directly

Here's an example of chaining multiple statements directly without using the block! macro:

#![allow(unused)]
fn main() {
#[tokio::test]
async fn test_transaction_commit_increment_and_decrement_update() -> SurrealOrmResult<()> {
    let db = Surreal::new::<Mem>(()).await.unwrap();
    db.use_ns("test").use_db("test").await.unwrap();

    let ref id1 = Account::create_id("one".into());
    let ref id2 = Account::create_id("two".into());
    let amount_to_transfer = 300.00;

    let acc = Account::schema();

    begin_transaction()
        .query(create().content(Account {
            id: id1.clone(),
            balance: 135_605.16,
        }))
        .query(create().content(Account {
            id: id2.clone(),
            balance: 91_031.31,
        }))
        .query(update::<Account>(id1).set(acc.balance.increment_by(amount_to_transfer)))
        .query(update::<Account>(id2).set(acc.balance.decrement_by(amount_to_transfer)))
        .commit_transaction()
        .run(db.clone())
        .await?;

    // Assertions and other code...

    Ok(())
}
}

In this approach, multiple statements are chained directly within the transaction. The create and update statements

are used to perform operations on the Account table.

The less recommended approach of chaining multiple statements directly can be less ergonomic, especially when dealing with complex transactions that require variable bindings and subqueries.

It is generally recommended to use the recommended approaches with the block! macro for better readability, automation of variable bindings, and subquery handling.

Cancel Transaction

The cancel transaction feature in Surreal ORM allows you to roll back a transaction and discard the changes made within the transaction. It is useful when you want to undo a series of database operations within a transaction.

To cancel a transaction, you can use the cancel_transaction statement. Let's see an example:

#![allow(unused)]
fn main() {
let db = Surreal::new::<Mem>(()).await.unwrap();
db.use_ns("test").use_db("test").await.unwrap();

let transaction_query = begin_transaction()
    .query(create().content(Account {
        id: Account::create_id("one".into()),
        balance: 135_605.16,
    }))
    .cancel_transaction();

transaction_query.run(db.clone()).await?;

Ok(())
}

In the code snippet above, the cancel_transaction statement is used to cancel the ongoing transaction. This ensures that any changes made within the transaction are discarded, and the database state remains unchanged.

Handling Transactions with Database Operations

When performing database operations within a transaction, it is important to ensure that the operations are executed as a single atomic unit. Surreal ORM provides transaction management features to facilitate this.

To handle transactions with database operations, you can follow these steps:

  1. Begin the transaction using the begin_transaction statement.
  2. Chain the necessary database operations using the appropriate ORM statements.
  3. Use the recommended approaches described earlier to define and commit the transaction.
  4. If needed, use the cancel_transaction statement to cancel the transaction and discard any changes.

By following these steps, you can ensure the integrity and consistency of your database operations and handle transactions effectively.

That concludes the documentation for the Begin Transaction, Commit Statement, and Cancel Transaction features in Surreal ORM. Use the recommended approaches to perform transactions, commit changes, handle cancellations, and manage your database operations effectively.

Functions

Overview

Array Functions

Surreal ORM provides a set of array functions that allow you to manipulate and perform operations on arrays. These functions are designed to work with arrays of various types, including vectors, fields, and parameters. This documentation covers the usage and examples of the array functions available in Surreal ORM.

Table of Contents

Append

The append function appends a value to the end of an array.

#![allow(unused)]
fn main() {
use surreal_orm::{functions::array, *};

let result = array::append!(vec![1, 2, 3, 4, 5], 6);
}

You can use the append function to add values to an existing array.

Combine

The combine function combines all values from two arrays together, returning an array of arrays.

#![allow(unused)]
fn main() {
use surreal_orm::{functions::array, *};

let result = array::combine!(vec![1, 2, 3], vec![4, 5, 6]);
}

The combine function provides the same functionality as the append function but can work with two arrays instead of appending a single value.

Concat

The concat function merges two arrays together, returning an array that may contain duplicate values.

#![allow(unused)]
fn main() {
use surreal_orm::{functions::array, *};

let result = array::concat!(vec![1, 2, 3], vec![4, 5, 6]);
}

The concat function provides the same functionality as the combine function but does not remove duplicate values from the resulting array.

Union

The union function combines two arrays together, removing duplicate values, and returning a single array.

#![allow(unused)]
fn main() {
use surreal_orm::{functions::array, *};

let result = array::union!(vec![1, 2, 3], vec![4, 5, 6]);
}

The union function provides the same functionality as the concat function but removes duplicate values from the resulting array.

Difference

The difference function determines the difference between two arrays, returning a single array containing items that are not in both arrays.

#![allow(unused)]
fn main() {
use surreal_orm::{functions::array, *};

let result = array::difference!(vec![1, 2, 3], vec![4, 5, 6]);
}

The difference function provides the same functionality

as the previous functions but returns only the unique values that are present in one array but not in the other.

Intersect

The intersect function calculates the values that intersect two arrays, returning a single array containing the values present in both arrays.

#![allow(unused)]
fn main() {
use surreal_orm::{functions::array, *};

let result = array::intersect!(vec![1, 2, 3], vec![4, 5, 6]);
}

The intersect function provides the same functionality as the previous functions but returns only the values that are common between the two arrays.

Complement

The complement function returns the complement of two arrays, returning a single array containing items that are not in the second array.

#![allow(unused)]
fn main() {
use surreal_orm::{functions::array, *};

let result = array::complement!(vec![1, 2, 3, 4], vec![3, 4, 5, 6]);
}

The complement function provides the same functionality as the previous functions but returns only the values that are present in the first array but not in the second array.

Distinct

The distinct function calculates the unique values in an array, returning a single array.

#![allow(unused)]
fn main() {
use surreal_orm::{functions::array, *};

let result = array::distinct!(vec![1, 2, 3]);
}

You can use the distinct function to obtain unique values from an array.

Flatten

The flatten function flattens an array of arrays, returning a new array with all sub-array elements concatenated into it.

#![allow(unused)]
fn main() {
use surreal_orm::{functions::array, *};

let result = array::flatten!(array![vec![1, 2], vec![3, 4], "SurrealDB", vec![5, 6]]);
}

The flatten function provides the same functionality as the previous functions but flattens an array of arrays.

Group

The group function flattens and returns the unique items in an array.

#![allow(unused)]
fn main() {
use surreal_orm::{functions::array, *};

let result = array::group!(array![1, 2, 3, 4, array![3, 5, 6], vec![2, 4, 5, 6], 7, 8, 8, 9]);
}

The group function provides the same functionality as the previous functions but returns only the unique items in the array.

Insert

The insert function inserts a value into an array at a specific position.

#![allow(unused)]
fn main() {
use surreal_orm::{functions::array, *};

let result = array::insert!(vec![1, 2, 3, 4], 5, 2);
}

The insert function allows you to insert a value into an array at a specified index.

Len

The len function calculates the length of an array, returning a number. This function includes all items when counting the number of items in the array.

#![allow(unused)]
fn main() {
use surreal_orm::{functions::array, *};

let result = array::len!(vec![1, 2, 3]);
}

You can use the len function to calculate the length of an array.

Pop

The pop function removes a value from the end of an array and returns it.

#![allow(unused)]
fn main() {
use surreal_orm::{functions::array, *};

let result = array::pop!(vec![1, 2, 3, 4]);
}

You can use the pop function to remove the last value from an array.

Prepend

The prepend function prepends a value to the end of an array.

#![allow(unused)]
fn main() {
use surreal_orm::{functions::array, *};

let result = array::prepend!(vec![1, 2, 3, 4], 5);
}

You can use the prepend function to add a value to the beginning of an array.

Push

The push function appends a value to the end of an array.

#![allow(unused)]
fn main() {
use surreal_orm::{functions::array, *};

let result = array::push!(vec![1, 2, 3, 4], 5);
}

The push function provides the same functionality as the prepend function but appends the value to the end of the array instead of the beginning.

Remove

The remove function removes an item from a specific position in an array.

#![allow(unused)]
fn main() {
use surreal_orm::{functions::array, *};

let result = array::remove!(vec![1, 2, 3, 4, 5], 2);
}

You can use the remove function to delete an item from a specific position in an array.

Reverse

The reverse function reverses the order of the elements in an array.

#![allow(unused)]
fn main() {
use surreal_orm::{functions::array, *};

let result = array::reverse!(vec![1, 2, 3, 4, 5]);
}

You can use the reverse function to reverse the order of elements in an array.

Sort

The sort function sorts an array in ascending or descending order.

#![allow(unused)]
fn main() {
use surreal_orm::{functions::array, *};

let result = array::sort!(vec![3, 1, 2], "asc");
}

You can use the sort function to sort an array in ascending or descending order.

The sort function also provides the following ordering options:

  • "asc": Sorts the array in ascending order.
  • "desc": Sorts the array in descending order.
  • false: Does not sort the array.

You can use the sort function with different ordering options to sort an array accordingly.

Asc and Desc

The asc and desc functions are shorthand convenience functions for the sort function. They sort values in an array in ascending or descending order, respectively.

#![allow(unused)]
fn main() {
use surreal_orm::{functions::array, *};

let result_asc = array::sort::asc!(vec![3, 1, 2]);
let result_desc = array::sort::desc!(vec![3, 1, 2]);
}

The asc and desc functions provide the same functionality as the sort function but with a more concise syntax.

These are the array functions available in Surreal ORM. Use them to perform various operations on arrays and manipulate array data effectively.

That concludes the documentation for the array functions in Surreal ORM. Refer to this documentation whenever you need to use array functions in your code.

Count Function

This chapter introduces the count macros provided by Surreal ORM. The count macros are used to generate SQL queries for counting records in a database table.

Table of Contents

count!()

The count!() macro counts all records in a table. It generates the SQL query count().

#![allow(unused)]
fn main() {
use surreal_orm::{count, *};

let result = count!();
}

Generated SQL query:

count()

The count!() macro provides the following functionality:

  • to_raw().build(): Converts the count macro into a raw SQL query string. In this case, it would be "count()".

count!().__as__(alias)

The count!().__as__(alias) macro allows you to specify an alias for the count result. It generates the SQL query count() AS alias.

#![allow(unused)]
fn main() {
use surreal_orm::{count, AliasName};

let head_count = AliasName::new("head_count");
let result = count!().__as__(head_count);
}

Generated SQL query:

count() AS head_count

The count!().__as__(alias) macro provides the same functionality as count!(), but with an additional AS clause to specify the alias for the count result.

count!(field)

The count!(field) macro counts records in a table based on a specific field. It generates the SQL query count(field).

#![allow(unused)]
fn main() {
use surreal_orm::{count, Field};

let email = Field::new("email");
let result = count!(email);
}

Generated SQL query:

count(email)

The count!(field) macro provides the same functionality as count!(), but with a specific field to count records on.

count!(field.operation(value))

The count!(field.operation(value)) macro allows you to perform filter operations on the count. It generates the SQL query count(field.operation(value)).

#![allow(unused)]
fn main() {
use surreal_orm::{count, Field};

let email = Field::new("email");
let result = count!(email.greater_than(15));
}

Generated SQL query:

count(email > 15)

The count!(field.operation(value)) macro provides the same functionality as count!(field), but with a filter operation applied to the field.

count!(condition1.and(condition2))

The count!(condition1.and(condition2)) macro allows you to apply multiple conditions to the count. It generates the SQL query count(condition1 AND condition2).

#![allow(unused)]
fn main() {
use surreal_orm::{count, Field, cond};

let email = Field::new("email");
let age = Field::new("age");
let result = count!(cond(age.greater_than(15)).and(email.like("oyelowo@example.com")));
}

Generated SQL query:

count((age > 15) AND (email ~ 'oyelowo@example.com'))

The `count!(condition1.and(condition2))

macro provides the same functionality ascount!(field.operation(value)), but with multiple conditions combined using theAND` operator.

count!(array)

The count!(array) macro counts the number of elements in an array. It generates the SQL query count(array).

#![allow(unused)]
fn main() {
use surreal_orm::{count, array};

let result = count!(array![1, 2, 3, 4, 5]);
}

Generated SQL query:

count([1, 2, 3, 4, 5])

The count!(array) macro provides the same functionality as count!(), but with an array as the input for counting.

Crypto Functions

This chapter introduces the crypto macros provided by Surreal ORM. The crypto macros are used for cryptographic operations such as password hashing and comparison.

Table of Contents

argon2::compare!()

The argon2::compare!() macro compares two values using the Argon2 hashing algorithm. It has the following syntax:

#![allow(unused)]
fn main() {
let result = argon2::compare!("Oyelowo", "Oyedayo");
}

The argon2::compare!() macro generates the following SQL query:

crypto::argon2::compare('Oyelowo', 'Oyedayo')

argon2::generate!()

The argon2::generate!() macro generates a hash value using the Argon2 hashing algorithm. It has the following syntax:

#![allow(unused)]
fn main() {
let result = argon2::generate!("Oyelowo");
}

The argon2::generate!() macro generates the following SQL query:

crypto::argon2::generate('Oyelowo')

pbkdf2::compare!()

The pbkdf2::compare!() macro compares two values using the PBKDF2 hashing algorithm. It has the following syntax:

#![allow(unused)]
fn main() {
let result = pbkdf2::compare!("hash_value", "password");
}

The pbkdf2::compare!() macro generates the following SQL query:

crypto::pbkdf2::compare('hash_value', 'password')

pbkdf2::generate!()

The pbkdf2::generate!() macro generates a hash value using the PBKDF2 hashing algorithm. It has the following syntax:

#![allow(unused)]
fn main() {
let result = pbkdf2::generate!("password");
}

The pbkdf2::generate!() macro generates the following SQL query:

crypto::pbkdf2::generate('password')

scrypt::compare!()

The scrypt::compare!() macro compares two values using the scrypt hashing algorithm. It has the following syntax:

#![allow(unused)]
fn main() {
let result = scrypt::compare!("hash_value", "password");
}

The scrypt::compare!() macro generates the following SQL query:

crypto::scrypt::compare('hash_value', 'password')

scrypt::generate!()

The scrypt::generate!() macro generates a hash value using the scrypt hashing algorithm. It has the

following syntax:

#![allow(unused)]
fn main() {
let result = scrypt::generate!("password");
}

The scrypt::generate!() macro generates the following SQL query:

crypto::scrypt::generate('password')

bcrypt::compare!()

The bcrypt::compare!() macro compares two values using the bcrypt hashing algorithm. It has the following syntax:

#![allow(unused)]
fn main() {
let result = bcrypt::compare!("hash_value", "password");
}

The bcrypt::compare!() macro generates the following SQL query:

crypto::bcrypt::compare('hash_value', 'password')

bcrypt::generate!()

The bcrypt::generate!() macro generates a hash value using the bcrypt hashing algorithm. It has the following syntax:

#![allow(unused)]
fn main() {
let result = bcrypt::generate!("password");
}

The bcrypt::generate!() macro generates the following SQL query:

Geo Functions

This chapter introduces the geo macros provided by the Surreal ORM. The geo macros are used for geospatial operations such as calculating area, distance, bearing, centroid, and encoding/decoding hashes.

Table of Contents

geo::area!()

The geo::area!() macro calculates the area of a polygon. It has the following syntax:

#![allow(unused)]
fn main() {
let poly = polygon!(
    exterior: [
        (x: -111., y: 45.),
        (x: -111., y: 41.),
        (x: -104., y: 41.),
        (x: -104., y: 45.),
    ],
    interiors: [
        [
            (x: -110., y: 44.),
            (x: -110., y: 42.),
            (x: -105., y: 42.),
            (x: -105., y: 44.),
        ],
    ],
);
let result = geo::area!(poly);
}

The geo::area!() macro generates the following SQL query:

geo::area({ type: 'Polygon', coordinates: [[[-111, 45], [-111, 41], [-104, 41], [-104, 45], [-111, 45]], [[[-110, 44], [-110, 42], [-105, 42], [-105, 44], [-110, 44]]]] })

geo::bearing!()

The geo::bearing!() macro calculates the bearing between two points. It has the following syntax:

#![allow(unused)]
fn main() {
let point1 = point! {
    x: 40.02f64,
    y: 116.34,
};

let point2 = point! {
    x: 80.02f64,
    y: 103.19,
};
let result = geo::bearing!(point1, point2);
}

The geo::bearing!() macro generates the following SQL query:

geo::bearing((40.02, 116.34), (80.02, 103.19))

geo::centroid!()

The geo::centroid!() macro calculates the centroid of a polygon. It has the following syntax:

#![allow(unused)]
fn main() {
let poly = polygon!(
    exterior: [
        (x: -111., y: 45.),
        (x: -111., y: 41.),
        (x: -104., y: 41.),
        (x: -104., y: 45.),
    ],
    interiors: [
        [
            (x: -110., y: 44.),
            (x: -110., y: 42.),
            (x: -105., y: 42.),
            (x: -105., y: 44.),
        ],
    ],
);
let result = geo::centroid!(poly);
}

The geo::centroid!() macro generates the following SQL query:

geo::centroid({ type: 'Polygon', coordinates: [[[-111, 45], [-111, 41], [-104, 41], [-104, 45], [-111, 45]], [[[-110, 44], [-110, 42], [-105, 42], [-105, 44], [-110, 44]]]] })

geo::distance!()

The geo::distance!() macro calculates the distance between two points. It has the following syntax:

#![allow(unused)]
fn main() {
let point1 = point! {
    x: 40.02f64,
    y: 116.34,
};

let point2 = point! {
    x: 80.02f64,
    y: 103.19,
};
let result = geo::distance!(point1, point2);
}

The geo::distance!() macro generates the following SQL query:

geo::distance((40.02, 116.34), (80.02, 103.19))

geo::hash::decode!()

The geo::hash::decode!() macro decodes a geohash string. It has the following syntax:

#![allow(unused)]
fn main() {
let result = geo::hash::decode!("mpuxk4s24f51");
}

The geo::hash::decode!() macro generates the following SQL query:

geo::hash::decode('mpuxk4s24f51')

geo::hash::encode!()

The geo::hash::encode!() macro encodes a point or polygon into a geohash string. It has the following syntax:

#![allow(unused)]
fn main() {
let point = point! {
    x: 40.02f64,
    y: 116.34,
};

let result = geo::hash::encode!(point, 5);
}

The geo::hash::encode!() macro generates the following SQL query:

geo::hash::encode((40.02, 116.34), 5)

HTTP Functions

This chapter introduces the http macros provided by the Surreal ORM. The http macros are used for performing remote HTTP requests such as HEAD, GET, POST, PUT, and PATCH.

Table of Contents

http::head!()

The http::head!() macro performs a remote HTTP HEAD request. It has the following syntax:

#![allow(unused)]
fn main() {
http::head!("https://codebreather.com");
}

The http::head!() macro generates the following function call:

http::head("https://codebreather.com", None as Option<ObjectLike>)

http::get!()

The http::get!() macro performs a remote HTTP GET request. It has the following syntax:

#![allow(unused)]
fn main() {
http::get!("https://codebreather.com");
}

The http::get!() macro generates the following function call:

http::get("https://codebreather.com", None as Option<ObjectLike>)

http::delete!()

The http::delete!() macro performs a remote HTTP DELETE request. It has the following syntax:

#![allow(unused)]
fn main() {
http::delete!("https://codebreather.com");
}

The http::delete!() macro generates the following function call:

http::delete("https://codebreather.com", None as Option<ObjectLike>)

http::post!()

The http::post!() macro performs a remote HTTP POST request. It has the following syntax:

#![allow(unused)]
fn main() {
http::post!("https://codebreather.com", body);
}

The http::post!() macro generates the following function call:

http::post("https://codebreather.com", body, None as Option<ObjectLike>)

http::put!()

The http::put!() macro performs a remote HTTP PUT request. It has the following syntax:

#![allow(unused)]
fn main() {
http::put!("https://codebreather.com", body);
}

The http::put!() macro generates the following function call:

http::put("https://codebreather.com", body, None as Option<ObjectLike>)

http::patch!()

The http::patch!() macro performs a remote HTTP PATCH request. It has the following syntax:

#![allow(unused)]
fn main() {
http::patch!("https://codebreather.com", body);
}

The http::patch!() macro generates the following function call:

http::patch("https://codebreather.com", body, None as Option<ObjectLike>)

Validation Functions

This documentation provides an overview of the validation functions in the codebase. The is macros are used to create validation functions for checking various conditions on values.

Table of Contents

Alphanum

The is::alphanum function checks whether a value has only alphanumeric characters. It is also aliased as is_alphanum!.

Arguments

  • value - The value to check. It could be a field or a parameter that represents the value.

Example

#![allow(unused)]
fn main() {
use surreal_orm::{*, functions::is, statements::let_};

let result = is::alphanum!("oyelowo1234");
assert_eq!(result.to_raw().build(), "is::alphanum('oyelowo1234')");

let alphanum_field = Field::new("alphanum_field");
let result = is::alphanum!(alphanum_field);
assert_eq!(result.to_raw().build(), "is::alphanum(alphanum_field)");

block!{
    LET alphanum_param = "oyelowo1234";
    LET result = is::alphanum!(alphanum_param);
};
}

Alpha

The is::alpha function checks whether a value has only alpha characters. It is also aliased as is_alpha!.

Arguments

  • value - The value to check. It could be a field or a parameter that represents the value.

Example

#![allow(unused)]
fn main() {
use surreal_orm::{*, functions::is, statements::let_};

let result = is::alpha!("oyelowo");
assert_eq!(result.to_raw().build(), "is::alpha('oyelowo')");

let alpha_field = Field::new("alpha_field");
let result = is::alpha!(alpha_field);
assert_eq!(result.to_raw().build(), "is::alpha(alpha_field)");
}

ASCII

The is::ascii function checks whether a value has only ASCII characters. It is also aliased as is_ascii!.

Arguments

  • value - The value to check. It could be a field or a parameter that represents the value.

Example

#![allow(unused)]
fn main() {
use surreal_orm::{*, functions::is, statements::let_};

let result = is::ascii!("oyelowo");
assert_eq!(result.to_raw().build(), "is::ascii('oyelowo')");

let ascii_field = Field::new("ascii_field");
let result = is::ascii!(ascii_field);
assert_eq!(result.to_raw().build(), "is::ascii(ascii_field)");
}

Domain

The is::domain function checks whether a value is a domain. It is also aliased as is_domain!.

Arguments

  • value - The value to check. It could be a field or a parameter that represents the value.

Example

#![allow(unused)]
fn main() {
use surreal_orm::{*, functions::is, statements::let_};

let result = is::domain!("oyelowo.com");
assert_eq!(result.to_raw().build(), "is::domain('oyelowo.com')");

let domain_field = Field::new("domain_field");
let result = is::domain!(domain_field);
assert_eq!(result.to_raw().build(), "is::domain(domain_field)");
}

Email

The is::email function checks whether a value is an email. It is also aliased as is_email!.

Arguments

  • value - The value to check. It could be a field or a parameter that represents the value.

Example

#![allow(unused)]
fn main() {
use surreal_orm::{*, functions::is, statements::let_};

let result = is::email!("oyelowo@codebreather.com");
assert_eq!(result.to_raw().to_string(), "is::email('oyelowo@codebreather.com')");

let email_field = Field::new("email_field");
let result = is::email!(email_field);
assert_eq!(result.to_raw().to_string(), "is::email(email_field)");
}

Hexadecimal

The is::hexadecimal function checks whether a value is hexadecimal. It is also aliased as is_hexadecimal!.

Arguments

  • value - The value to check. It could be a field or a parameter that represents the value.

Example

#![allow(unused)]
fn main() {
use surreal_orm::{*, functions::is, statements::let_};

let result = is::hexadecimal!("oyelowo");
assert_eq!(result.to_raw().to_string(), "is::hexadecimal('oyelowo')");

let hexadecimal_field = Field::new("hexadecimal_field");
let result = is::hexadecimal!(hexadecimal_field);
assert_eq!(result.to_raw().to_string(), "is::hexadecimal(hexadecimal_field)");

let!(hexadecimal_param = "oyelowo");
let result = is::hexadecimal!(hexadecimal_param);
assert_eq!(result.fine_tune_params(), "is::hexadecimal($hexadecimal_param)");
}

Latitude

The is::latitude function checks whether a value is a latitude value. It is also aliased as is_latitude!.

Arguments

  • value - The value to check. It could be a field or a parameter that represents the value.

Example

#![allow(unused)]
fn main() {
use surreal_orm::{*, functions::is, statements::let_};

let result = is::latitude!("-0.118092");
assert_eq!(result.to_raw().build(), "is::latitude('-0.118092')");

let latitude_field = Field::new("latitude_field");
let result = is::latitude!(latitude_field);
assert_eq!(

result.to_raw().build(), "is::latitude(latitude_field)");
}

Longitude

The is::longitude function checks whether a value is a longitude value. It is also aliased as is_longitude!.

Arguments

  • value - The value to check. It could be a field or a parameter that represents the value.

Example

#![allow(unused)]
fn main() {
use surreal_orm::{*, functions::is, statements::let_};

let result = is::longitude!("51.509865");
assert_eq!(result.to_raw().build(), "is::longitude('51.509865')");

let longitude_field = Field::new("longitude_field");
let result = is::longitude!(longitude_field);
assert_eq!(result.to_raw().build(), "is::longitude(longitude_field)");
}

Numeric

The is::numeric function checks whether a value has only numeric characters. It is also aliased as is_numeric!.

Arguments

  • value - The value to check. It could be a field or a parameter that represents the value.

Example

#![allow(unused)]
fn main() {
use surreal_orm::{*, functions::is, statements::let_};

let result = is::numeric!("oyelowo");
assert_eq!(result.to_raw().build(), "is::numeric('oyelowo')");

let numeric_field = Field::new("numeric_field");
let result = is::numeric!(numeric_field);
assert_eq!(result.to_raw().build(), "is::numeric(numeric_field)");
}

Semver

The is::semver function checks whether a value matches a semver version. It is also aliased as is_semver!.

Arguments

  • value - The value to check. It could be a field or a parameter that represents the value.

Example

#![allow(unused)]
fn main() {
use surreal_orm::{*, functions::is, statements::let_};

let result = is::semver!("oyelowo");
assert_eq!(result.to_raw().build(), "is::semver('oyelowo')");

let semver_field = Field::new("semver_field");
let result = is::semver!(semver_field);
assert_eq!(result.to_raw().build(), "is::semver(semver_field)");
}

UUID

The is::uuid function checks whether a value is a UUID. It is also aliased as is_uuid!.

Arguments

  • value - The value to check. It could be a field or a parameter that represents the value.

Example

#![allow(unused)]
fn main() {
use surreal_orm::{*, functions::is, statements::let_};

let result = is::uuid!("oyelowo");
assert_eq!(result.to_raw().build(), "

is::uuid('oyelowo')");

let uuid_field = Field::new("uuid_field");
let result = is::uuid!(uuid_field);
assert_eq!(result.to_raw().build(), "is::uuid(uuid_field)");
}

Datetime

The is::datetime function checks whether a value matches a datetime format. It is also aliased as is_datetime!.

Arguments

  • value - The value to check. It could be a field or a parameter that represents the value.

Example

#![allow(unused)]
fn main() {
use surreal_orm::{*, functions::is, statements::let_};

let result = is::datetime!("oyelowo");
assert_eq!(result.to_raw().build(), "is::datetime('oyelowo')");

let datetime_field = Field::new("datetime_field");
let result = is::datetime!(datetime_field);
assert_eq!(result.to_raw().build(), "is::datetime(datetime_field)");
}

Math Functions

Table of Contents


math::abs!()

The math::abs function returns the absolute value of a number.

Function signature: math::abs(number) -> number

Example:

#![allow(unused)]
fn main() {
math::abs!(45.23);
}

math::ceil!()

The math::ceil function rounds a number up to the next largest integer.

Function signature: math::ceil(number) -> number

Example:

#![allow(unused)]
fn main() {
math::ceil!(45.23);
}

math::floor!()

The math::floor function rounds a number down to the next largest integer.

Function signature: math::floor(number) -> number

Example:

#![allow(unused)]
fn main() {
math::floor!(45.23);
}

math::round!()

The math::round function rounds a number up or down to the nearest integer.

Function signature: math::round(number) -> number

Example:

#![allow(unused)]
fn main() {
math::round!(45.23);
}

math::sqrt!()

The math::sqrt function returns the square root of a number.

Function signature: math::sqrt(number) -> number

Example:

#![allow(unused)]
fn main() {
math::sqrt!(45.23);
}

math::mean!()

The math::mean function returns the average of a set of numbers.

Function signature: math::mean(array) -> number

Example:

#![allow(unused)]
fn main() {
math::mean!(vec![1, 2, 3, 4, 5]);
}

math::median!()

The math::median function returns the median of a set of numbers.

Function signature: math::median(array) -> number

Example:

#![allow(unused)]
fn main() {
math::median!(vec![1, 2, 3, 4, 5]);
}

math::mode!()

The math::mode function returns the mode of a set of numbers.

Function signature: math::mode(array) -> number

Example:

#![allow(unused)]
fn main() {
math::mode!(vec![1, 2, 3, 4, 5]);
}

math::min!()

The math::min function returns the minimum number in a set of numbers.

Function signature: math::min(array) -> number

Example:

#![allow(unused)]
fn main() {
math::min!(vec![1, 2, 3, 4, 5]);
}

math::product!()

The math::product function returns the product of a set of numbers.

Function signature: math::product(array) -> number

Example:

#![allow(unused)]
fn main() {
math::product!(vec![1, 2, 3, 4, 5]);
}

math::sum!()

The math::sum function returns the total sum of a set of numbers.

Function signature: math::sum(array) -> number

Example:

#![allow(unused)]
fn main() {
math::sum!(vec![1, 2, 3, 4, 5]);
}

That concludes the documentation for the math macros.

Meta functions

Parse Functions

Table of Contents


parse::email::host()

The parse::email::host function parses and returns the email host from a valid email address. This function is also aliased as parse_email_host!.

Function signature: parse::email::host(string) -> value

Example:

#![allow(unused)]
fn main() {
parse::email::host!("oyelowo@codebreather.com");
}

parse::email::user()

The parse::email::user function parses and returns the email username from a valid email address. This function is also aliased as parse_email_user!.

Function signature: parse::email::user(string) -> value

Example:

#![allow(unused)]
fn main() {
parse::email::user!("oyelowo@codebreather.com");
}

parse::url::domain()

The parse::url::domain function parses and returns the domain from a valid URL. This function is also aliased as parse_url_domain!.

Function signature: parse::url::domain(string) -> value

Example:

#![allow(unused)]
fn main() {
parse::url::domain!("https://codebreather.com:443/topics?arg=value#fragment");
}

parse::url::fragment()

The parse::url::fragment function parses and returns the fragment from a valid URL. This function is also aliased as parse_url_fragment!.

Function signature: parse::url::fragment(string) -> value

Example:

#![allow(unused)]
fn main() {
parse::url::fragment!("https://codebreather.com:443/topics?arg=value#fragment");
}

parse::url::host()

The parse::url::host function parses and returns the hostname from a valid URL. This function is also aliased as parse_url_host!.

Function signature: parse::url::host(string) -> value

Example:

#![allow(unused)]
fn main() {
parse::url::host!("https://codebreather.com:443/topics?arg=value#fragment");
}

parse::url::path()

The parse::url::path function parses and returns the path from a valid URL. This function is also aliased as parse_url_path!.

Function signature: parse::url::path(string) -> value

Example:

#![allow(unused)]
fn main() {
parse::url::path!("https://codebreather.com:443/topics?arg=value#fragment");
}

parse::url::port()

The parse::url::port function parses and returns the port from a valid URL. This function is also aliased as parse_url_port!.

Function signature: `parse::url

::port(string) -> value`

Example:

#![allow(unused)]
fn main() {
parse::url::port!("https://codebreather.com:443/topics?arg=value#fragment");
}

parse::url::query()

The parse::url::query function parses and returns the query from a valid URL. This function is also aliased as parse_url_query!.

Function signature: parse::url::query(string) -> value

Example:

#![allow(unused)]
fn main() {
parse::url::query!("https://codebreather.com:443/topics?arg=value#fragment");
}

That concludes the documentation for the parse macros.

Rand Functions

Table of Contents

rand!()

The rand!() macro generates a random number.

Example:

#![allow(unused)]
fn main() {
use surreal_orm::functions::rand;

let result = rand!();
}

rand::bool!()

The rand::bool!() macro generates a random boolean value.

Example:

#![allow(unused)]
fn main() {
use surreal_orm::functions::rand;

let result = rand::bool!();
}

rand::uuid!()

The rand::uuid!() macro generates a random UUID.

Example:

#![allow(unused)]
fn main() {
use surreal_orm::functions::rand;

let result = rand::uuid!();
}

rand::uuid::v4!()

The rand::uuid::v4!() macro generates a random UUID v4.

Example:

#![allow(unused)]
fn main() {
use surreal_orm::functions::rand;

let result = rand::uuid::v4!();
}

rand::uuid::v7!()

The rand::uuid::v7!() macro generates a random UUID v7.

Example:

#![allow(unused)]
fn main() {
use surreal_orm::functions::rand;

let result = rand::uuid::v7!();
}

rand::enum!()

The rand::enum!() macro generates a random value from a list of options.

Example:

#![allow(unused)]
fn main() {
use surreal_orm::functions::rand;
use surreal_orm::functions::rand::arr;

let result = rand::enum!(arr!["one", "two", 3, 4.15385, "five", true]);
}

rand::string!()

The rand::string!() macro generates a random string.

Example:

#![allow(unused)]
fn main() {
use surreal_orm::functions::rand;

let result = rand::string!();
}

rand::guid!()

The rand::guid!() macro generates a random GUID.

Example:

#![allow(unused)]
fn main() {
use surreal_orm::functions::rand;

let result = rand::guid!();
}

rand::float!()

The rand::float!() macro generates a random floating-point number.

Example:

#![allow(unused)]
fn main() {
use surreal_orm::functions::rand;

let result = rand::float!();
}

rand::int!()

The rand::int!() macro generates a random integer.

Example:

#![allow(unused)]
fn main() {
use surreal_orm::functions::rand;

let result = rand::int!();
}

rand::time!()

The rand::time!() macro generates a random time value.

Example:

#![allow(unused)]
fn main() {
use surreal_orm::functions::rand;

let result = rand::time!();
}

Session functions

Table of Contents

session::db!()

The session::db!() macro returns the currently selected database.

Example:

#![allow(unused)]
fn main() {
use surreal_orm::functions::session;

session::db!();
}

session::id!()

The session::id!() macro returns the current user's session ID.

Example:

#![allow(unused)]
fn main() {
use surreal_orm::functions::session;

session::id!();
}

session::ip!()

The session::ip!() macro returns the current user's session IP address.

Example:

#![allow(unused)]
fn main() {
use surreal_orm::functions::session;

session::ip!();
}

session::ns!()

The session::ns!() macro returns the currently selected namespace.

Example:

#![allow(unused)]
fn main() {
use surreal_orm::functions::session;

session::ns!();
}

session::origin!()

The session::origin!() macro returns the current user's HTTP origin.

Example:

#![allow(unused)]
fn main() {
use surreal_orm::functions::session;

session::origin!();
}

session::sc!()

The session::sc!() macro returns the current user's authentication scope.

Example:

#![allow(unused)]
fn main() {
use surreal_orm::functions::session;

session::sc!();
}

Sleep function

Table of Contents

sleep!()

The sleep!() macro suspends the current thread for the specified duration.

Example:

#![allow(unused)]
fn main() {
use std::time;
use surreal_orm::functions::sleep;

let result = sleep!(time::Duration::from_secs(55));
assert_eq!(result.to_raw().build(), "sleep(55s)");
}

String functions

Table of Contents

string::concat!()

The string::concat!() macro allows you to concatenate multiple values into a string.

Examples:

#![allow(unused)]
fn main() {
use crate::functions::string;
use crate::*;

let title = Field::new("title");
let result = string::concat!(title, "one", 3, 4.15385, "  ", true);
assert_eq!(result.fine_tune_params(), "string::concat(title, $_param_00000001, $_param_00000002, $_param_00000003, $_param_00000004, $_param_00000005)");
assert_eq!(
    result.to_raw().build(),
    "string::concat(title, 'one', 3, 4.15385, '  ', true)"
);

let result = string::concat!(arr!["one", "two", 3, 4.15385, "five", true]);
assert_eq!(result.fine_tune_params(), "string::concat($_param_00000001, $_param_00000002, $_param_00000003, $_param_00000004, $_param_00000005, $_param_00000006)");
assert_eq!(
    result.to_raw().build(),
    "string::concat('one', 'two', 3, 4.15385, 'five', true)"
);
}

string::join!()

The string::join!() macro allows you to join multiple values into a string using a delimiter.

Examples:

#![allow(unused)]
fn main() {
use crate::functions::string;
use crate::*;

let title = Field::new("title");
let result = string::join!(title, "one", 3, 4.15385, "  ", true);
assert_eq!(result.fine_tune_params(), "string::join(title, $_param_00000001, $_param_00000002, $_param_00000003, $_param_00000004, $_param_00000005)");
assert_eq!(
    result.to_raw().build(),
    "string::join(title, 'one', 3, 4.15385, '  ', true)"
);

let result = string::join!(arr!["one", "two", 3, 4.15385, "five", true]);
assert_eq!(result.fine_tune_params(), "string::join($_param_00000001, $_param_00000002, $_param_00000003, $_param_00000004

, $_param_00000005, $_param_00000006)");
assert_eq!(
    result.to_raw().build(),
    "string::join('one', 'two', 3, 4.15385, 'five', true)"
);
}

string::ends_with!()

The string::ends_with!() macro allows you to check if a string ends with a specified substring.

Examples:

#![allow(unused)]
fn main() {
use crate::functions::string;
use crate::*;

let name = Field::new("name");
let result = string::ends_with!(name, "lowo");
assert_eq!(
    result.fine_tune_params(),
    "string::ends_with(name, $_param_00000001)"
);
assert_eq!(result.to_raw().build(), "string::ends_with(name, 'lowo')");

let result = string::ends_with!("Oyelowo", "lowo");
assert_eq!(
    result.fine_tune_params(),
    "string::ends_with($_param_00000001, $_param_00000002)"
);
assert_eq!(
    result.to_raw().build(),
    "string::ends_with('Oyelowo', 'lowo')"
);
}

string::starts_with!()

The string::starts_with!() macro allows you to check if a string starts with a specified substring.

Examples:

#![allow(unused)]
fn main() {
use crate::functions::string;
use crate::*;

let name = Field::new("name");
let result = string::starts_with!(name, "lowo");
assert_eq!(
    result.fine_tune_params(),
    "string::starts_with(name, $_param_00000001)"
);
assert_eq!(result.to_raw().build(), "string::starts_with(name, 'lowo')");

let result = string::starts_with!("Oyelowo", "Oye");
assert_eq!(
    result.fine_tune_params(),
    "string::starts_with($_param_00000001, $_param_00000002)"
);
assert_eq!(
    result.to_raw().build(),
    "string::starts_with('Oyelowo', 'Oye')"
);
}

string::split!()

The string::split!() macro allows you to split a string into multiple substrings based on a delimiter.

Examples:

#![allow(unused)]
fn main() {
use crate::functions::string;
use crate::*;

let phrase = Field::new("phrase");
let result = string::split!(phrase, ", ");
assert_eq!(
    result.fine_tune_params(),
    "string::split(phrase, $_param_00000001)"
);
assert_eq!(result.to_raw().build(), "string::split(phrase, ', ')");

let result = string::split!(
    "With great power, comes great responsibility",
    ", "
);
assert_eq!(
    result.fine_tune_params(),
    "string::split($_param_00000001, $_param_00000002)"
);
assert_eq!(
    result.to_raw().build(),
    "string::split('With great power, comes great responsibility', ', ')"
);
}

string::len!()

The string::len!() macro allows you to get the length of a string.

Examples:

#![allow(unused)]
fn main() {
use crate::functions::string;
use crate::*;

let name = Field::new("name");
let result = string::len!(name);
assert_eq!(result.fine_tune_params(), "string::length(name)");
assert_eq!(result.to_raw().build(), "

string::length(name)");

let result = string::len!("toronto");
assert_eq!(
    result.fine_tune_params(),
    "string::length($_param_00000001)"
);
assert_eq!(result.to_raw().build(), "string::length('toronto')");
}

string::reverse!()

The string::reverse!() macro allows you to reverse a string.

Examples:

#![allow(unused)]
fn main() {
use crate::functions::string;
use crate::*;

let name = Field::new("name");
let result = string::reverse!(name);
assert_eq!(result.fine_tune_params(), "string::reverse(name)");
assert_eq!(result.to_raw().build(), "string::reverse(name)");

let result = string::reverse!("oyelowo");
assert_eq!(
    result.fine_tune_params(),
    "string::reverse($_param_00000001)"
);
assert_eq!(result.to_raw().build(), "string::reverse('oyelowo')");
}

string::trim!()

The string::trim!() macro allows you to remove leading and trailing whitespace from a string.

Examples:

#![allow(unused)]
fn main() {
use crate::functions::string;
use crate::*;

let name = Field::new("name");
let result = string::trim!(name);
assert_eq!(result.fine_tune_params(), "string::trim(name)");
assert_eq!(result.to_raw().build(), "string::trim(name)");

let result = string::trim!("oyelowo");
assert_eq!(result.fine_tune_params(), "string::trim($_param_00000001)");
assert_eq!(result.to_raw().build(), "string::trim('oyelowo')");
}

string::slug!()

The string::slug!() macro allows you to convert a string into a slug.

Examples:

#![allow(unused)]
fn main() {
use crate::functions::string;
use crate::*;

let name = Field::new("name");
let result = string::slug!(name);
assert_eq!(result.fine_tune_params(), "string::slug(name)");
assert_eq!(result.to_raw().build(), "string::slug(name)");

let result = string::slug!("Codebreather is from #Jupiter");
assert_eq!(result.fine_tune_params(), "string::slug($_param_00000001)");
assert_eq!(
    result.to_raw().build(),
    "string::slug('Codebreather is from #Jupiter')"
);
}

string::lowercase!()

The string::lowercase!() macro allows you to convert a string to lowercase.

Examples:

#![allow(unused)]
fn main() {
use crate::functions::string;
use crate::*;

let name = Field::new("name");
let result = string::lowercase!(name);
assert_eq!(result.fine_tune_params(), "string::lowercase(name)");
assert_eq!(result.to_raw().build(), "string::lowercase(name)");

let result = string::lowercase!("OYELOWO");
assert_eq!(
    result.fine_tune_params(),
    "string::lowercase($_param_00000001)"
);
assert_eq!(result.to_raw().build(), "string::lowercase('OYELOWO')");
}

string::uppercase!()

The string::uppercase!() macro allows you to convert a string to uppercase.

Examples:

#![allow(unused)]
fn main() {
use crate::functions::string;
use crate::*;

let name = Field::new("name");
let result = string::uppercase!(name);
assert

_eq!(result.fine_tune_params(), "string::uppercase(name)");
assert_eq!(result.to_raw().build(), "string::uppercase(name)");

let result = string::uppercase!("oyelowo");
assert_eq!(
    result.fine_tune_params(),
    "string::uppercase($_param_00000001)"
);
assert_eq!(result.to_raw().build(), "string::uppercase('oyelowo')");
}

string::words!()

The string::words!() macro allows you to split a string into individual words.

Examples:

#![allow(unused)]
fn main() {
use crate::functions::string;
use crate::*;

let sentence = Field::new("sentence");
let result = string::words!(sentence);
assert_eq!(result.fine_tune_params(), "string::words(sentence)");
assert_eq!(result.to_raw().build(), "string::words(sentence)");

let result = string::words!("The quick brown fox");
assert_eq!(
    result.fine_tune_params(),
    "string::words($_param_00000001)"
);
assert_eq!(result.to_raw().build(), "string::words('The quick brown fox')");
}

string::repeat!()

The string::repeat!() macro allows you to repeat a string multiple times.

Examples:

#![allow(unused)]
fn main() {
use crate::functions::string;
use crate::*;

let word = Field::new("word");
let result = string::repeat!(word, 3);
assert_eq!(result.fine_tune_params(), "string::repeat(word, $_param_00000001)");
assert_eq!(result.to_raw().build(), "string::repeat(word, 3)");

let result = string::repeat!("hello", 5);
assert_eq!(result.fine_tune_params(), "string::repeat($_param_00000001, $_param_00000002)");
assert_eq!(result.to_raw().build(), "string::repeat('hello', 5)");
}

string::replace!()

The string::replace!() macro allows you to replace occurrences of a substring in a string with another substring.

Examples:

#![allow(unused)]
fn main() {
use crate::functions::string;
use crate::*;

let phrase = Field::new("phrase");
let result = string::replace!(phrase, "world", "Universe");
assert_eq!(
    result.fine_tune_params(),
    "string::replace(phrase, $_param_00000001, $_param_00000002)"
);
assert_eq!(
    result.to_raw().build(),
    "string::replace(phrase, 'world', 'Universe')"
);

let result = string::replace!("Hello, world!", "world", "Universe");
assert_eq!(
    result.fine_tune_params(),
    "string::replace($_param_00000001, $_param_00000002, $_param_00000003)"
);
assert_eq!(
    result.to_raw().build(),
    "string::replace('Hello, world!', 'world', 'Universe')"
);
}

string::slice!()

The string::slice!() macro allows you to extract a portion of a string.

Examples:

#![allow(unused)]
fn main() {
use crate::functions::string;
use crate::*;

let phrase = Field::new("phrase");
let result = string::slice!(phrase, 6, 11);
assert_eq!(
    result.fine_tune_params(),
    "string::slice(phrase, $_param_00000001, $_param_00000002)"
);
assert_eq!(result.to_raw().build(), "string::slice

(phrase, 6, 11)");

let result = string::slice!("Hello, world!", 7, 12);
assert_eq!(
    result.fine_tune_params(),
    "string::slice($_param_00000001, $_param_00000002, $_param_00000003)"
);
assert_eq!(
    result.to_raw().build(),
    "string::slice('Hello, world!', 7, 12)"
);
}

string::concat!()

The string::concat!() macro allows you to concatenate multiple strings.

Examples:

#![allow(unused)]
fn main() {
use crate::functions::string;
use crate::*;

let word1 = Field::new("word1");
let word2 = Field::new("word2");
let result = string::concat!(word1, " ", word2);
assert_eq!(
    result.fine_tune_params(),
    "string::concat(word1, $_param_00000001, word2)"
);
assert_eq!(result.to_raw().build(), "string::concat(word1, ' ', word2)");

let result = string::concat!("Hello", ", ", "world!");
assert_eq!(
    result.fine_tune_params(),
    "string::concat($_param_00000001, $_param_00000002, $_param_00000003)"
);
assert_eq!(
    result.to_raw().build(),
    "string::concat('Hello', ', ', 'world!')"
);
}

string::to_string!()

The string::to_string!() macro allows you to convert a value to a string.

Examples:

#![allow(unused)]
fn main() {
use crate::functions::string;
use crate::*;

let number = Field::new("number");
let result = string::to_string!(number);
assert_eq!(
    result.fine_tune_params(),
    "string::to_string(number)"
);
assert_eq!(result.to_raw().build(), "string::to_string(number)");

let result = string::to_string!(42);
assert_eq!(
    result.fine_tune_params(),
    "string::to_string($_param_00000001)"
);
assert_eq!(result.to_raw().build(), "string::to_string(42)");
}

Time Functions

time::day()

The time::day() function extracts the day as a number from a datetime.

Usage:

#![allow(unused)]
fn main() {
use surreal_orm::{*, functions::time};

let dt = chrono::DateTime::<chrono::Utc>::from_naive_utc_and_offset(
    chrono::NaiveDateTime::from_timestamp_opt(61, 0).unwrap(),
    chrono::Utc,
);

let result = time::day(dt);
assert_eq!(
    result.to_raw().build(),
    "time::day('1970-01-01T00:01:01Z')"
);

let rebirth_date = Field::new("rebirth_date");
let result = time::day(rebirth_date);
assert_eq!(result.to_raw().build(), "time::day(rebirth_date)");

let param = Param::new("rebirth_date");
let result = time::day(param);
assert_eq!(result.to_raw().build(), "time::day($rebirth_date)");
}

Example:

#![allow(unused)]
fn main() {
use surreal_orm::{*, functions::time};

let dt = chrono::DateTime::<chrono::Utc>::from_naive_utc_and_offset(
    chrono::NaiveDateTime::from_timestamp_opt(61, 0).unwrap(),
    chrono::Utc,
);

let result = time::day(dt);
assert_eq!(
    result.to_raw().build(),
    "time::day('1970-01-01T00:01:01Z')"
);

let rebirth_date = Field::new("rebirth_date");
let result = time::day(rebirth_date);
assert_eq!(result.to_raw().build(), "time::day(rebirth_date)");

let param = Param::new("rebirth_date");
let result = time::day(param);
assert_eq!(result.to_raw().build(), "time::day($rebirth_date)");
}

time::floor()

The time::floor() function rounds a datetime down by a specific duration.

Usage:

#![allow(unused)]
fn main() {
use surreal_orm::{*, functions::time};

let rebirth_date = Field::new("rebirth_date");
let duration = Field::new("duration");
let result = time::floor(rebirth_date, duration);

assert_eq!(
    result.to_raw().build(),
    "time::floor(rebirth_date, duration)"
);
}

Example:

#![allow(unused)]
fn main() {
use surreal_orm::{*, functions::time};

let rebirth_date = Field::new("rebirth_date");
let duration = Field::new("duration");
let result = time::floor(rebirth_date, duration);

assert_eq!(
    result.to_raw().

build(),
    "time::floor(rebirth_date, duration)"
);
}

time::format()

The time::format() function outputs a datetime according to a specific format.

Usage:

#![allow(unused)]
fn main() {
use surreal_orm::{*, functions::time};

let dt = chrono::DateTime::<chrono::Utc>::from_naive_utc_and_offset(
    chrono::NaiveDateTime::from_timestamp_opt(61, 0).unwrap(),
    chrono::Utc,
);
let format_str = "'Year: 'yyyy-MM-dd";

let result = time::format(dt, format_str);
assert_eq!(
    result.to_raw().build(),
    "time::format('1970-01-01T00:01:01Z', 'Year: 'yyyy-MM-dd)"
);
}

Example:

#![allow(unused)]
fn main() {
use surreal_orm::{*, functions::time};

let dt = chrono::DateTime::<chrono::Utc>::from_naive_utc_and_offset(
    chrono::NaiveDateTime::from_timestamp_opt(61, 0).unwrap(),
    chrono::Utc,
);
let format_str = "'Year: 'yyyy-MM-dd";

let result = time::format(dt, format_str);
assert_eq!(
    result.to_raw().build(),
    "time::format('1970-01-01T00:01:01Z', 'Year: 'yyyy-MM-dd)"
);
}

time::group()

The time::group() function groups a datetime by a particular time interval.

Usage:

#![allow(unused)]
fn main() {
use surreal_orm::{*, functions::time};

let dt = chrono::DateTime::<chrono::Utc>::from_naive_utc_and_offset(
    chrono::NaiveDateTime::from_timestamp_opt(61, 0).unwrap(),
    chrono::Utc,
);
let interval = "month";

let result = time::group(dt, interval);
assert_eq!(
    result.to_raw().build(),
    "time::group('1970-01-01T00:01:01Z', 'month')"
);

let rebirth_date = Field::new("rebirth_date");
let interval_field = Field::new("interval");
let result = time::group(rebirth_date, interval_field);
assert_eq!(
    result.to_raw().build(),
    "time::group(rebirth_date, interval)"
);

let param = Param::new("rebirth_date");
let result = time::group(param, interval);
assert_eq!(
    result.to_raw().build(),
    "time::group($rebirth_date, 'month')"
);
}

Example:

#![allow(unused)]
fn main() {
use surreal_orm::{*, functions::time};

let dt = chrono::DateTime::<chrono::Utc>::from_naive_utc_and_offset(
    chrono::NaiveDateTime::from_timestamp_opt(61, 0).unwrap(),
    chrono::Utc,
);
let interval = "month";

let result = time::group(dt, interval);
assert_eq!(
    result.to_raw().build(),
    "time::group('1970-01-01T00:01:01Z', 'month')"
);

let rebirth_date = Field::new("rebirth_date");
let interval_field = Field::new("interval");
let result = time::group(rebirth_date, interval_field);
assert_eq!(
    result.to_raw().build(),
    "time::group(rebirth_date, interval)"
);

let param = Param::new("rebirth_date");
let result = time::group(param, interval);
assert_eq!(
    result.to_raw().build(),
    "time::group($rebirth_date, 'month')"
);
}

time::hour()

The time::hour() function extracts the hour as a number

from a datetime.

Usage:

#![allow(unused)]
fn main() {
use surreal_orm::{*, functions::time};

let dt = chrono::DateTime::<chrono::Utc>::from_naive_utc_and_offset(
    chrono::NaiveDateTime::from_timestamp_opt(61, 0).unwrap(),
    chrono::Utc,
);

let result = time::hour(dt);
assert_eq!(
    result.to_raw().build(),
    "time::hour('1970-01-01T00:01:01Z')"
);

let rebirth_date = Field::new("rebirth_date");
let result = time::hour(rebirth_date);
assert_eq!(result.to_raw().build(), "time::hour(rebirth_date)");

let param = Param::new("rebirth_date");
let result = time::hour(param);
assert_eq!(result.to_raw().build(), "time::hour($rebirth_date)");
}

Example:

#![allow(unused)]
fn main() {
use surreal_orm::{*, functions::time};

let dt = chrono::DateTime::<chrono::Utc>::from_naive_utc_and_offset(
    chrono::NaiveDateTime::from_timestamp_opt(61, 0).unwrap(),
    chrono::Utc,
);

let result = time::hour(dt);
assert_eq!(
    result.to_raw().build(),
    "time::hour('1970-01-01T00:01:01Z')"
);

let rebirth_date = Field::new("rebirth_date");
let result = time::hour(rebirth_date);
assert_eq!(result.to_raw().build(), "time::hour(rebirth_date)");

let param = Param::new("rebirth_date");
let result = time::hour(param);
assert_eq!(result.to_raw().build(), "time::hour($rebirth_date)");
}

time::minute()

The time::minute() function extracts the minutes as a number from a datetime.

Usage:

#![allow(unused)]
fn main() {
use surreal_orm::{*, functions::time};

let dt = chrono::DateTime::<chrono::Utc>::from_naive_utc_and_offset(
    chrono::NaiveDateTime::from_timestamp_opt(61, 0).unwrap(),
    chrono::Utc,
);

let result = time::minute(dt);
assert_eq!(
    result.to_raw().build(),
    "time::minute('1970-01-01T00:01:01Z')"
);

let rebirth_date = Field::new("rebirth_date");
let result = time::minute(rebirth_date);
assert_eq!(result.to_raw().build(), "time::minute(rebirth_date)");

let param = Param::new("rebirth_date");
let result = time::minute(param);
assert_eq!(result.to_raw().build(), "time::minute($rebirth_date)");
}

Example:

#![allow(unused)]
fn main() {
use surreal_orm::{*, functions::time};

let dt = chrono::DateTime::<chrono::Utc>::from_naive_utc_and_offset(
    chrono::NaiveDateTime::from_timestamp_opt(61, 0).unwrap(),
    chrono::Utc,
);

let result = time::minute(dt);
assert_eq!(
    result.to_raw().build(),
    "time::minute('1970-01-01T00:01:01Z')"
);

let rebirth_date = Field::new("rebirth_date");
let result = time::minute(rebirth_date);
assert_eq!(result.to_raw().build(), "time::minute(rebirth_date)");

let param = Param::new("rebirth_date");
let result = time::minute(param);
assert_eq!(result.to_raw().build(), "time::minute($rebirth_date)");
}

time::month()

The time::month() function extracts the month as a number from a datetime.

Usage:

#![allow(unused)]
fn main() {
use surreal_orm::{*, functions::time};

let dt = chrono::DateTime::<chrono::Utc>::from_naive_utc_and_offset(
    chrono::NaiveDateTime::from_timestamp_opt(61, 0).unwrap(),
    chrono::Utc,
);

let result = time::month(dt);
assert_eq!(
    result.to_raw().build(),
    "time::month('1970-01-01T00:01:01Z')"
);

let rebirth_date = Field::new("rebirth_date");
let result = time::month(rebirth_date);
assert_eq!(result.to_raw().build(), "time::month(rebirth_date)");

let param = Param::new("rebirth_date");
let result = time::month(param);
assert_eq!(result.to_raw().build(), "time::month($rebirth_date)");
}

Example:

#![allow(unused)]
fn main() {
use surreal_orm::{*, functions::time};

let dt = chrono::DateTime::<chrono::Utc>::from_naive_utc_and_offset(
    chrono::NaiveDateTime::from_timestamp_opt(61, 0).unwrap(),
    chrono::Utc,
);

let result = time::month(dt);
assert_eq!(
    result.to_raw().build(),
    "time::month('1970-01-01T00:01:01Z')"
);

let rebirth_date = Field::new("rebirth_date");
let result = time::month(rebirth_date);
assert_eq!(result.to_raw().build(), "time::month(rebirth_date)");

let param = Param::new("rebirth_date");
let result = time::month(param);
assert_eq!(result.to_raw().build(), "time::month($rebirth_date)");
}

time::nano()

The time::nano() function returns the number of nanoseconds since the UNIX epoch.

Usage:

#![allow(unused)]
fn main() {
use surreal_orm::{*, functions::time};

let result = time::nano();
assert_eq!(result.to_raw().build(), "time::nano()");
}

Example:

#![allow(unused)]
fn main() {
use surreal_orm::{*, functions::time};

let result = time::nano();
assert_eq!(result.to_raw().build(), "time::nano()");
}

time::now()

The time::now() function returns the current datetime.

Usage:

#![allow(unused)]
fn main() {
use surreal_orm::{*, functions::time};

let result = time::now();
assert_eq!(result.to_raw().build(), "time::now()");
}

Example:

#![allow(unused)]
fn main() {
use surreal_orm::{*, functions::time};

let result = time::now();
assert_eq!(result.to_raw().build(), "time::now()");
}

time::round()

The time::round() function rounds a datetime to the nearest multiple of a specific duration.

Usage:

#![allow(unused)]
fn main() {
use surreal_orm::{*, functions::time};

let rebirth_date = Field::new("rebirth_date");
let duration = Field::new("duration");
let result = time::round(rebirth_date, duration);

assert_eq!(
    result.to_raw().build(),
    "time::round(rebirth_date, duration)"
);
}

Example:

#![allow(unused)]
fn main() {
use surreal_orm::{*, functions::time};

let rebirth_date = Field::new("rebirth_date");
let duration = Field::new("duration");
let result = time::round(rebirth_date, duration);

assert_eq!(
    result.to_raw().build(),
    "time::round(rebirth_date, duration)"
);
}

time::second() </a

The time::second() function extracts the second as a number from a datetime.

Usage:

#![allow(unused)]
fn main() {
use surreal_orm::{*, functions::time};

let dt = chrono::DateTime::<chrono::Utc>::from_naive_utc_and_offset(
    chrono::NaiveDateTime::from_timestamp_opt(61, 0).unwrap(),
    chrono::Utc,
);

let result = time::second(dt);
assert_eq!(
    result.to_raw().build(),
    "time::second('1970-01-01T00:01:01Z')"
);

let rebirth_date = Field::new("rebirth_date");
let result = time::second(rebirth_date);
assert_eq!(result.to_raw().build(), "time::second(rebirth_date)");

let param = Param::new("rebirth_date");
let result = time::second(param);
assert_eq!(result.to_raw().build(), "time::second($rebirth_date)");
}

Example:

#![allow(unused)]
fn main() {
use surreal_orm::{*, functions::time};

let dt = chrono::DateTime::<chrono::Utc>::from_naive_utc_and_offset(
    chrono::NaiveDateTime::from_timestamp_opt(61, 0).unwrap(),
    chrono::Utc,
);

let result = time::second(dt);
assert_eq!(
    result.to_raw().build(),
    "time::second('1970-01-01T00:01:01Z')"
);

let rebirth_date = Field::new("rebirth_date");
let result = time::second(rebirth_date);
assert_eq!(result.to_raw().build(), "time::second(rebirth_date)");

let param = Param::new("rebirth_date");
let result = time::second(param);
assert_eq!(result.to_raw().build(), "time::second($rebirth_date)");
}

time::timezone()

The time::timezone() function returns the current local timezone offset in hours.

Usage:

#![allow(unused)]
fn main() {
use surreal_orm::{*, functions::time};

let result = time::timezone();
assert_eq!(result.to_raw().build(), "time::timezone()");
}

Example:

#![allow(unused)]
fn main() {
use surreal_orm::{*, functions::time};

let result = time::timezone();
assert_eq!(result.to_raw().build(), "time::timezone()");
}

time::unix()

The time::unix() function returns the number of seconds since the UNIX epoch.

Usage:

#![allow(unused)]
fn main() {
use surreal_orm::{*, functions::time};

let result = time::unix();
assert_eq!(result.to_raw().build(), "time::unix()");
}

Example:

#![allow(unused)]
fn main() {
use surreal_orm::{*, functions::time};

let result = time::unix();
assert_eq!(result.to_raw().build(), "time::unix()");
}

time::wday()

The time::wday() function extracts the week day as a number from a datetime.

Usage:

#![allow(unused)]
fn main() {
use surreal_orm::{*, functions::time};

let dt = chrono::DateTime::<chrono::Utc>::from_naive_utc_and_offset(
    chrono::NaiveDateTime::from_timestamp_opt(61, 0).unwrap(),
    chrono::Utc,
);

let result = time::wday(dt);
assert_eq!(
    result.to_raw().build(),
    "time::wday('1970-01-01T00:01:01Z')"
);

let rebirth_date = Field::new("rebirth_date");
let result = time::wday(rebirth_date);
assert_eq!(result.to_raw().build(), "time::wday(rebirth_date)");



let param = Param::new("rebirth_date");
let result = time::wday(param);
assert_eq!(result.to_raw().build(), "time::wday($rebirth_date)");
}

Example:

#![allow(unused)]
fn main() {
use surreal_orm::{*, functions::time};

let dt = chrono::DateTime::<chrono::Utc>::from_naive_utc_and_offset(
    chrono::NaiveDateTime::from_timestamp_opt(61, 0).unwrap(),
    chrono::Utc,
);

let result = time::wday(dt);
assert_eq!(
    result.to_raw().build(),
    "time::wday('1970-01-01T00:01:01Z')"
);

let rebirth_date = Field::new("rebirth_date");
let result = time::wday(rebirth_date);
assert_eq!(result.to_raw().build(), "time::wday(rebirth_date)");

let param = Param::new("rebirth_date");
let result = time::wday(param);
assert_eq!(result.to_raw().build(), "time::wday($rebirth_date)");
}

time::week()

The time::week() function extracts the week as a number from a datetime.

Usage:

#![allow(unused)]
fn main() {
use surreal_orm::{*, functions::time};

let dt = chrono::DateTime::<chrono::Utc>::from_naive_utc_and_offset(
    chrono::NaiveDateTime::from_timestamp_opt(61, 0).unwrap(),
    chrono::Utc,
);

let result = time::week(dt);
assert_eq!(
    result.to_raw().build(),
    "time::week('1970-01-01T00:01:01Z')"
);

let rebirth_date = Field::new("rebirth_date");
let result = time::week(rebirth_date);
assert_eq!(result.to_raw().build(), "time::week(rebirth_date)");

let param = Param::new("rebirth_date");
let result = time::week(param);
assert_eq!(result.to_raw().build(), "time::week($rebirth_date)");
}

Example:

#![allow(unused)]
fn main() {
use surreal_orm::{*, functions::time};

let dt = chrono::DateTime::<chrono::Utc>::from_naive_utc_and_offset(
    chrono::NaiveDateTime::from_timestamp_opt(61, 0).unwrap(),
    chrono::Utc,
);

let result = time::week(dt);
assert_eq!(
    result.to_raw().build(),
    "time::week('1970-01-01T00:01:01Z')"
);

let rebirth_date = Field::new("rebirth_date");
let result = time::week(rebirth_date);
assert_eq!(result.to_raw().build(), "time::week(rebirth_date)");

let param = Param::new("rebirth_date");
let result = time::week(param);
assert_eq!(result.to_raw().build(), "time::week($rebirth_date)");
}

time::yday()

The time::yday() function extracts the yday as a number from a datetime.

Usage:

#![allow(unused)]
fn main() {
use surreal_orm::{*, functions::time};

let dt = chrono::DateTime::<chrono::Utc>::from_naive_utc_and_offset(
    chrono::NaiveDateTime::from_timestamp_opt(61, 0).unwrap(),
    chrono::Utc,
);

let result = time::yday(dt);
assert_eq!(
    result.to_raw().build(),
    "time::yday('1970-01-01T00:01:01Z')"
);

let rebirth_date = Field::new("rebirth_date");
let result = time::yday(rebirth_date);
assert_eq!(result.to_raw().build(), "

time::yday(rebirth_date)");

let param = Param::new("rebirth_date");
let result = time::yday(param);
assert_eq!(result.to_raw().build(), "time::yday($rebirth_date)");
}

Example:

#![allow(unused)]
fn main() {
use surreal_orm::{*, functions::time};

let dt = chrono::DateTime::<chrono::Utc>::from_naive_utc_and_offset(
    chrono::NaiveDateTime::from_timestamp_opt(61, 0).unwrap(),
    chrono::Utc,
);

let result = time::yday(dt);
assert_eq!(
    result.to_raw().build(),
    "time::yday('1970-01-01T00:01:01Z')"
);

let rebirth_date = Field::new("rebirth_date");
let result = time::yday(rebirth_date);
assert_eq!(result.to_raw().build(), "time::yday(rebirth_date)");

let param = Param::new("rebirth_date");
let result = time::yday(param);
assert_eq!(result.to_raw().build(), "time::yday($rebirth_date)");
}

time::year()

The time::year() function extracts the year as a number from a datetime.

Usage:

#![allow(unused)]
fn main() {
use surreal_orm::{*, functions::time};

let dt = chrono::DateTime::<chrono::Utc>::from_naive_utc_and_offset(
    chrono::NaiveDateTime::from_timestamp_opt(61, 0).unwrap(),
    chrono::Utc,
);

let result = time::year(dt);
assert_eq!(
    result.to_raw().build(),
    "time::year('1970-01-01T00:01:01Z')"
);

let rebirth_date = Field::new("rebirth_date");
let result = time::year(rebirth_date);
assert_eq!(result.to_raw().build(), "time::year(rebirth_date)");

let param = Param::new("rebirth_date");
let result = time::year(param);
assert_eq!(result.to_raw().build(), "time::year($rebirth_date)");
}

Example:

#![allow(unused)]
fn main() {
use surreal_orm::{*, functions::time};

let dt = chrono::DateTime::<chrono::Utc>::from_naive_utc_and_offset(
    chrono::NaiveDateTime::from_timestamp_opt(61, 0).unwrap(),
    chrono::Utc,
);

let result = time::year(dt);
assert_eq!(
    result.to_raw().build(),
    "time::year('1970-01-01T00:01:01Z')"
);

let rebirth_date = Field::new("rebirth_date");
let result = time::year(rebirth_date);
assert_eq!(result.to_raw().build(), "time::year(rebirth_date)");

let param = Param::new("rebirth_date");
let result = time::year(param);
assert_eq!(result.to_raw().build(), "time::year($rebirth_date)");
}

Type functions

Table of Contents

type_::bool!()

The type_::bool!() macro allows you to convert a value into a boolean.

Examples:

#![allow(unused)]
fn main() {
use surreal_orm::macros::type_;

let result = type_::bool!(43545);
assert_eq!(result.to_raw().build(), "type::bool(43545)");

let bool_field = Field::new("bool_field");
let result = type_::bool!(bool_field);
assert_eq!(result.to_raw().build(), "type::bool(bool_field)");

let bool_param = Param::new("bool_param");
let result = type_::bool!(bool_param);
assert_eq!(result.to_raw().build(), "type::bool($bool_param)");
}

type_::datetime!()

The type_::datetime!() macro allows you to convert a value into a datetime.

Examples:

#![allow(unused)]
fn main() {
use surreal_orm::macros::type_;
use chrono::DateTime;
use chrono::Utc;

let value = DateTime::<Utc>::from_naive_utc_and_offset(chrono::NaiveDateTime::from_timestamp_opt(61, 0).unwrap(), Utc);
let result = type_::datetime!(value);
assert_eq!(result.to_raw().build(), "type::datetime('1970-01-01T00:01:01Z')");

let datetime_field = Field::new("datetime_field");
let result = type_::datetime!(datetime_field);
assert_eq!(result.to_raw().build(), "type::datetime(datetime_field)");

let datetime_param = Param::new("datetime_param");
let result = type_::datetime!(datetime_param);
assert_eq!(result.to_raw().build(), "type::datetime($datetime_param)");
}

type_::decimal!()

The type_::decimal!() macro allows you to convert a value into a decimal.

Examples:

#![allow(unused)]
fn main() {
use surreal_orm::macros::type_;

let result = type_::decimal!(1234.56);
assert_eq!(result.to_raw().build(), "type::decimal(1234.56)");

let decimal_field = Field::new("decimal_field");
let result = type_::decimal!(decimal_field);
assert_eq!(result.to_raw().build(), "type::decimal(decimal_field)");

let decimal_param = Param::new("decimal_param");
let result = type_::decimal!(decimal_param);
assert_eq!(result.to_raw().build(), "type::decimal($decimal_param)");
}

type_::duration!()

The type_::duration!() macro allows you to convert a value into a duration.

Examples:

#![allow(unused)]


fn main() {
use surreal_orm::macros::type_;
use std::time::Duration;

let result = type_::duration!(Duration::from_secs(24 * 60 * 60 * 7));
assert_eq!(result.to_raw().build(), "type::duration(1w)");

let duration_field = Field::new("duration_field");
let result = type_::duration!(duration_field);
assert_eq!(result.to_raw().build(), "type::duration(duration_field)");

let duration_param = Param::new("duration_param");
let result = type_::duration!(duration_param);
assert_eq!(result.to_raw().build(), "type::duration($duration_param)");
}

type_::float!()

The type_::float!() macro allows you to convert a value into a floating point number.

Examples:

#![allow(unused)]
fn main() {
use surreal_orm::macros::type_;

let result = type_::float!(43.5);
assert_eq!(result.to_raw().build(), "type::float(43.5)");

let float_field = Field::new("float_field");
let result = type_::float!(float_field);
assert_eq!(result.to_raw().build(), "type::float(float_field)");

let float_param = Param::new("float_param");
let result = type_::float!(float_param);
assert_eq!(result.to_raw().build(), "type::float($float_param)");
}

type_::int!()

The type_::int!() macro allows you to convert a value into an integer.

Examples:

#![allow(unused)]
fn main() {
use surreal_orm::macros::type_;

let result = type_::int!(99);
assert_eq!(result.to_raw().build(), "type::int(99)");

let int_field = Field::new("int_field");
let result = type_::int!(int_field);
assert_eq!(result.to_raw().build(), "type::int(int_field)");

let int_param = Param::new("int_param");
let result = type_::int!(int_param);
assert_eq!(result.to_raw().build(), "type::int($int_param)");
}

type_::number!()

The type_::number!() macro allows you to convert a value into a number.

Examples:

#![allow(unused)]
fn main() {
use surreal_orm::macros::type_;

let result = type_::number!(5);
assert_eq!(result.to_raw().build(), "type::number(5)");

let number_field = Field::new("number_field");
let result = type_::number!(number_field);
assert_eq!(result.to_raw().build(), "type::number(number_field)");

let number_param = Param::new("number_param");
let result = type_::number!(number_param);
assert_eq!(result.to_raw().build(), "type::number($number_param)");
}

type_::point!()

The type_::point!() macro allows you to convert a value into a geometry point.

Examples:

#![allow(unused)]
fn main() {
use surreal_orm::macros::type_;

let result = type_::point!(51.509865, -0.118092);
assert_eq!(result.to_raw().build(), "type::point(51.509865, -0.118092)");

let point_field = Field::new("point_field");
let result = type_::point!(point_field);
assert_eq!(result.to_raw().build(), "type::point(point_field)");

let point_param = Param::new("point_param");
let result = type

_::point!(point_param);
assert_eq!(result.to_raw().build(), "type::point($point_param)");
}

type_::regex!()

The type_::regex!() macro allows you to convert a value into a regular expression.

Examples:

#![allow(unused)]
fn main() {
use surreal_orm::macros::type_;

let result = type_::regex!("/[A-Z]{3}/");
assert_eq!(result.to_raw().build(), "type::regex('/[A-Z]{3}/')");

let regex_field = Field::new("regex_field");
let result = type_::regex!(regex_field);
assert_eq!(result.to_raw().build(), "type::regex(regex_field)");

let regex_param = Param::new("regex_param");
let result = type_::regex!(regex_param);
assert_eq!(result.to_raw().build(), "type::regex($regex_param)");
}

type_::string!()

The type_::string!() macro allows you to convert a value into a string.

Examples:

#![allow(unused)]
fn main() {
use surreal_orm::macros::type_;

let result = type_::string!(5454);
assert_eq!(result.to_raw().build(), "type::string(5454)");

let string_field = Field::new("string_field");
let result = type_::string!(string_field);
assert_eq!(result.to_raw().build(), "type::string(string_field)");

let string_param = Param::new("string_param");
let result = type_::string!(string_param);
assert_eq!(result.to_raw().build(), "type::string($string_param)");
}

type_::table!()

The type_::table!() macro allows you to convert a value into a table definition.

Examples:

#![allow(unused)]
fn main() {
use surreal_orm::macros::type_;
use surreal_orm::statements::let_;

let result = type_::table!("user");
assert_eq!(result.to_raw().build(), "type::table(user)");

let table_field = Field::new("table_field");
let result = type_::table!(table_field);
assert_eq!(result.to_raw().build(), "type::table(table_field)");

let table_param = let_("table_param").equal_to("user").get_param();
let result = type_::table!(table_param);
assert_eq!(result.to_raw().build(), "type::table($table_param)");
}

type_::thing!()

The type_::thing!() macro allows you to convert a value into a record pointer.

Examples:

#![allow(unused)]
fn main() {
use surreal_orm::macros::type_;
use surreal_orm::Table;

let user = Table::from("user");
let id = "oyelowo";
let result = type_::thing!(user, id);
assert_eq!(result.to_raw().build(), "type::thing(user, 'oyelowo')");

let table = Table::new("table");
let id = Field::new("id");
let result = type_::thing!(table, id);
assert_eq!(result.to_raw().build(), "type::thing(table, id)");
}

Scripting function

Table of Contents

function!()

The function!() macro allows you to define JavaScript functions with different parameters and function bodies.

Example:

#![allow(unused)]
fn main() {
use surreal_orm::macros::function;
use surreal_orm::statements::let_;

let value = let_("value").equal_to("SurrealDB").get_param();
let words = let_("words").equal_to(vec!["awesome", "advanced", "cool"]).get_param();

let f2 = function!(
    (value, words),
    "{ return `${arguments[0]} is ${arguments[1]}`; }"
);

assert_eq!(
    f2.build(),
    "function($value, $words) { return `${arguments[0]} is ${arguments[1]}`; }"
);

assert_eq!(
    f2.to_raw().build(),
    "function($value, $words) { return `${arguments[0]} is ${arguments[1]}`; }"
);
}

Conclusion

Throughout our exploration of Surreal ORM, we've traversed the vast landscape of database management, from the foundational concepts of Object-Relational Mapping to the intricate nuances of creating advanced and efficient database queries. The journey, while occasionally challenging, has been immensely rewarding.

We started by understanding the core tenets of ORM and how Surreal ORM aims to revolutionize the way we interact with databases. The emphasis on a more intuitive, macro-driven approach not only simplifies query building but also offers a level of flexibility previously unseen in conventional ORM tools.

Our in-depth discussions shed light on the importance of efficient query building. With the help of the surreal_orm query builder, we explored how to craft complex queries that maintain readability and efficiency. The cond function and cond! macro was a particular highlight, providing a convenient method to generate filters without sacrificing clarity or performance.

The challenges of nested queries and multi-level operations were not overlooked. By introducing an elegant solution in the form of recursive macros, Surreal ORM ensures that developers can handle any level of complexity with ease.

However, it's essential to remember that while tools like Surreal ORM can significantly simplify database operations, the true power lies in understanding the underlying principles. A tool is only as good as the craftsman wielding it. Your grasp on the intricacies of ORM and your ability to adapt and innovate will define the success of your projects.

As we conclude this guide, it's worth noting that the world of technology is ever-evolving. While Surreal ORM offers a robust solution for today's challenges, the future might bring new requirements. Engage with the Surreal ORM community, share your insights, learn from others, and contribute towards refining and expanding this already formidable tool.

Thank you for accompanying me on this enlightening journey through Surreal ORM. Armed with the knowledge from our discussions, you're now well-equipped to harness the full potential of this tool. Here's to building more efficient, intuitive, and scalable applications!