consciousness/src/hippocampus/store/index.rs

105 lines
3.1 KiB
Rust
Raw Normal View History

// redb index tables
//
// capnp logs are source of truth; redb provides indexed access.
// Tables:
// nodes: key → offset in capnp log (u64)
// uuid_to_key: [u8;16] → key
//
// To read a node: lookup offset in redb, seek in capnp file, deserialize.
use anyhow::{Context, Result};
use redb::{Database, ReadableDatabase, ReadableTable, ReadableTableMetadata, TableDefinition};
use std::path::Path;
// Table definitions - nodes maps key to byte offset in capnp log
pub const NODES: TableDefinition<&str, u64> = TableDefinition::new("nodes");
pub const UUID_TO_KEY: TableDefinition<&[u8], &str> = TableDefinition::new("uuid_to_key");
/// Open or create the redb database, ensuring all tables exist.
pub fn open_db(path: &Path) -> Result<Database> {
let db = Database::create(path)
.with_context(|| format!("create redb {}", path.display()))?;
// Ensure tables exist by opening a write transaction
let txn = db.begin_write()?;
{
let _ = txn.open_table(NODES)?;
let _ = txn.open_table(UUID_TO_KEY)?;
}
txn.commit()?;
Ok(db)
}
/// Record a node's location in the index.
pub fn index_node(db: &Database, key: &str, offset: u64, uuid: &[u8; 16]) -> Result<()> {
let txn = db.begin_write()?;
{
let mut nodes_table = txn.open_table(NODES)?;
let mut uuid_table = txn.open_table(UUID_TO_KEY)?;
nodes_table.insert(key, offset)?;
uuid_table.insert(uuid.as_slice(), key)?;
}
txn.commit()?;
Ok(())
}
/// Get offset for a node by key.
pub fn get_offset(db: &Database, key: &str) -> Result<Option<u64>> {
let txn = db.begin_read()?;
let table = txn.open_table(NODES)?;
Ok(table.get(key)?.map(|v| v.value()))
}
/// Check if a key exists in the index.
pub fn contains_key(db: &Database, key: &str) -> Result<bool> {
let txn = db.begin_read()?;
let table = txn.open_table(NODES)?;
Ok(table.get(key)?.is_some())
}
/// Get key by uuid from redb.
pub fn get_key_by_uuid(db: &Database, uuid: &[u8; 16]) -> Result<Option<String>> {
let txn = db.begin_read()?;
let table = txn.open_table(UUID_TO_KEY)?;
match table.get(uuid.as_slice())? {
Some(key) => Ok(Some(key.value().to_string())),
None => Ok(None),
}
}
/// Remove a node from the index.
pub fn remove_node(db: &Database, key: &str, uuid: &[u8; 16]) -> Result<()> {
let txn = db.begin_write()?;
{
let mut nodes_table = txn.open_table(NODES)?;
let mut uuid_table = txn.open_table(UUID_TO_KEY)?;
nodes_table.remove(key)?;
uuid_table.remove(uuid.as_slice())?;
}
txn.commit()?;
Ok(())
}
/// Count nodes in the index.
pub fn node_count(db: &Database) -> Result<u64> {
let txn = db.begin_read()?;
let table = txn.open_table(NODES)?;
Ok(table.len()?)
}
/// Collect all keys from the index.
pub fn all_keys(db: &Database) -> Result<Vec<String>> {
let txn = db.begin_read()?;
let table = txn.open_table(NODES)?;
let mut keys = Vec::new();
for entry in table.iter()? {
let (key, _) = entry?;
keys.push(key.value().to_string());
}
Ok(keys)
}