mirror of
https://github.com/thegeneralist01/archivr
synced 2026-10-09 21:03:17 +02:00
Merge branch 'summary-search' into integration-all-three
This commit is contained in:
commit
55f96c66e9
1 changed files with 194 additions and 1 deletions
|
|
@ -773,7 +773,14 @@ pub fn search_entries(
|
||||||
sql.push_str(&format!(
|
sql.push_str(&format!(
|
||||||
" AND (LOWER(e.title) LIKE ?{n} OR LOWER(si.canonical_url) LIKE ?{n} \
|
" AND (LOWER(e.title) LIKE ?{n} OR LOWER(si.canonical_url) LIKE ?{n} \
|
||||||
OR LOWER(e.entry_uid) LIKE ?{n} OR LOWER(e.source_kind) LIKE ?{n} \
|
OR LOWER(e.entry_uid) LIKE ?{n} OR LOWER(e.source_kind) LIKE ?{n} \
|
||||||
OR LOWER(e.entity_kind) LIKE ?{n} OR LOWER(e.visibility) LIKE ?{n})"
|
OR LOWER(e.entity_kind) LIKE ?{n} OR LOWER(e.visibility) LIKE ?{n} \
|
||||||
|
OR LOWER(COALESCE((\
|
||||||
|
SELECT s.summary_text FROM entry_summaries s \
|
||||||
|
WHERE s.entry_id = e.id AND s.status = 'completed' \
|
||||||
|
AND s.summary_text IS NOT NULL \
|
||||||
|
ORDER BY s.completed_at DESC, s.updated_at DESC, s.id DESC \
|
||||||
|
LIMIT 1\
|
||||||
|
), '')) LIKE ?{n})"
|
||||||
));
|
));
|
||||||
params.push(term);
|
params.push(term);
|
||||||
}
|
}
|
||||||
|
|
@ -1497,6 +1504,192 @@ mod tests {
|
||||||
assert_eq!(results.len(), 1);
|
assert_eq!(results.len(), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn entry_id_by_title(conn: &rusqlite::Connection, title: &str) -> i64 {
|
||||||
|
conn.query_row(
|
||||||
|
"SELECT id FROM archived_entries WHERE title = ?1",
|
||||||
|
[title],
|
||||||
|
|row| row.get(0),
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn complete_summary(
|
||||||
|
conn: &rusqlite::Connection,
|
||||||
|
entry_id: i64,
|
||||||
|
cache_key: &str,
|
||||||
|
summary_text: &str,
|
||||||
|
) -> String {
|
||||||
|
let uid = database::upsert_pending_entry_summary(
|
||||||
|
conn,
|
||||||
|
entry_id,
|
||||||
|
"test_provider",
|
||||||
|
None,
|
||||||
|
"v1",
|
||||||
|
cache_key,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
database::update_entry_summary_status(conn, &uid, "completed", Some(summary_text), None)
|
||||||
|
.unwrap();
|
||||||
|
uid
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_summary_timestamps(
|
||||||
|
conn: &rusqlite::Connection,
|
||||||
|
summary_uid: &str,
|
||||||
|
timestamp: &str,
|
||||||
|
) {
|
||||||
|
conn.execute(
|
||||||
|
"UPDATE entry_summaries SET completed_at = ?1, updated_at = ?1 WHERE summary_uid = ?2",
|
||||||
|
rusqlite::params![timestamp, summary_uid],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn search_summary_json_tags_match_and_unrelated_text_is_absent() {
|
||||||
|
let conn = make_test_db_with_entries();
|
||||||
|
let entry_id = entry_id_by_title(&conn, "Resume Templates");
|
||||||
|
complete_summary(
|
||||||
|
&conn,
|
||||||
|
entry_id,
|
||||||
|
"tags",
|
||||||
|
r#"{"tags":["skincare","dermatology"]}"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
let matches = search_entries(
|
||||||
|
&conn,
|
||||||
|
&SearchEntriesQuery {
|
||||||
|
q: Some("skincare".to_string()),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(matches.len(), 1);
|
||||||
|
assert_eq!(matches[0].title.as_deref(), Some("Resume Templates"));
|
||||||
|
|
||||||
|
let unrelated = search_entries(
|
||||||
|
&conn,
|
||||||
|
&SearchEntriesQuery {
|
||||||
|
q: Some("neurology".to_string()),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert!(unrelated.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn search_summary_uses_only_the_newest_completed_row() {
|
||||||
|
let conn = make_test_db_with_entries();
|
||||||
|
let entry_id = entry_id_by_title(&conn, "Resume Templates");
|
||||||
|
let older = complete_summary(&conn, entry_id, "older", "legacy-skincare-term");
|
||||||
|
set_summary_timestamps(&conn, &older, "2026-01-01T00:00:00Z");
|
||||||
|
let newer = complete_summary(&conn, entry_id, "newer", "current-dermatology-term");
|
||||||
|
set_summary_timestamps(&conn, &newer, "2026-02-01T00:00:00Z");
|
||||||
|
|
||||||
|
let old_matches = search_entries(
|
||||||
|
&conn,
|
||||||
|
&SearchEntriesQuery {
|
||||||
|
q: Some("legacy-skincare-term".to_string()),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert!(old_matches.is_empty());
|
||||||
|
|
||||||
|
let current_matches = search_entries(
|
||||||
|
&conn,
|
||||||
|
&SearchEntriesQuery {
|
||||||
|
q: Some("current-dermatology-term".to_string()),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(current_matches.len(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn search_summary_keeps_latest_completed_when_newer_rows_are_pending_or_failed() {
|
||||||
|
let conn = make_test_db_with_entries();
|
||||||
|
let entry_id = entry_id_by_title(&conn, "Resume Templates");
|
||||||
|
let completed = complete_summary(&conn, entry_id, "completed", "retained-skincare-term");
|
||||||
|
set_summary_timestamps(&conn, &completed, "2026-01-01T00:00:00Z");
|
||||||
|
|
||||||
|
let pending = database::upsert_pending_entry_summary(
|
||||||
|
&conn,
|
||||||
|
entry_id,
|
||||||
|
"test_provider",
|
||||||
|
None,
|
||||||
|
"v1",
|
||||||
|
"pending",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
set_summary_timestamps(&conn, &pending, "2026-03-01T00:00:00Z");
|
||||||
|
let failed = database::upsert_pending_entry_summary(
|
||||||
|
&conn,
|
||||||
|
entry_id,
|
||||||
|
"test_provider",
|
||||||
|
None,
|
||||||
|
"v1",
|
||||||
|
"failed",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
database::update_entry_summary_status(&conn, &failed, "failed", None, Some("boom")).unwrap();
|
||||||
|
set_summary_timestamps(&conn, &failed, "2026-04-01T00:00:00Z");
|
||||||
|
|
||||||
|
let matches = search_entries(
|
||||||
|
&conn,
|
||||||
|
&SearchEntriesQuery {
|
||||||
|
q: Some("retained-skincare-term".to_string()),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(matches.len(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn search_summary_preserves_prefix_collection_and_visibility_scope() {
|
||||||
|
let conn = make_test_db_with_entries();
|
||||||
|
let entry_id = entry_id_by_title(&conn, "Polymarket tweet");
|
||||||
|
complete_summary(&conn, entry_id, "scoped", "scoped-skincare-term");
|
||||||
|
let tag = create_tag(&conn, "/summary-scope").unwrap();
|
||||||
|
database::assign_entry_to_tag(
|
||||||
|
&conn,
|
||||||
|
entry_id,
|
||||||
|
database::get_tag_by_uid(&conn, &tag.tag_uid).unwrap().unwrap().id,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let collection = database::create_collection(&conn, "Summary scope", "summary-scope", 2, false)
|
||||||
|
.unwrap();
|
||||||
|
database::add_entry_to_collection(&conn, collection.id, entry_id, 2).unwrap();
|
||||||
|
|
||||||
|
let query = SearchEntriesQuery {
|
||||||
|
q: Some("scoped-skincare-term".to_string()),
|
||||||
|
source_kind: Some("x".to_string()),
|
||||||
|
entity_kind: Some("tweet".to_string()),
|
||||||
|
url: Some("x.com".to_string()),
|
||||||
|
title: Some("polymarket".to_string()),
|
||||||
|
after: Some("2020-01-01T00:00:00Z".to_string()),
|
||||||
|
before: Some("9999-01-01T00:00:00Z".to_string()),
|
||||||
|
tag: Some("/summary-scope".to_string()),
|
||||||
|
caller_bits: 1,
|
||||||
|
collection_id: Some(collection.id),
|
||||||
|
};
|
||||||
|
assert!(search_entries(&conn, &query).unwrap().is_empty());
|
||||||
|
|
||||||
|
let matches = search_entries(
|
||||||
|
&conn,
|
||||||
|
&SearchEntriesQuery {
|
||||||
|
caller_bits: 2,
|
||||||
|
..query
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(matches.len(), 1);
|
||||||
|
assert_eq!(matches[0].title.as_deref(), Some("Polymarket tweet"));
|
||||||
|
}
|
||||||
|
|
||||||
// ---- tag API tests ----
|
// ---- tag API tests ----
|
||||||
|
|
||||||
fn make_tag_test_db() -> (rusqlite::Connection, i64, i64) {
|
fn make_tag_test_db() -> (rusqlite::Connection, i64, i64) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue