Examples

Runnable code snippets for every major feature. Copy-paste into your project or run the included examples with cargo run --example <name>.

Basic KV Operations

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

TTL and Compaction

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

Vector Search (Flat + HNSW)

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

Full-Text 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

Atomic Transactions

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());

Merkle Replication

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

Command-Line Interface

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

Cost-Aware Reads

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

Bounded Scans with Pagination

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);

Search Snippets for RAG

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