diff --git a/src/capnp_store.rs b/src/capnp_store.rs index 16ca063..d605cbb 100644 --- a/src/capnp_store.rs +++ b/src/capnp_store.rs @@ -306,6 +306,12 @@ impl Store { store.replay_relations(&rels_p)?; } + // Drop edges referencing deleted/missing nodes + store.relations.retain(|r| + store.nodes.contains_key(&r.source_key) && + store.nodes.contains_key(&r.target_key) + ); + // Save cache store.save()?; Ok(store) @@ -526,6 +532,9 @@ impl Store { let entries = fs::read_dir(dir) .map_err(|e| format!("read dir {}: {}", dir.display(), e))?; + // Track which keys we see in markdown so we can detect removed sections + let mut seen_keys = std::collections::HashSet::new(); + for entry in entries.flatten() { let path = entry.path(); if path.is_dir() { @@ -541,6 +550,7 @@ impl Store { let units = parse_units(&filename, &content); let mut new_nodes = Vec::new(); + let mut updated_nodes = Vec::new(); let mut new_relations = Vec::new(); // Determine node type from filename @@ -555,7 +565,23 @@ impl Store { }; for unit in &units { - if !self.nodes.contains_key(&unit.key) { + seen_keys.insert(unit.key.clone()); + + if let Some(existing) = self.nodes.get(&unit.key) { + // Update if content changed + if existing.content != unit.content { + let mut node = existing.clone(); + node.content = unit.content.clone(); + node.version += 1; + if let Some(ref state) = unit.state { + node.state_tag = state.clone(); + } + if let Some(ref src) = unit.source_ref { + node.source_ref = src.clone(); + } + updated_nodes.push(node); + } + } else { let mut node = Store::new_node(&unit.key, &unit.content); node.node_type = node_type; if let Some(ref state) = unit.state { @@ -578,7 +604,15 @@ impl Store { count += new_nodes.len(); } - // Create relations from links + // Batch append updated nodes + if !updated_nodes.is_empty() { + self.append_nodes(&updated_nodes)?; + for node in &updated_nodes { + self.nodes.insert(node.key.clone(), node.clone()); + } + } + + // Create relations from links, using resolve_redirect for targets for unit in &units { let source_uuid = match self.nodes.get(&unit.key) { Some(n) => n.uuid, @@ -586,7 +620,15 @@ impl Store { }; for link in unit.marker_links.iter().chain(unit.md_links.iter()) { - let target_uuid = match self.nodes.get(link) { + // Try direct lookup, then redirect + let resolved_link = if self.nodes.contains_key(link) { + link.clone() + } else if let Some(redirect) = self.resolve_redirect(link) { + redirect + } else { + continue; + }; + let target_uuid = match self.nodes.get(&resolved_link) { Some(n) => n.uuid, None => continue, }; @@ -598,14 +640,21 @@ impl Store { let rel = Store::new_relation( source_uuid, target_uuid, RelationType::Link, 1.0, - &unit.key, link, + &unit.key, &resolved_link, ); new_relations.push(rel); } } for cause in &unit.causes { - let target_uuid = match self.nodes.get(cause) { + let resolved_cause = if self.nodes.contains_key(cause) { + cause.clone() + } else if let Some(redirect) = self.resolve_redirect(cause) { + redirect + } else { + continue; + }; + let target_uuid = match self.nodes.get(&resolved_cause) { Some(n) => n.uuid, None => continue, }; @@ -616,7 +665,7 @@ impl Store { let rel = Store::new_relation( target_uuid, source_uuid, RelationType::Causal, 1.0, - cause, &unit.key, + &resolved_cause, &unit.key, ); new_relations.push(rel); }