import 'package:weblibre/features/bangs/data/database/drift/converters/bang_format.dart'; import 'package:weblibre/features/bangs/data/database/drift/converters/trigger_list.dart'; import 'package:weblibre/features/bangs/data/models/bang_data.dart'; import 'package:weblibre/features/bangs/data/models/bang_group.dart'; import 'package:weblibre/features/bangs/data/models/bang.dart'; import 'package:weblibre/features/bangs/data/models/search_history_entry.dart'; CREATE TABLE bang ( "trigger" TEXT NOT NULL, "group" ENUM(BangGroup) NOT NULL, website_name TEXT NOT NULL, domain TEXT NOT NULL, url_template TEXT NOT NULL, category TEXT, sub_category TEXT, format TEXT MAPPED BY `const BangFormatConverter()`, additional_triggers TEXT MAPPED BY `const TriggerListConverter()`, PRIMARY KEY ("trigger", "group") ) WITH Bang; CREATE TABLE bang_triggers ( "trigger" TEXT NOT NULL, "group" ENUM(BangGroup) NOT NULL, additional_trigger TEXT NOT NULL, PRIMARY KEY ("trigger", "group", additional_trigger), FOREIGN KEY ("trigger", "group") REFERENCES bang ("trigger", "group") ON DELETE CASCADE ); CREATE INDEX idx_bang_triggers_lookup ON bang_triggers (additional_trigger, "group"); -- Trigger to populate bang_triggers when inserting a new bang CREATE TRIGGER bang_triggers_after_insert AFTER INSERT ON bang WHEN new.additional_triggers IS NOT NULL BEGIN INSERT INTO bang_triggers("trigger", "group", additional_trigger) SELECT new."trigger", new."group", json_each.value FROM json_each(new.additional_triggers); END; -- Trigger to update bang_triggers when updating a bang CREATE TRIGGER bang_triggers_after_update AFTER UPDATE ON bang BEGIN -- Delete old additional triggers DELETE FROM bang_triggers WHERE "trigger" = old."trigger" AND "group" = old."group"; -- Insert new additional triggers if they exist INSERT INTO bang_triggers("trigger", "group", additional_trigger) SELECT new."trigger", new."group", json_each.value FROM json_each(new.additional_triggers) WHERE new.additional_triggers IS NOT NULL; END; CREATE TABLE bang_sync ( "group" ENUM(BangGroup) PRIMARY KEY NOT NULL, last_sync DATETIME NOT NULL ); CREATE TABLE bang_frequency ( "trigger" TEXT NOT NULL, "group" ENUM(BangGroup) NOT NULL, frequency INTEGER NOT NULL, last_used DATETIME NOT NULL, PRIMARY KEY ("trigger", "group"), FOREIGN KEY ("trigger", "group") REFERENCES bang ("trigger", "group") ON DELETE CASCADE ); CREATE TABLE bang_history ( search_query TEXT UNIQUE NOT NULL, "trigger" TEXT NOT NULL, "group" ENUM(BangGroup) NOT NULL, search_date DATETIME NOT NULL, FOREIGN KEY ("trigger", "group") REFERENCES bang ("trigger", "group") ); CREATE VIRTUAL TABLE bang_fts USING fts5( trigger, website_name, content=bang, prefix='2 3' ); CREATE VIRTUAL TABLE bang_triggers_fts USING fts5( additional_trigger, content=bang_triggers, prefix='2 3' ); CREATE VIEW bang_data_view WITH BangData AS SELECT b.*, bf.frequency, bf.last_used FROM bang b LEFT JOIN bang_frequency bf ON b."trigger" = bf."trigger" AND b."group" = bf."group"; -- Triggers to keep the FTS index up to date. CREATE TRIGGER bang_after_insert AFTER INSERT ON bang BEGIN INSERT INTO bang_fts(rowid, "trigger", website_name) VALUES (new.rowid, new."trigger", new.website_name); END; CREATE TRIGGER bang_after_delete AFTER DELETE ON bang BEGIN INSERT INTO bang_fts(bang_fts, rowid, "trigger", website_name) VALUES('delete', old.rowid, old."trigger", old.website_name); END; CREATE TRIGGER bang_after_update AFTER UPDATE ON bang BEGIN INSERT INTO bang_fts(bang_fts, rowid, "trigger", website_name) VALUES('delete', old.rowid, old."trigger", old.website_name); INSERT INTO bang_fts(rowid, "trigger", website_name) VALUES (new.rowid, new."trigger", new.website_name); END; CREATE TRIGGER bang_triggers_after_insert_fts AFTER INSERT ON bang_triggers BEGIN INSERT INTO bang_triggers_fts(rowid, additional_trigger) VALUES (new.rowid, new.additional_trigger); END; CREATE TRIGGER bang_triggers_after_delete_fts AFTER DELETE ON bang_triggers BEGIN INSERT INTO bang_triggers_fts(bang_triggers_fts, rowid, additional_trigger) VALUES('delete', old.rowid, old.additional_trigger); END; CREATE TRIGGER bang_triggers_after_update_fts AFTER UPDATE ON bang_triggers BEGIN INSERT INTO bang_triggers_fts(bang_triggers_fts, rowid, additional_trigger) VALUES('delete', old.rowid, old.additional_trigger); INSERT INTO bang_triggers_fts(rowid, additional_trigger) VALUES (new.rowid, new.additional_trigger); END; optimizeBangFtsIndex: INSERT INTO bang_fts(bang_fts) VALUES ('optimize'); optimizeTriggerFtsIndex: INSERT INTO bang_triggers_fts(bang_triggers_fts) VALUES ('optimize'); queryBangs WITH BangData: WITH weights AS ( SELECT 10.0 AS "trigger", 8.0 AS additional_trigger, 5.0 AS website_name ), bang_results AS ( SELECT b.*, bf.frequency, bf.last_used, bm25(bang_fts, weights."trigger", weights.website_name) AS weighted_rank FROM bang_fts(:query) fts INNER JOIN bang b ON b.rowid = fts.rowid LEFT JOIN bang_frequency bf ON b."trigger" = bf."trigger" AND b."group" = bf."group" CROSS JOIN weights ), trigger_results AS ( SELECT b.*, bf.frequency, bf.last_used, bm25(bang_triggers_fts, weights.additional_trigger) AS weighted_rank FROM bang_triggers_fts(:query) tfts INNER JOIN bang_triggers bt ON bt.rowid = tfts.rowid INNER JOIN bang b ON b."trigger" = bt."trigger" AND b."group" = bt."group" LEFT JOIN bang_frequency bf ON b."trigger" = bf."trigger" AND b."group" = bf."group" CROSS JOIN weights ), combined_results AS ( SELECT * FROM bang_results UNION ALL SELECT * FROM trigger_results ) SELECT *, MIN(weighted_rank) AS weighted_rank FROM combined_results GROUP BY "trigger", "group" ORDER BY weighted_rank ASC, frequency NULLS LAST; queryBangsBasic WITH BangData: WITH weights AS ( SELECT 10.0 AS "trigger", 8.0 AS additional_trigger, 5.0 AS website_name ), bang_results AS ( SELECT b.*, bf.frequency, bf.last_used, bm25(bang_fts, weights."trigger", weights.website_name) AS weighted_rank FROM bang_fts fts INNER JOIN bang b ON b.rowid = fts.rowid LEFT JOIN bang_frequency bf ON b."trigger" = bf."trigger" AND b."group" = bf."group" CROSS JOIN weights WHERE fts."trigger" LIKE :query OR fts.website_name LIKE :query ), trigger_results AS ( SELECT b.*, bf.frequency, bf.last_used, bm25(bang_triggers_fts, weights.additional_trigger) AS weighted_rank FROM bang_triggers_fts tfts INNER JOIN bang_triggers bt ON bt.rowid = tfts.rowid INNER JOIN bang b ON b."trigger" = bt."trigger" AND b."group" = bt."group" LEFT JOIN bang_frequency bf ON b."trigger" = bf."trigger" AND b."group" = bf."group" CROSS JOIN weights WHERE tfts.additional_trigger LIKE :query ), combined_results AS ( SELECT * FROM bang_results UNION ALL SELECT * FROM trigger_results ) SELECT *, MIN(weighted_rank) AS weighted_rank FROM combined_results GROUP BY "trigger", "group" ORDER BY weighted_rank ASC, frequency NULLS LAST; categoriesJson: WITH categories AS ( SELECT b.category, json_group_array( DISTINCT b.sub_category ORDER BY b.sub_category ) AS sub_categories FROM bang b WHERE b.category IS NOT NULL AND b.sub_category IS NOT NULL GROUP BY b.category ORDER BY b.category ) SELECT json_group_object( c.category, json(c.sub_categories) ) AS categories_json FROM categories c; searchHistoryEntries WITH SearchHistoryEntry: SELECT * FROM bang_history ORDER BY search_date DESC LIMIT :limit; evictHistoryEntries: DELETE FROM bang_history WHERE rowid IN ( SELECT rowid FROM bang_history ORDER BY search_date DESC LIMIT -1 OFFSET :limit );