Add search index (#15)
- Add utility rebuild script - Add Search to header - Add shell of search page - Add search query support to ItemList
This commit is contained in:
parent
210377b4da
commit
9d59bfb1c6
|
@ -14,6 +14,31 @@ class Data {
|
||||||
return $db;
|
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
|
* Make sure the expected tables exist
|
||||||
*/
|
*/
|
||||||
|
@ -23,17 +48,16 @@ class Data {
|
||||||
$tableQuery = $db->query("SELECT name FROM sqlite_master WHERE type = 'table'");
|
$tableQuery = $db->query("SELECT name FROM sqlite_master WHERE type = 'table'");
|
||||||
while ($table = $tableQuery->fetchArray(SQLITE3_NUM)) $tables[] = $table[0];
|
while ($table = $tableQuery->fetchArray(SQLITE3_NUM)) $tables[] = $table[0];
|
||||||
if (!in_array('frc_user', $tables)) {
|
if (!in_array('frc_user', $tables)) {
|
||||||
$query = <<<'SQL'
|
$db->exec(<<<'SQL'
|
||||||
CREATE TABLE frc_user (
|
CREATE TABLE frc_user (
|
||||||
id INTEGER NOT NULL PRIMARY KEY,
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
email TEXT NOT NULL,
|
email TEXT NOT NULL,
|
||||||
password TEXT NOT NULL)
|
password TEXT NOT NULL)
|
||||||
SQL;
|
SQL);
|
||||||
$db->exec($query);
|
|
||||||
$db->exec('CREATE INDEX idx_user_email ON frc_user (email)');
|
$db->exec('CREATE INDEX idx_user_email ON frc_user (email)');
|
||||||
}
|
}
|
||||||
if (!in_array('feed', $tables)) {
|
if (!in_array('feed', $tables)) {
|
||||||
$query = <<<'SQL'
|
$db->exec(<<<'SQL'
|
||||||
CREATE TABLE feed (
|
CREATE TABLE feed (
|
||||||
id INTEGER NOT NULL PRIMARY KEY,
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
user_id INTEGER NOT NULL,
|
user_id INTEGER NOT NULL,
|
||||||
|
@ -42,11 +66,10 @@ class Data {
|
||||||
updated_on TEXT,
|
updated_on TEXT,
|
||||||
checked_on TEXT,
|
checked_on TEXT,
|
||||||
FOREIGN KEY (user_id) REFERENCES frc_user (id))
|
FOREIGN KEY (user_id) REFERENCES frc_user (id))
|
||||||
SQL;
|
SQL);
|
||||||
$db->exec($query);
|
|
||||||
}
|
}
|
||||||
if (!in_array('item', $tables)) {
|
if (!in_array('item', $tables)) {
|
||||||
$query = <<<'SQL'
|
$db->exec(<<<'SQL'
|
||||||
CREATE TABLE item (
|
CREATE TABLE item (
|
||||||
id INTEGER NOT NULL PRIMARY KEY,
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
feed_id INTEGER NOT NULL,
|
feed_id INTEGER NOT NULL,
|
||||||
|
@ -59,8 +82,8 @@ class Data {
|
||||||
is_read BOOLEAN NOT NULL DEFAULT 0,
|
is_read BOOLEAN NOT NULL DEFAULT 0,
|
||||||
is_bookmarked BOOLEAN NOT NULL DEFAULT 0,
|
is_bookmarked BOOLEAN NOT NULL DEFAULT 0,
|
||||||
FOREIGN KEY (feed_id) REFERENCES feed (id))
|
FOREIGN KEY (feed_id) REFERENCES feed (id))
|
||||||
SQL;
|
SQL);
|
||||||
$db->exec($query);
|
self::createSearchIndex($db);
|
||||||
}
|
}
|
||||||
$db->close();
|
$db->close();
|
||||||
}
|
}
|
||||||
|
|
|
@ -134,6 +134,22 @@ class ItemList {
|
||||||
"/feed/items?id=$feedId&bookmarked");
|
"/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
|
* Render this item list
|
||||||
*/
|
*/
|
||||||
|
|
23
src/public/search.php
Normal file
23
src/public/search.php
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Item Search Page
|
||||||
|
*
|
||||||
|
* Search for items across all feeds
|
||||||
|
*/
|
||||||
|
|
||||||
|
include '../start.php';
|
||||||
|
|
||||||
|
$db = Data::getConnection();
|
||||||
|
Security::verifyUser($db);
|
||||||
|
|
||||||
|
if (key_exists('search', $_GET)) {
|
||||||
|
$list = ItemList::matchingSearch($_GET['search'], $db);
|
||||||
|
}
|
||||||
|
|
||||||
|
page_head('Item Search'); ?>
|
||||||
|
<h1>Item Search</h1>
|
||||||
|
// TODO: search form <?php
|
||||||
|
if (isset($list)) $list->render();
|
||||||
|
page_foot();
|
||||||
|
$db->close();
|
|
@ -67,7 +67,8 @@ function title_bar(): void {
|
||||||
$hasBookmarks = $bookResult && $bookResult->fetchArray(SQLITE3_NUM)[0];
|
$hasBookmarks = $bookResult && $bookResult->fetchArray(SQLITE3_NUM)[0];
|
||||||
echo hx_get('/feeds', 'Feeds') . ' | ';
|
echo hx_get('/feeds', 'Feeds') . ' | ';
|
||||||
if ($hasBookmarks) echo hx_get('/?bookmarked', 'Bookmarked') . ' | ';
|
if ($hasBookmarks) echo hx_get('/?bookmarked', 'Bookmarked') . ' | ';
|
||||||
echo hx_get('/docs/', 'Docs') . ' | <a href=/user/log-off>Log Off</a>';
|
echo hx_get('/search', 'Search') . ' | ' . hx_get('/docs/', 'Docs')
|
||||||
|
. ' | <a href=/user/log-off>Log Off</a>';
|
||||||
if ($_SESSION[Key::USER_EMAIL] != Security::SINGLE_USER_EMAIL) {
|
if ($_SESSION[Key::USER_EMAIL] != Security::SINGLE_USER_EMAIL) {
|
||||||
echo " | {$_SESSION[Key::USER_EMAIL]}";
|
echo " | {$_SESSION[Key::USER_EMAIL]}";
|
||||||
}
|
}
|
||||||
|
|
49
src/util/search.php
Normal file
49
src/util/search.php
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
<?php
|
||||||
|
use JetBrains\PhpStorm\NoReturn;
|
||||||
|
|
||||||
|
require __DIR__ . '/../cli-start.php';
|
||||||
|
|
||||||
|
cli_title('SEARCH MAINTENANCE');
|
||||||
|
|
||||||
|
if ($argc < 2) display_help();
|
||||||
|
|
||||||
|
switch ($argv[1]) {
|
||||||
|
case 'rebuild':
|
||||||
|
rebuild_index();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
printfn('Unrecognized option "%s"', $argv[1]);
|
||||||
|
display_help();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display the options for this utility and exit
|
||||||
|
*/
|
||||||
|
#[NoReturn]
|
||||||
|
function display_help(): void {
|
||||||
|
printfn('Options:');
|
||||||
|
printfn(' - rebuild');
|
||||||
|
printfn(' Rebuilds search index');
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rebuild the search index, creating it if it does not already exist
|
||||||
|
*/
|
||||||
|
function rebuild_index(): void {
|
||||||
|
$db = Data::getConnection();
|
||||||
|
|
||||||
|
try {
|
||||||
|
$hasIndex = $db->query("SELECT COUNT(*) FROM sqlite_master WHERE name = 'item_ai'");
|
||||||
|
if ($hasIndex->fetchArray(SQLITE3_NUM)[0] == 0) {
|
||||||
|
printfn('Creating search index....');
|
||||||
|
Data::createSearchIndex($db);
|
||||||
|
}
|
||||||
|
printfn('Rebuilding search index...');
|
||||||
|
$db->exec("INSERT INTO item_search (item_search) VALUES ('rebuild')");
|
||||||
|
printfn(PHP_EOL . 'Search index rebuilt');
|
||||||
|
} finally {
|
||||||
|
$db->close();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user