vincent-herlemont / native_db

Drop-in embedded database in Rust
MIT License
433 stars 17 forks source link

Does native_db support nested structures? #205

Closed Raj2032 closed 2 months ago

Raj2032 commented 2 months ago

in this example code:

use serde::{Deserialize, Serialize};
use native_db::*;
use native_model::{native_model, Model};
use once_cell::sync::Lazy;

#[derive(Serialize, Deserialize, PartialEq, Debug)]
#[native_model(id = 1, version = 1)]
#[native_db]
struct Item {
    #[primary_key]
    id: u32,
    #[secondary_key]
    name: Name,
}

struct Name {
    first: String,
    last: String,
}

// Define the models
// The lifetime of the models needs to be longer or equal to the lifetime of the database.
// In many cases, it is simpler to use a static variable but it is not mandatory.
static MODELS: Lazy<Models> = Lazy::new(|| {
    let mut models = Models::new();
    models.define::<Item>().unwrap();
    models
});

fn main() -> Result<(), db_type::Error> {
    // Create a database in memory
    let mut db = Builder::new().create_in_memory(&MODELS)?;

    // Insert data (open a read-write transaction)
    let rw = db.rw_transaction().unwrap();
    rw.insert(Item { id: 1, name: Name{first: "John".to_string(), last: "Doe".to_string()})?;
    rw.commit()?;

    // Open a read-only transaction
    let r = db.r_transaction()?;
    // Retrieve data with id=3 
    let retrieve_data: Item = r.get().primary(1_u32)?.unwrap();
    println!("data id='3': {:?}", retrieve_data);

    for item in r.scan().secondary::<Item>(ItemKey::name)?.start_with("John") {
        println!("data name=\"red\": {:?}", item);
    }

    // Remove data (open a read-write transaction)
    let rw = db.rw_transaction()?;
    rw.remove(retrieve_data)?;
    rw.commit()?;
    Ok(())
}

In this example code not too sure if its possible to query just the first name using a nested structure if so may I be provided with an example on how I would achieve this?

vincent-herlemont commented 2 months ago

Here is an example using ToKey (read the documentation carefully).

use native_db::*;
use native_model::{native_model, Model};
use once_cell::sync::Lazy;
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, PartialEq, Debug)]
#[native_model(id = 1, version = 1)]
#[native_db]
struct Item {
    #[primary_key]
    id: u32,
    #[secondary_key]
    name: Name,
}

#[derive(Debug, Deserialize, Serialize, PartialEq, Eq)]
struct Name {
    first: String,
    last: String,
}

impl ToKey for &Name {
    fn to_key(&self) -> Key {
        // Concatenate the first and last name
        let mut key = self.first.clone();
        key.push_str(&self.last);
        Key::new(key.as_bytes().to_vec())
    }
}

// Define the models
// The lifetime of the models needs to be longer or equal to the lifetime of the database.
// In many cases, it is simpler to use a static variable but it is not mandatory.
static MODELS: Lazy<Models> = Lazy::new(|| {
    let mut models = Models::new();
    models.define::<Item>().unwrap();
    models
});

fn main() -> Result<(), db_type::Error> {
    // Create a database in memory
    let db = Builder::new().create_in_memory(&MODELS)?;

    // Insert data (open a read-write transaction)
    let rw: transaction::RwTransaction = db.rw_transaction().unwrap();
    rw.insert(Item { id: 1, name: Name{first: "John".to_string(), last: "Doe".to_string()}})?;
    rw.commit()?;

    // Open a read-only transaction
    let r = db.r_transaction()?;
    // Retrieve data with id=3 
    let retrieve_data: Item = r.get().primary(1_u32)?.unwrap();
    println!("data id='3': {:?}", retrieve_data);

    for item in r.scan().secondary::<Item>(ItemKey::name)?.start_with("John") {
        println!("data name=\"red\": {:?}", item);
    }

    // Remove data (open a read-write transaction)
    let rw = db.rw_transaction()?;
    rw.remove(retrieve_data)?;
    rw.commit()?;
    Ok(())
}

Here is an example using custom keys. Read this documentation carefully Models.define.

use native_db::*;
use native_model::{native_model, Model};
use once_cell::sync::Lazy;
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, PartialEq, Debug)]
#[native_model(id = 1, version = 1)]
#[native_db(secondary_key(name))]
struct Item {
    #[primary_key]
    id: u32,
    name: Name,
}

impl Item {
    pub fn name(&self) -> String {
        format!("{} {}", self.name.first, self.name.last)
    }
}

#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct Name {
    first: String,
    last: String,
}

// Define the models
// The lifetime of the models needs to be longer or equal to the lifetime of the database.
// In many cases, it is simpler to use a static variable but it is not mandatory.
static MODELS: Lazy<Models> = Lazy::new(|| {
    let mut models = Models::new();
    models.define::<Item>().unwrap();
    models
});

fn main() -> Result<(), db_type::Error> {
    // Create a database in memory
    let db = Builder::new().create_in_memory(&MODELS)?;

    // Insert data (open a read-write transaction)
    let rw: transaction::RwTransaction = db.rw_transaction().unwrap();
    rw.insert(Item { id: 1, name: Name{first: "John".to_string(), last: "Doe".to_string()}})?;
    rw.commit()?;

    // Open a read-only transaction
    let r = db.r_transaction()?;
    // Retrieve data with id=3 
    let retrieve_data: Item = r.get().primary(1_u32)?.unwrap();
    println!("data id='3': {:?}", retrieve_data);

    for item in r.scan().secondary::<Item>(ItemKey::name)?.start_with("John") {
        println!("data name=\"red\": {:?}", item);
    }

    // Remove data (open a read-write transaction)
    let rw = db.rw_transaction()?;
    rw.remove(retrieve_data)?;
    rw.commit()?;
    Ok(())
}

@Raj2032 If there are any problems with the explanations, feel free to let me know and make a merge request; I would be delighted.