ensure the search database is attached to all connections
This commit is contained in:
parent
aa47ec7c43
commit
a0c5122735
2 changed files with 52 additions and 18 deletions
|
@ -5,10 +5,11 @@
|
|||
//! virtual table leveraging fts5 (https://www.sqlite.org/fts5.html). Full-text search is run on
|
||||
//! that virtual table.
|
||||
|
||||
use std::path::Path;
|
||||
|
||||
use r2d2::{Pool, PooledConnection};
|
||||
use r2d2_sqlite::SqliteConnectionManager;
|
||||
use rusqlite::named_params;
|
||||
use tempfile::NamedTempFile;
|
||||
|
||||
use crate::data::{book::Book, error::DataStoreError};
|
||||
|
||||
|
@ -32,28 +33,53 @@ const SEARCH_INIT_QUERY: &str = "INSERT INTO search.fts(book_id, data)
|
|||
LEFT JOIN main.series AS s ON b2s.series = s.id
|
||||
GROUP BY b.id";
|
||||
|
||||
/// Attach the fts temporary database to the read-only calibre database.
|
||||
pub(crate) fn attach(pool: &Pool<SqliteConnectionManager>) -> Result<(), DataStoreError> {
|
||||
let conn = pool.get()?;
|
||||
let tmpfile = NamedTempFile::new()?;
|
||||
let (_, path) = tmpfile.keep()?;
|
||||
/// Ensure the search database is attached to the connection and
|
||||
/// initializes the data if needed.
|
||||
fn ensure_search_db(
|
||||
conn: &PooledConnection<SqliteConnectionManager>,
|
||||
db_path: &Path,
|
||||
) -> Result<(), DataStoreError> {
|
||||
let mut stmt =
|
||||
conn.prepare("SELECT COUNT() FROM pragma_database_list WHERE name = 'search'")?;
|
||||
let count: u64 = stmt.query_row([], |x| x.get(0))?;
|
||||
let need_attachment = count == 0;
|
||||
|
||||
if need_attachment {
|
||||
attach(conn, db_path)?;
|
||||
init(conn)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Attach the fts temporary database to the read-only calibre database.
|
||||
fn attach(
|
||||
conn: &PooledConnection<SqliteConnectionManager>,
|
||||
db_path: &Path,
|
||||
) -> Result<(), DataStoreError> {
|
||||
conn.execute(
|
||||
&format!("ATTACH DATABASE '{}' AS search", path.to_string_lossy()),
|
||||
&format!("ATTACH DATABASE '{}' AS search", db_path.to_string_lossy()),
|
||||
[],
|
||||
)?;
|
||||
init(&conn)?;
|
||||
init(conn)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Initialise the fts virtual table.
|
||||
fn init(conn: &PooledConnection<SqliteConnectionManager>) -> Result<(), DataStoreError> {
|
||||
conn.execute(
|
||||
"CREATE VIRTUAL TABLE search.fts USING fts5(book_id, data)",
|
||||
[],
|
||||
)?;
|
||||
conn.execute(SEARCH_INIT_QUERY, [])?;
|
||||
let mut stmt = conn
|
||||
.prepare("SELECT COUNT() FROM search.sqlite_master WHERE type='table' AND name = 'fts'")?;
|
||||
let count: u64 = stmt.query_row([], |x| x.get(0))?;
|
||||
let need_init = count == 0;
|
||||
|
||||
if need_init {
|
||||
conn.execute(
|
||||
"CREATE VIRTUAL TABLE search.fts USING fts5(book_id, data)",
|
||||
[],
|
||||
)?;
|
||||
conn.execute(SEARCH_INIT_QUERY, [])?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -62,8 +88,10 @@ fn init(conn: &PooledConnection<SqliteConnectionManager>) -> Result<(), DataStor
|
|||
pub(crate) fn search(
|
||||
query: &str,
|
||||
pool: &Pool<SqliteConnectionManager>,
|
||||
search_db_path: &Path,
|
||||
) -> Result<Vec<Book>, DataStoreError> {
|
||||
let conn = pool.get()?;
|
||||
ensure_search_db(&conn, search_db_path)?;
|
||||
|
||||
let mut stmt =
|
||||
conn.prepare("SELECT book_id FROM search.fts WHERE data MATCH (:query) ORDER BY rank")?;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue