/* * SearchHistoryStore.vala * * CRUD operations for search history. */ /** * SearchHistoryStore - Manages search history persistence */ public class RSSuper.SearchHistoryStore : Object { private Database db; /** * Maximum number of history entries to keep */ public int max_entries { get; set; default = 100; } /** * Signal emitted when a search is recorded */ public signal void search_recorded(SearchQuery query, int result_count); /** * Signal emitted when history is cleared */ public signal void history_cleared(); /** * Create a new search history store */ public SearchHistoryStore(Database db) { this.db = db; } /** * Record a search query */ public int record_search(SearchQuery query, int result_count = 0) throws Error { var stmt = db.prepare( "INSERT INTO search_history (query, filters_json, sort_option, page, page_size, result_count) " + "VALUES (?, ?, ?, ?, ?, ?);" ); stmt.bind_text(1, query.query, -1, null); stmt.bind_text(2, query.filters_json ?? "", -1, null); stmt.bind_text(3, SearchFilters.sort_option_to_string(query.sort), -1, null); stmt.bind_int(4, query.page); stmt.bind_int(5, query.page_size); stmt.bind_int(6, result_count); stmt.step(); debug("Search recorded: %s (%d results)", query.query, result_count); search_recorded(query, result_count); // Clean up old entries if needed cleanup_old_entries(); return 0; // Returns the last inserted row ID in SQLite } /** * Get search history */ public SearchQuery[] get_history(int limit = 50) throws Error { var queries = new GLib.List(); var stmt = db.prepare( "SELECT query, filters_json, sort_option, page, page_size, result_count, created_at " + "FROM search_history " + "ORDER BY created_at DESC " + "LIMIT ?;" ); stmt.bind_int(1, limit); while (stmt.step() == Sqlite.ROW) { var query = row_to_query(stmt); queries.append(query); } return queries_to_array(queries); } /** * Get recent searches (last 24 hours) */ public SearchQuery[] get_recent() throws Error { var queries = new GLib.List(); var now = new DateTime.now_local(); var yesterday = now.add_days(-1); var threshold = yesterday.format("%Y-%m-%dT%H:%M:%S"); var stmt = db.prepare( "SELECT query, filters_json, sort_option, page, page_size, result_count, created_at " + "FROM search_history " + "WHERE created_at >= ? " + "ORDER BY created_at DESC " + "LIMIT 20;" ); stmt.bind_text(1, threshold, -1, null); while (stmt.step() == Sqlite.ROW) { var query = row_to_query(stmt); queries.append(query); } return queries_to_array(queries); } /** * Delete a search history entry by ID */ public void delete(int id) throws Error { var stmt = db.prepare("DELETE FROM search_history WHERE id = ?;"); stmt.bind_int(1, id); stmt.step(); debug("Search history entry deleted: %d", id); } /** * Clear all search history */ public void clear() throws Error { var stmt = db.prepare("DELETE FROM search_history;"); stmt.step(); debug("Search history cleared"); history_cleared(); } /** * Clear old search history entries */ private void cleanup_old_entries() throws Error { var stmt = db.prepare( "DELETE FROM search_history WHERE id NOT IN (" + "SELECT id FROM search_history ORDER BY created_at DESC LIMIT ?" + ");" ); stmt.bind_int(1, max_entries); stmt.step(); } /** * Convert a database row to a SearchQuery */ private SearchQuery row_to_query(Sqlite.Statement stmt) { string query_str = stmt.column_text(0); string? filters_json = stmt.column_text(1); string sort_str = stmt.column_text(2); int page = stmt.column_int(3); int page_size = stmt.column_int(4); return SearchQuery(query_str, page, page_size, filters_json, SearchFilters.sort_option_from_string(sort_str)); } private SearchQuery[] queries_to_array(GLib.List list) { SearchQuery[] arr = {}; for (unowned var node = list; node != null; node = node.next) { arr += node.data; } return arr; } }