init: reconcile with existing nodes, filter orphaned edges
init now detects content changes in markdown files and updates existing nodes (bumps version, appends to capnp log) instead of only creating new ones. Link resolution uses the redirect table so references to moved sections (e.g. from the reflections split) create edges to the correct target. On cache rebuild from capnp logs, filter out relations that reference deleted/missing nodes so the relation count matches the actual graph edge count.
This commit is contained in:
parent
2d6c8d5199
commit
1a01cbf8f8
1 changed files with 55 additions and 6 deletions
|
|
@ -306,6 +306,12 @@ impl Store {
|
||||||
store.replay_relations(&rels_p)?;
|
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
|
// Save cache
|
||||||
store.save()?;
|
store.save()?;
|
||||||
Ok(store)
|
Ok(store)
|
||||||
|
|
@ -526,6 +532,9 @@ impl Store {
|
||||||
let entries = fs::read_dir(dir)
|
let entries = fs::read_dir(dir)
|
||||||
.map_err(|e| format!("read dir {}: {}", dir.display(), e))?;
|
.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() {
|
for entry in entries.flatten() {
|
||||||
let path = entry.path();
|
let path = entry.path();
|
||||||
if path.is_dir() {
|
if path.is_dir() {
|
||||||
|
|
@ -541,6 +550,7 @@ impl Store {
|
||||||
|
|
||||||
let units = parse_units(&filename, &content);
|
let units = parse_units(&filename, &content);
|
||||||
let mut new_nodes = Vec::new();
|
let mut new_nodes = Vec::new();
|
||||||
|
let mut updated_nodes = Vec::new();
|
||||||
let mut new_relations = Vec::new();
|
let mut new_relations = Vec::new();
|
||||||
|
|
||||||
// Determine node type from filename
|
// Determine node type from filename
|
||||||
|
|
@ -555,7 +565,23 @@ impl Store {
|
||||||
};
|
};
|
||||||
|
|
||||||
for unit in &units {
|
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);
|
let mut node = Store::new_node(&unit.key, &unit.content);
|
||||||
node.node_type = node_type;
|
node.node_type = node_type;
|
||||||
if let Some(ref state) = unit.state {
|
if let Some(ref state) = unit.state {
|
||||||
|
|
@ -578,7 +604,15 @@ impl Store {
|
||||||
count += new_nodes.len();
|
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 {
|
for unit in &units {
|
||||||
let source_uuid = match self.nodes.get(&unit.key) {
|
let source_uuid = match self.nodes.get(&unit.key) {
|
||||||
Some(n) => n.uuid,
|
Some(n) => n.uuid,
|
||||||
|
|
@ -586,7 +620,15 @@ impl Store {
|
||||||
};
|
};
|
||||||
|
|
||||||
for link in unit.marker_links.iter().chain(unit.md_links.iter()) {
|
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,
|
Some(n) => n.uuid,
|
||||||
None => continue,
|
None => continue,
|
||||||
};
|
};
|
||||||
|
|
@ -598,14 +640,21 @@ impl Store {
|
||||||
let rel = Store::new_relation(
|
let rel = Store::new_relation(
|
||||||
source_uuid, target_uuid,
|
source_uuid, target_uuid,
|
||||||
RelationType::Link, 1.0,
|
RelationType::Link, 1.0,
|
||||||
&unit.key, link,
|
&unit.key, &resolved_link,
|
||||||
);
|
);
|
||||||
new_relations.push(rel);
|
new_relations.push(rel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for cause in &unit.causes {
|
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,
|
Some(n) => n.uuid,
|
||||||
None => continue,
|
None => continue,
|
||||||
};
|
};
|
||||||
|
|
@ -616,7 +665,7 @@ impl Store {
|
||||||
let rel = Store::new_relation(
|
let rel = Store::new_relation(
|
||||||
target_uuid, source_uuid,
|
target_uuid, source_uuid,
|
||||||
RelationType::Causal, 1.0,
|
RelationType::Causal, 1.0,
|
||||||
cause, &unit.key,
|
&resolved_cause, &unit.key,
|
||||||
);
|
);
|
||||||
new_relations.push(rel);
|
new_relations.push(rel);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue