plan search db

This commit is contained in:
polv committed 2026-03-08 19:17:31 +07:00
1 parent ec14023c04
commit a97da3fa1d
6 files changed
+61 -23

No files matched your search

+15 -12
View File
@@ -7,16 +7,19 @@ use std::{
}, },
}; };
use anyhow::{Ok, Result};
use enum_map::EnumMap; use enum_map::EnumMap;
use rusqlite::Connection; use rusqlite::Connection;
mod search;
mod yomitan; mod yomitan;
pub use yomitan::YomitanRow; pub use yomitan::YomitanRow;
mod yomitan_writer; mod yomitan_writer;
pub use yomitan_writer::{YomitanProgress, YomitanWriter, YomitanZipImportResult, ZipSource}; pub use yomitan_writer::{YomitanProgress, YomitanWriter, YomitanZipImportResult, ZipSource};
use crate::CJDicError;
#[derive(Clone)] #[derive(Clone)]
pub(crate) struct Database { pub(crate) struct Database {
pub(crate) conn: Arc<Mutex<Connection>>, pub(crate) conn: Arc<Mutex<Connection>>,
@@ -28,28 +31,28 @@ pub(crate) struct Database {
pub(crate) enum DbChild { pub(crate) enum DbChild {
Yomitan, Yomitan,
YomitanGlossary, YomitanGlossary,
Reading, Search,
} }
const USER_DBFILE: &str = "user.db"; const USER_DBFILE: &str = "user.db";
pub(crate) const DBFILE: LazyLock<EnumMap<DbChild, &str>> = LazyLock::new(|| { pub(crate) static DBFILE: LazyLock<EnumMap<DbChild, &str>> = LazyLock::new(|| {
enum_map! { enum_map! {
DbChild::Yomitan => "yomitan.db", DbChild::Yomitan => "yomitan.db",
DbChild::YomitanGlossary => "yomitan-glossary.db", DbChild::YomitanGlossary => "yomitan-glossary.db",
DbChild::Reading => "reading.db" DbChild::Search => "reading.db"
} }
}); });
pub(crate) const DBSCHEMA: LazyLock<EnumMap<DbChild, &str>> = LazyLock::new(|| { pub(crate) static DBSCHEMA: LazyLock<EnumMap<DbChild, &str>> = LazyLock::new(|| {
enum_map! { enum_map! {
DbChild::Yomitan => "yomitan", DbChild::Yomitan => "yomitan",
DbChild::YomitanGlossary => "glossary", DbChild::YomitanGlossary => "glossary",
DbChild::Reading => "reading" DbChild::Search => "reading"
} }
}); });
impl Database { impl Database {
pub fn new(db_dir: impl AsRef<Path>) -> Result<Self> { pub fn new(db_dir: impl AsRef<Path>) -> Result<Self, CJDicError> {
let dir = db_dir.as_ref().to_path_buf(); let dir = db_dir.as_ref().to_path_buf();
let conn = Connection::open(dir.join(USER_DBFILE))?; let conn = Connection::open(dir.join(USER_DBFILE))?;
@@ -68,24 +71,24 @@ impl Database {
is_db_attached: enum_map! { is_db_attached: enum_map! {
DbChild::Yomitan => Arc::new(AtomicBool::new(false)), DbChild::Yomitan => Arc::new(AtomicBool::new(false)),
DbChild::YomitanGlossary => Arc::new(AtomicBool::new(false)), DbChild::YomitanGlossary => Arc::new(AtomicBool::new(false)),
DbChild::Reading => Arc::new(AtomicBool::new(false)), DbChild::Search => Arc::new(AtomicBool::new(false)),
}, },
}) })
} }
pub fn yomitan(&self) -> Result<yomitan::YomitanDatabase> { pub fn yomitan(&self) -> Result<yomitan::YomitanDatabase, CJDicError> {
self.ensure_db_attached(DbChild::Yomitan)?; self.ensure_db_attached(DbChild::Yomitan)?;
self.ensure_db_attached(DbChild::YomitanGlossary)?; self.ensure_db_attached(DbChild::YomitanGlossary)?;
self.ensure_db_attached(DbChild::Reading)?; self.ensure_db_attached(DbChild::Search)?;
Ok(yomitan::YomitanDatabase::new(self.clone())) Ok(yomitan::YomitanDatabase::new(self.clone()))
} }
fn ensure_db_attached(&self, db_child: DbChild) -> Result<()> { fn ensure_db_attached(&self, db_child: DbChild) -> Result<(), CJDicError> {
if self.is_db_attached[db_child].load(Ordering::Acquire) { if self.is_db_attached[db_child].load(Ordering::Acquire) {
return Ok(()); return Ok(());
} }
let conn = self.conn.lock().unwrap(); let conn = self.conn.lock()?;
let is_attached = self.is_db_attached[db_child].load(Ordering::Relaxed); let is_attached = self.is_db_attached[db_child].load(Ordering::Relaxed);
if !is_attached { if !is_attached {
+17
View File
@@ -0,0 +1,17 @@
#[allow(unused)]
pub(super) fn normalize_reading(s: &str) -> String {
s.chars()
.map(|c| {
if c >= 'ア' && c <= 'ン' {
// Katakana range
if let Some(x) = char::from_u32(c as u32 - 0x60) {
x // Convert to Hiragana
} else {
c // Ensure it's a valid char
}
} else {
c // Return the character as is if it's not in the range
}
})
.collect()
}
+2 -2
View File
@@ -31,7 +31,7 @@ impl YomitanDatabase {
} }
fn decompressor(&self) -> Result<Decompressor<'_>, CJDicError> { fn decompressor(&self) -> Result<Decompressor<'_>, CJDicError> {
let conn = self.db.conn.lock().unwrap(); let conn = self.db.conn.lock()?;
let row = conn.query_row( let row = conn.query_row(
"SELECT value FROM glossary.meta WHERE key = 'zstd_dict'", "SELECT value FROM glossary.meta WHERE key = 'zstd_dict'",
@@ -79,7 +79,7 @@ impl YomitanDatabase {
let eq1 = if q_term_norm.len() > 0 { "GLOB" } else { "=" }; let eq1 = if q_term_norm.len() > 0 { "GLOB" } else { "=" };
let eq2 = if q_reading.len() > 0 { "GLOB" } else { "=" }; let eq2 = if q_reading.len() > 0 { "GLOB" } else { "=" };
let conn = self.db.conn.lock().unwrap(); let conn = self.db.conn.lock()?;
conn.execute_batch( conn.execute_batch(
" "
CREATE TEMP TABLE IF NOT EXISTS yomitan_lookup_keys ( CREATE TEMP TABLE IF NOT EXISTS yomitan_lookup_keys (
+11 -7
View File
@@ -13,7 +13,7 @@ use zip::ZipArchive;
use crate::{ use crate::{
CJDicError, Timer, CJDicError, Timer,
db::{DBFILE, DbChild}, db::{DBFILE, DBSCHEMA, DbChild},
}; };
fn blake3_hex(s: &str) -> String { fn blake3_hex(s: &str) -> String {
@@ -174,7 +174,8 @@ impl YomitanWriter {
)?; )?;
self.create_materialized_views()?; self.create_materialized_views()?;
self.attach_glossary()?; self.attach_db(DbChild::YomitanGlossary)?;
self.attach_db(DbChild::Search)?;
let schema_version = "2026-03-07"; let schema_version = "2026-03-07";
@@ -361,7 +362,7 @@ impl YomitanWriter {
Ok(()) Ok(())
} }
fn attach_glossary(&mut self) -> Result<(), CJDicError> { fn attach_db(&mut self, db_child: DbChild) -> Result<(), CJDicError> {
let dir = self.dir.clone(); let dir = self.dir.clone();
let path = if self.dir.is_absolute() { let path = if self.dir.is_absolute() {
dir dir
@@ -369,8 +370,8 @@ impl YomitanWriter {
current_dir()?.join(dir) current_dir()?.join(dir)
}; };
let db_file = DBFILE[DbChild::YomitanGlossary]; let db_file = DBFILE[db_child];
let schema_name = "glossary"; let schema_name = DBSCHEMA[db_child];
let path = path.join(db_file).to_string_lossy().replace(r"\", r"/"); let path = path.join(db_file).to_string_lossy().replace(r"\", r"/");
@@ -392,7 +393,10 @@ impl YomitanWriter {
schema_name schema_name
))?; ))?;
self.create_schema_glossary()?; match db_child {
DbChild::YomitanGlossary => self.create_schema_glossary()?,
_ => (),
}
Ok(()) Ok(())
} }
@@ -529,7 +533,7 @@ impl YomitanWriter {
} }
} }
self.attach_glossary()?; self.attach_db(DbChild::YomitanGlossary)?;
{ {
let tx = self.conn.transaction()?; let tx = self.conn.transaction()?;
+15 -1
View File
@@ -1,5 +1,9 @@
use std::string::FromUtf8Error; use std::{
string::FromUtf8Error,
sync::{MutexGuard, PoisonError},
};
use rusqlite::Connection;
use serde::Serialize; use serde::Serialize;
use thiserror::Error; use thiserror::Error;
use vibrato::errors::VibratoError; use vibrato::errors::VibratoError;
@@ -32,6 +36,9 @@ pub enum CJDicError {
#[error("FromUtf8Error: {0}")] #[error("FromUtf8Error: {0}")]
FromUtf8Error(String), FromUtf8Error(String),
#[error("ConnectionMutexGuardError: {0}")]
ConnectionMutexGuardError(String),
#[error("Not found")] #[error("Not found")]
NotFound, NotFound,
@@ -87,3 +94,10 @@ impl From<FromUtf8Error> for CJDicError {
CJDicError::FromUtf8Error(e.to_string()) CJDicError::FromUtf8Error(e.to_string())
} }
} }
impl From<PoisonError<MutexGuard<'_, Connection>>> for CJDicError {
fn from(e: PoisonError<MutexGuard<'_, Connection>>) -> Self {
eprintln!("{e:#}");
CJDicError::ConnectionMutexGuardError(e.to_string())
}
}
+1 -1
View File
@@ -91,7 +91,7 @@ impl AppService {
) -> Result<serde_json::Value, CJDicError> { ) -> Result<serde_json::Value, CJDicError> {
let _timer = Timer::new(sql.to_string()); let _timer = Timer::new(sql.to_string());
let conn = self.db.conn.lock().unwrap(); let conn = self.db.conn.lock()?;
let converted_params: Vec<_> = params let converted_params: Vec<_> = params
.into_iter() .into_iter()
.map(|p| match p { .map(|p| match p {