Skip to main content
The Database struct is the primary entry point for the embedded API. It owns a multi-threaded shard engine and document engine, exposing typed methods for key-value, list, hash, set, vector, and document operations. All methods are safe to call from multiple threads simultaneously. Key-value operations route through per-shard channels and block the caller until the owning shard worker completes the command.

Opening a Database

Database::open

pub fn open(config: Config) -> Self
Open a new database with the given configuration. This spawns config.shard_count background worker threads that remain alive for the lifetime of the returned Database. Example:
use kora_embedded::{Config, Database};

let db = Database::open(Config::default());
db.set("greeting", b"hello world");
assert_eq!(db.get("greeting"), Some(b"hello world".to_vec()));
Multi-threaded usage:
use kora_embedded::{Config, Database};
use std::sync::Arc;

let db = Arc::new(Database::open(Config { shard_count: 4 }));
db.set("counter", b"0");
db.incr("counter").unwrap();
assert_eq!(db.get("counter"), Some(b"1".to_vec()));

String Operations

get

pub fn get(&self, key: &str) -> Option<Vec<u8>>
Return the value stored at key, or None if the key does not exist. Example:
db.set("hello", b"world");
assert_eq!(db.get("hello"), Some(b"world".to_vec()));
assert_eq!(db.get("nonexistent"), None);

set

pub fn set(&self, key: &str, value: &[u8])
Store a key-value pair, overwriting any existing value. Example:
db.set("user:1", b"Alice");
db.set("user:1", b"Bob"); // overwrites

set_ex

pub fn set_ex(&self, key: &str, value: &[u8], ttl: Duration)
Store a key-value pair that expires after ttl. Example:
use std::time::Duration;

db.set_ex("session:abc", b"user_data", Duration::from_secs(3600));

del

pub fn del(&self, key: &str) -> bool
Delete a key. Returns true if the key existed. Example:
db.set("temp", b"value");
assert!(db.del("temp"));
assert!(!db.del("temp")); // already deleted

exists

pub fn exists(&self, key: &str) -> bool
Check whether key exists in the store. Example:
db.set("key", b"value");
assert!(db.exists("key"));
assert!(!db.exists("nonexistent"));

incr

pub fn incr(&self, key: &str) -> Result<i64, String>
Atomically increment a key’s integer value by 1, returning the new value. Example:
assert_eq!(db.incr("counter").unwrap(), 1);
assert_eq!(db.incr("counter").unwrap(), 2);
assert_eq!(db.incr("counter").unwrap(), 3);

decr

pub fn decr(&self, key: &str) -> Result<i64, String>
Atomically decrement a key’s integer value by 1, returning the new value.

incrby

pub fn incrby(&self, key: &str, delta: i64) -> Result<i64, String>
Atomically increment a key’s integer value by delta, returning the new value. Example:
db.set("score", b"10");
assert_eq!(db.incrby("score", 5).unwrap(), 15);

decrby

pub fn decrby(&self, key: &str, delta: i64) -> Result<i64, String>
Atomically decrement a key’s integer value by delta, returning the new value.

getset

pub fn getset(&self, key: &str, value: &[u8]) -> Option<Vec<u8>>
Atomically set key to value and return the previous value, if any. Example:
db.set("counter", b"1");
let old = db.getset("counter", b"2");
assert_eq!(old, Some(b"1".to_vec()));
assert_eq!(db.get("counter"), Some(b"2".to_vec()));

append

pub fn append(&self, key: &str, value: &[u8]) -> i64
Append value to the string stored at key, returning the new byte length. Example:
db.set("message", b"Hello");
let len = db.append("message", b" World");
assert_eq!(len, 11);
assert_eq!(db.get("message"), Some(b"Hello World".to_vec()));

strlen

pub fn strlen(&self, key: &str) -> i64
Get the length of the string value stored at key.

mget

pub fn mget(&self, keys: &[&str]) -> Vec<Option<Vec<u8>>>
Return the values of multiple keys in a single call. Missing keys appear as None, preserving positional correspondence with keys. Example:
db.set("key1", b"value1");
db.set("key2", b"value2");
let values = db.mget(&["key1", "missing", "key2"]);
assert_eq!(values, vec![
    Some(b"value1".to_vec()),
    None,
    Some(b"value2".to_vec())
]);

mset

pub fn mset(&self, entries: &[(&str, &[u8])])
Store multiple key-value pairs in a single call. Example:
db.mset(&[
    ("key1", b"value1"),
    ("key2", b"value2"),
    ("key3", b"value3")
]);

setnx

pub fn setnx(&self, key: &str, value: &[u8]) -> bool
Store a key-value pair only if the key does not already exist. Returns true if the key was set, false if it already existed. Example:
assert!(db.setnx("lock", b"owner1")); // acquired
assert!(!db.setnx("lock", b"owner2")); // already exists

expire

pub fn expire(&self, key: &str, seconds: u64) -> bool
Set a time-to-live on key. Returns true if the key exists. Example:
db.set("session", b"data");
assert!(db.expire("session", 3600)); // 1 hour TTL

persist

pub fn persist(&self, key: &str) -> bool
Remove the time-to-live on key, making it persistent. Returns true if the key existed and had a TTL.

ttl

pub fn ttl(&self, key: &str) -> Option<i64>
Return the remaining time-to-live (in seconds) for key. Returns None if the key does not exist or has no TTL set.

key_type

pub fn key_type(&self, key: &str) -> String
Return the data type of the value stored at key (e.g. "string", "list", "hash", "set", "none").

keys

pub fn keys(&self, pattern: &str) -> Vec<Vec<u8>>
Find all keys matching a glob pattern. Example:
db.set("user:1", b"Alice");
db.set("user:2", b"Bob");
db.set("config:max", b"100");

let user_keys = db.keys("user:*");
assert_eq!(user_keys.len(), 2);

List Operations

lpush

pub fn lpush(&self, key: &str, values: &[&[u8]]) -> i64
Prepend one or more values to a list, returning the new length. Example:
let len = db.lpush("tasks", &[b"task3", b"task2", b"task1"]);
assert_eq!(len, 3);

rpush

pub fn rpush(&self, key: &str, values: &[&[u8]]) -> i64
Append one or more values to a list, returning the new length. Example:
db.rpush("list", &[b"a", b"b", b"c"]);
let items = db.lrange("list", 0, -1);
assert_eq!(items, vec![b"a".to_vec(), b"b".to_vec(), b"c".to_vec()]);

lrange

pub fn lrange(&self, key: &str, start: i64, stop: i64) -> Vec<Vec<u8>>
Return a contiguous range of elements from a list. Negative indices count from the end (-1 is the last element). Example:
db.rpush("numbers", &[b"1", b"2", b"3", b"4", b"5"]);
assert_eq!(db.lrange("numbers", 1, 3).len(), 3); // indices 1, 2, 3
assert_eq!(db.lrange("numbers", 0, -1).len(), 5); // all elements

lpop

pub fn lpop(&self, key: &str) -> Option<Vec<u8>>
Remove and return the first element of a list. Example:
db.rpush("queue", &[b"first", b"second"]);
assert_eq!(db.lpop("queue"), Some(b"first".to_vec()));
assert_eq!(db.llen("queue"), 1);

rpop

pub fn rpop(&self, key: &str) -> Option<Vec<u8>>
Remove and return the last element of a list.

llen

pub fn llen(&self, key: &str) -> i64
Return the number of elements in a list.

lindex

pub fn lindex(&self, key: &str, index: i64) -> Option<Vec<u8>>
Return the element at index in a list, or None if out of range. Example:
db.rpush("items", &[b"a", b"b", b"c"]);
assert_eq!(db.lindex("items", 1), Some(b"b".to_vec()));
assert_eq!(db.lindex("items", 10), None);

Hash Operations

hset

pub fn hset(&self, key: &str, field: &str, value: &[u8])
Set a field in a hash, creating the hash if it does not exist. Example:
db.hset("user", "name", b"Alice");
assert_eq!(db.hget("user", "name"), Some(b"Alice".to_vec()));
assert_eq!(db.hget("user", "age"), None);

hget

pub fn hget(&self, key: &str, field: &str) -> Option<Vec<u8>>
Return the value of a hash field, or None if the field or hash does not exist.

hdel

pub fn hdel(&self, key: &str, fields: &[&str]) -> i64
Remove one or more fields from a hash, returning the number of fields removed. Example:
db.hset("user", "name", b"Alice");
db.hset("user", "age", b"30");
let removed = db.hdel("user", &["age", "nonexistent"]);
assert_eq!(removed, 1);

hgetall

pub fn hgetall(&self, key: &str) -> Vec<(Vec<u8>, Vec<u8>)>
Return all field-value pairs from a hash. Example:
db.hset("user", "name", b"Alice");
db.hset("user", "age", b"30");
let all = db.hgetall("user");
assert_eq!(all.len(), 2);

hlen

pub fn hlen(&self, key: &str) -> i64
Return the number of fields in a hash.

hexists

pub fn hexists(&self, key: &str, field: &str) -> bool
Check whether a field exists in a hash.

hincrby

