store: wire up RELS index for relations

Complete redb schema with bidirectional relation indexing:
- RELS multimap: uuid → packed(other_uuid, strength, rel_type, is_outgoing)
- Each edge stored twice (once per endpoint) with direction bit
- pack_rel/unpack_rel for 22-byte packed format

Wired up:
- replay_relations indexes all relations on load
- add_relation indexes new relations
- for_each_relation reads from index (graph building)
- add_link uses index for existence check
- set_link_strength finds/updates edges via index
- cap_degree uses index for degree counting and pruning
- rename_node finds edges by uuid

Vec<Relation> still maintained for remaining uses (normalize_strengths,
graph_health diagnostics). To be removed in follow-up.

Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
This commit is contained in:
Kent Overstreet 2026-04-13 21:12:47 -04:00
commit 5fe51fbfda
5 changed files with 365 additions and 136 deletions

View file

@ -59,9 +59,42 @@ impl StoreView for Store {
}
fn for_each_relation<F: FnMut(&str, &str, f32, RelationType)>(&self, mut f: F) {
for rel in &self.relations {
if rel.deleted { continue; }
f(&rel.source_key, &rel.target_key, rel.strength, rel.rel_type);
let db = match self.db.as_ref() {
Some(db) => db,
None => return,
};
// Build uuid → key map by iterating all nodes once
let mut uuid_to_key: std::collections::HashMap<[u8; 16], String> = std::collections::HashMap::new();
let keys = match index::all_keys(db) {
Ok(keys) => keys,
Err(_) => return,
};
for key in &keys {
if let Ok(Some(uuid)) = index::get_uuid_for_key(db, key) {
uuid_to_key.insert(uuid, key.clone());
}
}
// Iterate edges: only process outgoing to avoid duplicates
for key in &keys {
let uuid = match index::get_uuid_for_key(db, key) {
Ok(Some(u)) => u,
_ => continue,
};
let edges = match index::edges_for_node(db, &uuid) {
Ok(e) => e,
Err(_) => continue,
};
for (other_uuid, strength, rel_type_byte, is_outgoing) in edges {
if !is_outgoing { continue; } // only process outgoing
let target_key = match uuid_to_key.get(&other_uuid) {
Some(k) => k,
None => continue, // orphan edge
};
let rel_type = RelationType::from_u8(rel_type_byte);
f(key, target_key, strength, rel_type);
}
}
}