Expand description
Just Another Memory Mapped Database
jammdb is an embedded, single-file database that allows you to store key / value pairs as bytes.
It started life as a Rust port of Ben Johnson’s BoltDB, which was inspired by Howard Chu’s LMDB, so please check out both of these awesome projects!
jammdb offers ACID compliance, serializable and isolated transactions, with multiple lock-free readers and a single concurrent writer. The data is organized in a single level B+ tree so random and sequential reads are very fast. The underlying file is memory mapped, so reads require no additional memory allocation.
jammdb is meant to be very simple, and has only a few exported types. It will allow you to store data in collections (called Buckets
),
and each bucket can contain any number of unique keys which map to either an arbitrary value (a &[u8]
) or a nested bucket. Examples on how to use jammdb are below.
There are also more examples in the docs, be sure to check out
- Using a
Cursor
to iterate over the data in a bucket - How to create and use multiple
Tx
s - Nested
Buckets
OpenOptions
to provide parameters for opening aDB
Examples
Simple put and get
use jammdb::{DB, Data, Error};
fn main() -> Result<(), Error> {
{
// open a new database file
let db = DB::open("my-database.db")?;
// open a writable transaction so we can make changes
let mut tx = db.tx(true)?;
// create a bucket to store a map of first names to last names
let mut names_bucket = tx.create_bucket("names")?;
names_bucket.put("Kanan", "Jarrus")?;
names_bucket.put("Ezra", "Bridger")?;
// commit the changes so they are saved to disk
tx.commit()?;
}
{
// open the existing database file
let db = DB::open("my-database.db")?;
// open a read-only transaction to get the data
let mut tx = db.tx(true)?;
// get the bucket we created in the last transaction
let names_bucket = tx.get_bucket("names")?;
// get the key / value pair we inserted into the bucket
if let Some(data) = names_bucket.get("Kanan") {
assert_eq!(data.kv().value(), b"Jarrus");
}
}
Ok(())
}
Storing structs
use jammdb::{DB, Data, Error};
use serde::{Deserialize, Serialize};
// use rmps crate to serialize structs using the MessagePack format
use rmp_serde::{Deserializer, Serializer};
#[derive(Debug, PartialEq, Deserialize, Serialize)]
struct User {
username: String,
password: String,
}
fn main() -> Result<(), Error> {
let user = User{
username: "my-user".to_string(),
password: "my-password".to_string(),
};
{
// open a new database file and start a writable transaction
let db = DB::open("my-database.db")?;
let mut tx = db.tx(true)?;
// create a bucket to store users
let mut users_bucket = tx.create_bucket("users")?;
// serialize struct to bytes and store in bucket
let user_bytes = rmp_serde::to_vec(&user).unwrap();
users_bucket.put("user1", user_bytes)?;
// commit the changes so they are saved to disk
tx.commit()?;
}
{
// open the existing database file
let db = DB::open("my-database.db")?;
// open a read-only transaction to get the data
let mut tx = db.tx(true)?;
// get the bucket we created in the last transaction
let users_bucket = tx.get_bucket("users")?;
// get the key / value pair we inserted into the bucket
if let Some(data) = users_bucket.get(b"user1") {
// deserialize into a user struct
let db_user: User = rmp_serde::from_slice(data.kv().value()).unwrap();
assert_eq!(db_user, user);
}
}
Ok(())
}
Structs
- A collection of data
- Bucket name
- An iterator over a bucket’s sub-buckets.
- An iterator over a bucket
- A database
- Key / Value Pair
- An iterator over a bucket’s key / value pairs.
- Options to configure how a
DB
is opened. - An isolated view of the database
Enums
- Key / Value or Bucket Data
- Possible database errors