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.