diff --git a/src/lib/Data.php b/src/lib/Data.php index 0d5328f..4bc8ac8 100644 --- a/src/lib/Data.php +++ b/src/lib/Data.php @@ -14,6 +14,31 @@ class Data { return $db; } + /** + * Create the search index and synchronization triggers for the item table + * + * @param SQLite3 $db The database connection on which these will be created + */ + public static function createSearchIndex(SQLite3 $db): void { + $db->exec("CREATE VIRTUAL TABLE item_search USING fts5(content, content='item', content_rowid='id')"); + $db->exec(<<<'SQL' + CREATE TRIGGER item_ai AFTER INSERT ON item BEGIN + INSERT INTO item_search (rowid, content) VALUES (new.id, new.content); + END; + SQL); + $db->exec(<<<'SQL' + CREATE TRIGGER item_au AFTER UPDATE ON item BEGIN + INSERT INTO item_search (item_search, rowid, content) VALUES ('delete', old.id, old.content); + INSERT INTO item_search (rowid, content) VALUES (new.id, new.content); + END; + SQL); + $db->exec(<<<'SQL' + CREATE TRIGGER item_ad AFTER DELETE ON item BEGIN + INSERT INTO item_search (item_search, rowid, content) VALUES ('delete', old.id, old.content); + END; + SQL); + } + /** * Make sure the expected tables exist */ @@ -23,17 +48,16 @@ class Data { $tableQuery = $db->query("SELECT name FROM sqlite_master WHERE type = 'table'"); while ($table = $tableQuery->fetchArray(SQLITE3_NUM)) $tables[] = $table[0]; if (!in_array('frc_user', $tables)) { - $query = <<<'SQL' + $db->exec(<<<'SQL' CREATE TABLE frc_user ( id INTEGER NOT NULL PRIMARY KEY, email TEXT NOT NULL, password TEXT NOT NULL) - SQL; - $db->exec($query); + SQL); $db->exec('CREATE INDEX idx_user_email ON frc_user (email)'); } if (!in_array('feed', $tables)) { - $query = <<<'SQL' + $db->exec(<<<'SQL' CREATE TABLE feed ( id INTEGER NOT NULL PRIMARY KEY, user_id INTEGER NOT NULL, @@ -42,11 +66,10 @@ class Data { updated_on TEXT, checked_on TEXT, FOREIGN KEY (user_id) REFERENCES frc_user (id)) - SQL; - $db->exec($query); + SQL); } if (!in_array('item', $tables)) { - $query = <<<'SQL' + $db->exec(<<<'SQL' CREATE TABLE item ( id INTEGER NOT NULL PRIMARY KEY, feed_id INTEGER NOT NULL, @@ -59,8 +82,8 @@ class Data { is_read BOOLEAN NOT NULL DEFAULT 0, is_bookmarked BOOLEAN NOT NULL DEFAULT 0, FOREIGN KEY (feed_id) REFERENCES feed (id)) - SQL; - $db->exec($query); + SQL); + self::createSearchIndex($db); } $db->close(); } diff --git a/src/lib/ItemList.php b/src/lib/ItemList.php index 18f71b0..4bb4c29 100644 --- a/src/lib/ItemList.php +++ b/src/lib/ItemList.php @@ -134,6 +134,22 @@ class ItemList { "/feed/items?id=$feedId&bookmarked"); } + /** + * Create an item list with items matching given search terms + * + * @param string $search The item search terms / query + * @param SQLite3 $db The database connection to use to obtain items + * @return static An item list match the given search terms + */ + public static function matchingSearch(string $search, SQLite3 $db): static { + $list = new static($db, + self::makeQuery($db, ['item.id IN (SELECT ROWID FROM item_search WHERE content MATCH :search)'], + [[':search', $search]]), 'Matching', "/search?search=$search"); + $list->showIndicators = true; + $list->displayFeed = true; + return $list; + } + /** * Render this item list */ diff --git a/src/public/search.php b/src/public/search.php new file mode 100644 index 0000000..04170c6 --- /dev/null +++ b/src/public/search.php @@ -0,0 +1,23 @@ + +