query: rich QueryResult + toolkit cleanup
QueryResult carries a fields map (BTreeMap<String, Value>) so callers don't re-resolve fields after queries run. Neighbors queries inject edge context (strength, rel_type) at construction time. New public API: - run_query(): parse + execute + format in one call - format_value(): format a Value for display - execute_parsed(): internal, avoids double-parse in run_query Removed: output_stages(), format_field() Simplified commands: - cmd_query, cmd_graph, cmd_link, cmd_list_keys all delegate to run_query - cmd_experience_mine uses existing find_current_transcript() Deduplication: - now_epoch() 3 copies → 1 (capnp_store's public fn) - hub_threshold → Graph::hub_threshold() method - eval_node + eval_edge → single eval() with closure for field resolution - compare() collapsed via Ordering (35 → 15 lines) Modernization: - 12 sites of partial_cmp().unwrap_or(Ordering::Equal) → total_cmp()
This commit is contained in:
parent
64d2b441f0
commit
fa7fe8c14b
7 changed files with 187 additions and 264 deletions
91
src/main.rs
91
src/main.rs
|
|
@ -318,16 +318,9 @@ fn cmd_status() -> Result<(), String> {
|
|||
fn cmd_graph() -> Result<(), String> {
|
||||
let store = capnp_store::Store::load()?;
|
||||
let g = store.build_graph();
|
||||
|
||||
println!("Top nodes by degree:");
|
||||
let results = query::execute_query(
|
||||
&store, &g, "* | sort degree | limit 10")?;
|
||||
for r in &results {
|
||||
let deg = g.degree(&r.key);
|
||||
let cc = g.clustering_coefficient(&r.key);
|
||||
println!(" {:40} deg={:3} cc={:.3}", r.key, deg, cc);
|
||||
}
|
||||
Ok(())
|
||||
query::run_query(&store, &g,
|
||||
"* | sort degree | limit 10 | select degree,clustering_coefficient")
|
||||
}
|
||||
|
||||
fn cmd_used(args: &[String]) -> Result<(), String> {
|
||||
|
|
@ -486,14 +479,9 @@ fn cmd_link(args: &[String]) -> Result<(), String> {
|
|||
let store = capnp_store::Store::load()?;
|
||||
let resolved = store.resolve_key(&key)?;
|
||||
let g = store.build_graph();
|
||||
|
||||
println!("Neighbors of '{}':", resolved);
|
||||
let neighbors = g.neighbors(&resolved);
|
||||
for (i, (n, strength)) in neighbors.iter().enumerate() {
|
||||
let cc = g.clustering_coefficient(n);
|
||||
println!(" {:2}. [{:.2}] {} (cc={:.3})", i + 1, strength, n, cc);
|
||||
}
|
||||
Ok(())
|
||||
query::run_query(&store, &g,
|
||||
&format!("neighbors('{}') | select strength,clustering_coefficient", resolved))
|
||||
}
|
||||
|
||||
fn cmd_replay_queue(args: &[String]) -> Result<(), String> {
|
||||
|
|
@ -843,29 +831,7 @@ fn cmd_experience_mine(args: &[String]) -> Result<(), String> {
|
|||
let jsonl_path = if let Some(path) = args.first() {
|
||||
path.clone()
|
||||
} else {
|
||||
// Find the most recent JSONL transcript
|
||||
let projects_dir = std::path::Path::new(&std::env::var("HOME").unwrap_or_default())
|
||||
.join(".claude/projects");
|
||||
let mut entries: Vec<(std::time::SystemTime, std::path::PathBuf)> = Vec::new();
|
||||
if let Ok(dirs) = std::fs::read_dir(&projects_dir) {
|
||||
for dir in dirs.flatten() {
|
||||
if let Ok(files) = std::fs::read_dir(dir.path()) {
|
||||
for file in files.flatten() {
|
||||
let path = file.path();
|
||||
if path.extension().map_or(false, |ext| ext == "jsonl") {
|
||||
if let Ok(meta) = file.metadata() {
|
||||
if let Ok(mtime) = meta.modified() {
|
||||
entries.push((mtime, path));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
entries.sort_by(|a, b| b.0.cmp(&a.0));
|
||||
entries.first()
|
||||
.map(|(_, p)| p.to_string_lossy().to_string())
|
||||
find_current_transcript()
|
||||
.ok_or("no JSONL transcripts found")?
|
||||
};
|
||||
|
||||
|
|
@ -1222,11 +1188,7 @@ fn cmd_spectral_suggest(args: &[String]) -> Result<(), String> {
|
|||
fn cmd_list_keys() -> Result<(), String> {
|
||||
let store = capnp_store::Store::load()?;
|
||||
let g = store.build_graph();
|
||||
let results = query::execute_query(&store, &g, "* | sort key asc")?;
|
||||
for r in &results {
|
||||
println!("{}", r.key);
|
||||
}
|
||||
Ok(())
|
||||
query::run_query(&store, &g, "* | sort key asc")
|
||||
}
|
||||
|
||||
fn cmd_list_edges() -> Result<(), String> {
|
||||
|
|
@ -1637,44 +1599,5 @@ Pipe stages:\n \
|
|||
let query_str = args.join(" ");
|
||||
let store = capnp_store::Store::load()?;
|
||||
let graph = store.build_graph();
|
||||
|
||||
let stages = query::output_stages(&query_str)?;
|
||||
let results = query::execute_query(&store, &graph, &query_str)?;
|
||||
|
||||
// Check for count stage
|
||||
if stages.iter().any(|s| matches!(s, query::Stage::Count)) {
|
||||
println!("{}", results.len());
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if results.is_empty() {
|
||||
eprintln!("No results");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Check for select stage
|
||||
let fields: Option<&Vec<String>> = stages.iter().find_map(|s| match s {
|
||||
query::Stage::Select(f) => Some(f),
|
||||
_ => None,
|
||||
});
|
||||
|
||||
if let Some(fields) = fields {
|
||||
let mut header = vec!["key".to_string()];
|
||||
header.extend(fields.iter().cloned());
|
||||
println!("{}", header.join("\t"));
|
||||
|
||||
for r in &results {
|
||||
let mut row = vec![r.key.clone()];
|
||||
for f in fields {
|
||||
row.push(query::format_field(f, &r.key, &store, &graph));
|
||||
}
|
||||
println!("{}", row.join("\t"));
|
||||
}
|
||||
} else {
|
||||
for r in &results {
|
||||
println!("{}", r.key);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
query::run_query(&store, &graph, &query_str)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue