Runnable code snippets for every major feature. Copy-paste into your project or run the included examples with cargo run --example <name>.
Open a database, write keys in different namespaces, read them back, and scan ranges.
use edgestore::{EdgestoreConfig, Engine};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = EdgestoreConfig::new("/tmp/kv_demo");
let mut db = Engine::open(config)?;
// Write in different namespaces
db.put(b"users", b"alice", b"{"name":"Alice"}")?;
db.put(b"users", b"bob", b"{"name":"Bob"}")?;
db.put(b"products", b"p100", b"Laptop")?;
// Point lookup
let alice = db.get(b"users", b"alice")?;
assert_eq!(alice, Some(b"{"name":"Alice"}".to_vec()));
// Range scan (exclusive end)
for (k, v) in db.range(b"users", b"a", b"z")? {
println!("{} = {}",
std::str::from_utf8(&k)?,
std::str::from_utf8(&v)?);
}
// Prefix scan
for (k, v) in db.prefix(b"products", b"p")? {
println!("product: {}", std::str::from_utf8(&v)?);
}
db.flush()?;
Ok(())
}
Run: cargo run --example basic_kv
Write time-to-live records, observe lazy expiry, and trigger compaction to reclaim space.
use std::time::Duration;
// Write a session that expires in 2 seconds
db.put_with_ttl(b"sessions", b"sess-1", b"active", 2)?;
// Still readable immediately (lazy expiry)
std::thread::sleep(Duration::from_secs(3));
let val = db.get(b"sessions", b"sess-1")?;
assert!(val.is_some(), "lazy expiry: still readable");
// Flush to segments, then compact
db.flush_to_segments()?;
let stats = db.compact_once()?;
// Now it's gone — the cohort fully expired, so zero bytes were relocated
let val = db.get(b"sessions", b"sess-1")?;
assert!(val.is_none());
println!("Compacted {} cohorts, {} bytes written",
stats.cohorts_collected, stats.bytes_written);
// bytes_written == 0 for fully-expired cohorts
Store embeddings and search by cosine similarity. Build an HNSW index for approximate search on large collections.
use edgestore::{Dtype, Metric};
use rand::random;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut db = Engine::open(EdgestoreConfig::new("/tmp/vec_demo"))?;
let dims = 128;
let ns = b"embeddings";
// Insert 1000 random f32 vectors
for i in 0..1000 {
let vec: Vec<f32> = (0..dims).map(|_| random::<f32>()).collect();
let bytes = unsafe {
std::slice::from_raw_parts(
vec.as_ptr() as *const u8,
vec.len() * std::mem::size_of::<f32>()
)
};
db.vector_put(ns, format!("vec-{i}").as_bytes(),
dims as u16, Dtype::F32, bytes)?;
}
// Build HNSW index for fast ANN search
db.build_vector_index(ns, dims as u16, Dtype::F32, Metric::Cosine)?;
// Search top-5 nearest neighbors
let query: Vec<f32> = (0..dims).map(|_| random::<f32>()).collect();
let q_bytes = unsafe {
std::slice::from_raw_parts(
query.as_ptr() as *const u8,
query.len() * std::mem::size_of::<f32>()
)
};
let results = db.vector_search(ns, q_bytes, 5, Metric::Cosine)?;
for (i, r) in results.iter().enumerate() {
println!("#{} {} distance={:.4}",
i + 1, std::str::from_utf8(&r.key)?, r.distance);
}
Ok(())
}
Run: cargo run --example vector_search
Index documents, search with BM25 relevance, and filter by facets.
let mut db = Engine::open(EdgestoreConfig::new("/tmp/text_demo"))?;
let ns = b"articles";
// Index some documents
db.index_text(ns, b"doc1", "EdgeStore is a fast embedded database")?;
db.index_text(ns, b"doc2", "RocksDB is an LSM tree database")?;
db.index_text(ns, b"doc3", "SQLite is a lightweight SQL database")?;
// Search for "fast database"
let hits = db.search_text(ns, "fast database", 5)?;
for hit in hits {
println!("{} score={:.3}",
std::str::from_utf8(&hit.key)?, hit.score);
}
// Output: doc1 score=2.341 (matches both "fast" and "database")
// Typo-tolerant search
let hits = db.search_text(ns, "databse", 5)?;
// Still finds "database" documents via Levenshtein ≤ 1
Batch multiple writes into a single atomic commit.
// Begin a transaction
let mut tx = db.begin();
// Add multiple operations
tx.put(b"accounts", b"alice", b"900", 0, 0)?;
tx.put(b"accounts", b"bob", b"1100", 0, 0)?;
tx.put(b"accounts", b"charlie", b"500", 0, 0)?;
// Commit atomically
db.commit_transaction(tx)?;
// Or roll back if something goes wrong
// db.rollback_transaction(tx);
// Verify all three are present
assert!(db.get(b"accounts", b"alice")?.is_some());
assert!(db.get(b"accounts", b"bob")?.is_some());
assert!(db.get(b"accounts", b"charlie")?.is_some());
Synchronize two engines using content-addressed segments and Merkle tree comparison.
// Setup primary and replica
let primary = Engine::open(EdgestoreConfig::new("/tmp/repl_primary"))?;
let mut replica = Engine::open(EdgestoreConfig::new("/tmp/repl_replica"))?;
// Write on primary, flush
primary.put(b"data", b"key1", b"value1")?;
primary.flush_to_segments()?;
// Export manifest from primary
let seg_refs = primary.export_manifest()?;
// Find missing segments on replica
let missing = replica.missing_segments(&seg_refs);
// Transfer each missing segment
for seg in &missing {
let data = primary.read_segment(seg)?;
let result = replica.import_segment(seg, &data)?;
println!("Imported: {:?}", result);
}
// Verify Merkle roots match
let primary_root = primary.range_merkle_root()?;
let match_ok = replica.compare_merkle(&primary_root)?;
assert!(match_ok);
Run: cargo run --example replication
The edgestore-cli binary provides administrative and data-access commands without writing code.
# Install the CLI
$ cargo install --path edgestore-cli
# Create a database
$ edgestore-cli create --path ./mydb
# Put and get values
$ edgestore-cli put --path ./mydb --key hello --value world
$ edgestore-cli get --path ./mydb --key hello
world
# Range scan
$ edgestore-cli range --path ./mydb --start a --end z
# Store a vector (128-dim f32, hex-encoded)
$ edgestore-cli vector-put --path ./mydb --namespace vec \
--key doc1 --dims 128 --dtype f32 --data "3f800000..."
# Search vectors
$ edgestore-cli vector-search --path ./mydb --namespace vec \
--query "3f800000..." --k 5 --metric cosine
# Compact expired data
$ edgestore-cli compact --path ./mydb
# Export to JSON
$ edgestore-cli export --path ./mydb --output backup.json
Every read has a _with_stats variant that returns a QueryStats struct alongside the result. Ideal for agent loops that need to track token- or byte-budgets per operation.
use edgestore::{EdgestoreConfig, Engine};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut db = Engine::open(EdgestoreConfig::new("/tmp/cost_demo"))?;
db.put(b"docs", b"doc1", b"hello world")?;
db.flush_to_segments()?;
// Point lookup with cost reporting
let (val, stats) = db.get_with_stats(b"docs", b"doc1")?;
println!("value={:?}", val);
println!("bytes={} segs={} items={}",
stats.bytes_scanned,
stats.segments_scanned,
stats.items_examined);
// Range scan with accounting
let (rows, stats) = db.range_with_stats(b"docs", b"a", b"z")?;
println!("{} rows, {} bytes", rows.len(), stats.bytes_scanned);
// Vector search with cost reporting
let (hits, stats) = db.vector_search_with_stats(
b"vec", &query_vec, 10, Metric::Cosine
)?;
println!("{} hits, {} bytes scanned", hits.len(), stats.bytes_scanned);
Ok(())
}
Run: cargo run --example cost_accounting
Stop a scan early when a byte or item budget is hit. Use next_key as the start of the next page.
use edgestore::ScanBudget;
let budget = ScanBudget { max_items: 100, max_bytes: 512 * 1024 };
let mut start = b"2026-01-01".to_vec();
let end = b"2026-12-31";
loop {
let page = db.range_budgeted(b"logs", &start, end, budget)?;
for (k, v) in &page.items {
process(k, v);
}
if !page.truncated {
break;
}
start = page.next_key;
}
// Same pattern works for prefix scans
let result = db.prefix_budgeted(b"events", b"click:", budget)?;
println!("got {} items, truncated={}", result.items.len(), result.truncated);
Return context windows around matched terms. Pass snippets to an LLM as evidence without sending the full document.
let mut db = Engine::open(EdgestoreConfig::new("/tmp/snippets_demo"))?;
let ns = b"articles";
db.index_text(ns, b"doc1",
"The quick brown fox jumps over the lazy dog near the river bank")?;
db.index_text(ns, b"doc2",
"A fox and a hound became unlikely friends in the forest")?;
let results = db.search_text_with_snippets(ns, "fox", 5)?;
for r in &results {
println!("doc={} score={:.3}", r.doc_id, r.score);
for s in &r.snippets {
println!(" matched={:?} context={:?}", s.span,
&original_text[s.context_start..s.context_end]);
}
}
// Output (approximate):
// doc=doc1 score=1.847
// matched="fox" context="quick brown fox jumps over"
// doc=doc2 score=1.521
// matched="fox" context="A fox and a hound became"
Run: cargo run --example search_snippets