pub fn hincrby(&self, key: &str, field: &str, delta: i64) -> Result<i64, String>
Atomically increment a hash field’s integer value by delta, returning the new value. Example:
db.hset("stats", "views", b"10");
assert_eq!(db.hincrby("stats", "views", 5).unwrap(), 15);

Set Operations

sadd

pub fn sadd(&self, key: &str, members: &[&[u8]]) -> i64
Add one or more members to a set, returning the number of new members added. Example:
db.sadd("tags", &[b"rust", b"cache", b"rust"]);
let members = db.smembers("tags");
assert_eq!(members.len(), 2); // "rust" deduplicated

smembers

pub fn smembers(&self, key: &str) -> Vec<Vec<u8>>
Return all members of a set.

srem

pub fn srem(&self, key: &str, members: &[&[u8]]) -> i64
Remove one or more members from a set, returning the number of members removed.

sismember

pub fn sismember(&self, key: &str, member: &[u8]) -> bool
Check whether member belongs to the set stored at key. Example:
db.sadd("tags", &[b"rust", b"cache"]);
assert!(db.sismember("tags", b"rust"));
assert!(!db.sismember("tags", b"python"));

scard

pub fn scard(&self, key: &str) -> i64
Return the number of members in a set.

Vector Operations

vector_set

pub fn vector_set(&self, index: &str, dim: usize, vector: &[f32]) -> Result<u64, String>
Insert a vector into a named index, returning the vector ID. Creates the index if it does not exist. Example:
let v1 = vec![1.0f32, 0.0, 0.0, 0.0];
let v2 = vec![0.0f32, 1.0, 0.0, 0.0];
let v3 = vec![1.0f32, 1.0, 0.0, 0.0];

let id1 = db.vector_set("my_idx", 4, &v1).unwrap();
let id2 = db.vector_set("my_idx", 4, &v2).unwrap();
let id3 = db.vector_set("my_idx", 4, &v3).unwrap();
pub fn vector_search(
    &self,
    index: &str,
    query: &[f32],
    k: usize,
) -> Result<Vec<(u64, f32)>, String>
Search for the K nearest neighbors of a query vector. Returns a list of (id, distance) pairs sorted by distance. Example:
let query = vec![1.0f32, 0.0, 0.0, 0.0];
let results = db.vector_search("my_idx", &query, 3).unwrap();
assert!(!results.is_empty());
assert!(results[0].1 < 0.001, "first result should be near-exact match");

vector_del

pub fn vector_del(&self, index: &str) -> Result<bool, String>
Delete an entire vector index. Returns true if it existed. Example:
assert!(db.vector_del("my_idx").unwrap());
assert!(!db.vector_del("my_idx").unwrap()); // already deleted

Server Operations

db_size

pub fn db_size(&self) -> i64
Return the total number of keys across all shards. Example:
db.set("a", b"1");
db.set("b", b"2");
assert_eq!(db.db_size(), 2);

flush_db

pub fn flush_db(&self)
Remove all keys from every shard. Example:
db.set("a", b"1");
db.set("b", b"2");
db.flush_db();
assert_eq!(db.db_size(), 0);

Engine Access

engine

pub fn engine(&self) -> &ShardEngine
Return a reference to the underlying shard engine.

shared_engine

pub fn shared_engine(&self) -> Arc<ShardEngine>
Return a shared handle to the underlying shard engine. Useful when integrating with components that need their own Arc clone, such as hybrid server mode or custom command dispatch layers.

shared_doc_engine

pub fn shared_doc_engine(&self) -> Arc<RwLock<DocEngine>>
Return a shared handle to the embedded document engine. Callers are responsible for acquiring the inner RwLock appropriately.

Hybrid Mode

start_listener

#[cfg(feature = "server")]
pub fn start_listener(
    &self,
    addr: &str,
) -> Result<(JoinHandle<()>, tokio::sync::watch::Sender<bool>), String>
Start a TCP listener on addr for hybrid embedded + network mode. Returns a JoinHandle for the background server task and a watch::Sender that shuts the server down when true is sent. Note: the server creates its own shard stores — data is not shared with the embedded Database key-value store. This is intended for scenarios where external clients need independent access. Requires the server Cargo feature. Example:
use kora_embedded::{Config, Database};

#[tokio::main]
async fn main() {
    let db = Database::open(Config::default());
    let (handle, shutdown_tx) = db.start_listener("127.0.0.1:6379").unwrap();
    
    // Use db directly in this process
    db.set("key", b"value");
    
    // External clients can also connect via TCP
    
    // Shutdown when done
    shutdown_tx.send(true).unwrap();
    handle.await.unwrap();
}

Build docs developers (and LLMs) love