journal tail: use query engine instead of manual filtering

Co-Authored-By: Proof of Concept <poc@bcachefs.org>
This commit is contained in:
Kent Overstreet 2026-04-12 02:29:52 -04:00
parent 919749dc67
commit 93fcc32a00

View file

@ -90,48 +90,21 @@ pub fn find_current_transcript() -> Option<String> {
newest.map(|(_, p)| p.to_string_lossy().to_string())
}
fn journal_tail_entries(store: &crate::store::Store, n: usize, full: bool) -> Result<(), String> {
let date_re = regex::Regex::new(r"(\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2})").unwrap();
let key_date_re = regex::Regex::new(r"j-(\d{4}-\d{2}-\d{2}[t-]\d{2}-\d{2})").unwrap();
fn journal_tail_query(store: &crate::store::Store, query: &str, n: usize, full: bool) -> Result<(), String> {
let graph = store.build_graph();
let stages = crate::query_parser::parse_stages(query)?;
let results = crate::search::run_query(&stages, vec![], &graph, store, false, n);
let normalize_date = |s: &str| -> String {
let s = s.replace('t', "T");
if s.len() >= 16 {
format!("{}T{}", &s[..10], s[11..].replace('-', ":"))
// Query sorts desc and limits, so reverse to show oldest-to-newest
for (key, _score) in results.into_iter().rev() {
let Some(node) = store.nodes.get(&key) else { continue };
let ts = if node.created_at > 0 {
crate::store::format_datetime(node.created_at)
} else if node.timestamp > 0 {
crate::store::format_datetime(node.timestamp)
} else {
s
}
node.key.clone()
};
let extract_sort = |node: &crate::store::Node| -> (i64, String) {
if node.created_at > 0 {
return (node.created_at, crate::store::format_datetime(node.created_at));
}
if let Some(caps) = key_date_re.captures(&node.key) {
return (0, normalize_date(&caps[1]));
}
if let Some(caps) = date_re.captures(&node.content) {
return (0, normalize_date(&caps[1]));
}
(node.timestamp, crate::store::format_datetime(node.timestamp))
};
let mut journal: Vec<_> = store.nodes.values()
.filter(|node| node.node_type == crate::store::NodeType::EpisodicSession)
.collect();
journal.sort_by(|a, b| {
let (at, as_) = extract_sort(a);
let (bt, bs) = extract_sort(b);
if at > 0 && bt > 0 {
at.cmp(&bt)
} else {
as_.cmp(&bs)
}
});
let skip = if journal.len() > n { journal.len() - n } else { 0 };
for node in journal.iter().skip(skip) {
let (_, ts) = extract_sort(node);
let title = extract_title(&node.content);
if full {
println!("--- [{}] {} ---\n{}\n", ts, title, node.content);
@ -142,44 +115,14 @@ fn journal_tail_entries(store: &crate::store::Store, n: usize, full: bool) -> Re
Ok(())
}
fn journal_tail_digests(store: &crate::store::Store, node_type: crate::store::NodeType, n: usize, full: bool) -> Result<(), String> {
let mut digests: Vec<_> = store.nodes.values()
.filter(|node| node.node_type == node_type)
.collect();
digests.sort_by(|a, b| {
if a.timestamp > 0 && b.timestamp > 0 {
a.timestamp.cmp(&b.timestamp)
} else {
a.key.cmp(&b.key)
}
});
let skip = if digests.len() > n { digests.len() - n } else { 0 };
for node in digests.iter().skip(skip) {
let label = &node.key;
let title = extract_title(&node.content);
if full {
println!("--- [{}] {} ---\n{}\n", label, title, node.content);
} else {
println!("[{}] {}", label, title);
}
}
Ok(())
}
pub fn cmd_journal_tail(n: usize, full: bool, level: u8) -> Result<(), String> {
let store = crate::store::Store::load()?;
if level == 0 {
journal_tail_entries(&store, n, full)
} else {
let node_type = match level {
1 => crate::store::NodeType::EpisodicDaily,
2 => crate::store::NodeType::EpisodicWeekly,
_ => crate::store::NodeType::EpisodicMonthly,
};
journal_tail_digests(&store, node_type, n, full)
}
let query = format!("all | type:{} | sort:timestamp | limit:{}",
match level { 0 => "episodic", 1 => "daily", 2 => "weekly", _ => "monthly" },
n
);
journal_tail_query(&store, &query, n, full)
}
pub fn cmd_journal_write(name: &str, text: &[String]) -> Result<(), String> {