Expand description
Native DB is a Rust library that provides a simple, fast, and embedded database solution, focusing on maintaining coherence between Rust types and stored data with minimal boilerplate. It supports multiple indexes, real-time watch with filters, model migration, hot snapshot, and more.
§Summary
- Api
- Quick Start
- Advanced
§Api
Models
- Collection of models. Equivalent to a schema in a traditional database.Builder
- Builder to create a database.create_in_memory
- Create a database in memory.create
- Create a database in a file.open
- Open a database.
Database
- Database instance.compact
- Compact the database.check_integrity
- Check the integrity of the database.rw_transaction
- Create a read-write transaction.insert
- Insert a item, fail if the item already exists.upsert
- Upsert a item, update if the item already exists.update
- Update a item, replace an existing item.remove
- Remove a item, remove an existing item.migrate
- Migrate a model, affect all items.commit
- Commit the transaction.abort
- Abort the transaction.
r_transaction
- Create a read-only transaction.get
- Get a item.scan
- Scan items.primary
- Scan items by primary key.all
- Scan all items.start_with
- Scan items with a primary key starting with a key.range
- Scan items with a primary key in a given range.
secondary
- Scan items by secondary key.all
- Scan items with a given secondary key.start_with
- Scan items with a secondary key starting with a key.range
- Scan items with a secondary key in a given range.
len
- Get the number of items.
watch
- Watch items in real-time. Works via std channel based or tokio channel based depending on the featuretokio
.get
- Watch a item.scan
- Watch items.primary
- Watch items by primary key.all
- Watch all items.start_with
- Watch items with a primary key starting with a key.range
- Watch items with a primary key in a given range.
secondary
- Watch items by secondary key.all
- Watch items with a given secondary key.start_with
- Watch items with a secondary key starting with a key.range
- Watch items with a secondary key in a given range.
§Quick Start
We will create a simple example to show how to use the library.
§Create a model
👉 Unlike the usual database where there is a difference between schema and model, here, as we can directly use Rust types that are serialized in the database, we do not have the concept of schema, only that of the model.
In this section, we will create a simple model. I have chosen a particular organization using Rust modules, which I find to be a best practice. However, it is not mandatory; you can do it as you prefer. (see define
for more information)
In this example:
- We create a module
data
which contains all versions of all models. - We create a module
v1
which contains the first version of your data, we will put other versions later. - We create a type alias
Person
to the latest versionv1::Person
, which allows us to use the latest version of the model in the application.
pub mod data {
use native_db::{native_db, ToKey};
use native_model::{native_model, Model};
use serde::{Deserialize, Serialize};
pub type Person = v1::Person;
pub mod v1 {
use super::*;
#[derive(Serialize, Deserialize, Debug)]
#[native_model(id = 1, version = 1)]
#[native_db]
pub struct Person {
#[primary_key]
pub name: String,
}
}
}
§Create a database
After creating the model in the previous step, we can now create the database with the model.
Note good practices: define
the models
by specifying each version, in our case data::v1::Person
.
use native_db::*;
use once_cell::sync::Lazy;
// 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();
// It's a good practice to define the models by specifying the version
models.define::<data::v1::Person>().unwrap();
models
});
fn main() -> Result<(), db_type::Error> {
// Create the database
let db = Builder::new().create_in_memory(&MODELS)?;
Ok(())
}
§Insert a model in the database
Note a good practice: use the latest version of the model in your application.
In our case, we use data::Person
.
use native_db::*;
use once_cell::sync::Lazy;
fn main() -> Result<(), db_type::Error> {
// ... database creation see previous example
// Insert a person
let rw = db.rw_transaction()?;
// It's a good practice to use the latest version in your application
rw.insert(data::Person { name: "Alice".to_string() })?;
rw.commit()?;
// Get the person
let r = db.r_transaction()?;
let person: data::Person = r.get().primary("Alice".to_string())?.unwrap();
assert_eq!(person.name, "Alice");
Ok(())
}
§Update a model
We need to add the field age
to the Person
model, but data is already stored in the database so we need to migrate it.
To do this we have to:
- Create a version
v2
of the modelPerson
with the new fieldage
. - Implement the
From
(orTryFrom
) trait for the previous versionv1
to the new versionv2
, so we can migrate the data. See native_model#Data model for more information.
pub mod data {
// ... same imports
// Update the type alias to the latest version
pub type Person = v2::Person;
pub mod v1 {
// ... the previous version of Person
impl From<v2::Person> for Person {
fn from(p: v2::Person) -> Self {
Self {
name: p.name,
}
}
}
}
pub mod v2 {
use super::*;
#[derive(Serialize, Deserialize, Debug)]
#[native_model(id = 1, version = 2, from = v1::Person)]
#[native_db]
pub struct Person {
#[primary_key]
pub name: String,
pub age: u8,
}
impl From<v1::Person> for Person {
fn from(p: v1::Person) -> Self {
Self {
name: p.name,
age: 0,
}
}
}
}
}
§Migration
After updating the model, we need to define the new version v2
of the model Person
and migrate the data.
use native_db::*;
use once_cell::sync::Lazy;
static MODELS: Lazy<Models> = Lazy::new(|| {
let mut models = Models::new();
// Define the models by specifying the version
models.define::<data::v1::Person>().unwrap();
models.define::<data::v2::Person>().unwrap();
models
});
fn main() -> Result<(), db_type::Error> {
// Create the database
let db = Builder::new().create_in_memory(&MODELS)?;
// Migrate the data in a transaction
let rw = db.rw_transaction()?;
rw.migrate::<data::Person>()?;
rw.commit()?;
// Now we can insert a person with the new field age ...
Ok(())
}
More details migrate
method.
Re-exports§
Modules§
- A collection of type used by native_db internally (macro included).
- Database interactions here.
- Watch data in real-time.
Structs§
- The database instance. Allows you to create rw_transaction and r_transaction, watch queries, and unwatch etc.
- See the documentation crate::Models::define to see how to define a model.
Functions§
Attribute Macros§
- Macro which link
native_model
to the Native DB. SeeBuilder.define
for more information.
Derive Macros§
- Macro which link
native_model
to the Native DB. SeeBuilder.define
for more information.