mirror of
https://github.com/patarapolw/cjdic2.git
synced 2026-11-09 17:39:35 +00:00
add delete dict operations
This commit is contained in:
1 parent
fa5bfb56b0
commit
1e91f21ca4
8 files changed
+129
-44
No files matched your search
@@ -31,7 +31,7 @@ fn main() -> Result<(), anyhow::Error> {
|
|||||||
"{:?}",
|
"{:?}",
|
||||||
AppService::import_yomitan_zip_file(&mut writer, p, "ja")?,
|
AppService::import_yomitan_zip_file(&mut writer, p, "ja")?,
|
||||||
);
|
);
|
||||||
println!("[{:.2?}]", start.elapsed(),);
|
println!("[{:.2?}]", start.elapsed());
|
||||||
} else {
|
} else {
|
||||||
println!("not zip_file: {:?}", p);
|
println!("not zip_file: {:?}", p);
|
||||||
}
|
}
|
||||||
+2
-1
@@ -10,7 +10,8 @@ fn main() -> Result<(), anyhow::Error> {
|
|||||||
let service = AppService::new(get_db_dir())?;
|
let service = AppService::new(get_db_dir())?;
|
||||||
|
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
service.get_yomitan_writer()?;
|
let mut writer = service.get_yomitan_writer()?;
|
||||||
|
AppService::remove_yomitan_dictionary(&mut writer, "JMnedict")?;
|
||||||
println!("[{:.2?}]", start.elapsed());
|
println!("[{:.2?}]", start.elapsed());
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
File renamed without changes.
@@ -1,10 +1,9 @@
|
|||||||
use std::{collections::HashMap, fs::File, io::Read, path::PathBuf};
|
|
||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use blake3::hash;
|
use blake3::hash;
|
||||||
use rusqlite::{Connection, Result, Transaction, params};
|
use rusqlite::{Connection, Result, Transaction, params};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
use std::{collections::HashMap, fs::File, io::Read, path::PathBuf, time::Instant};
|
||||||
use zip::ZipArchive;
|
use zip::ZipArchive;
|
||||||
|
|
||||||
use crate::CJDicError;
|
use crate::CJDicError;
|
||||||
@@ -25,19 +24,23 @@ pub struct YomitanWriter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl YomitanWriter {
|
impl YomitanWriter {
|
||||||
pub fn new(conn: Connection) -> Self {
|
pub fn new(conn: Connection) -> Result<Self> {
|
||||||
Self { conn }
|
conn.execute_batch(
|
||||||
}
|
"
|
||||||
|
|
||||||
pub fn create_schema(&self) -> Result<()> {
|
|
||||||
self.conn.execute_batch(
|
|
||||||
"
|
|
||||||
PRAGMA journal_mode = WAL;
|
PRAGMA journal_mode = WAL;
|
||||||
PRAGMA foreign_keys = ON;
|
PRAGMA foreign_keys = ON;
|
||||||
PRAGMA synchronous = NORMAL;
|
PRAGMA synchronous = NORMAL;
|
||||||
PRAGMA cache_size = -65536;
|
PRAGMA cache_size = -65536;
|
||||||
PRAGMA temp_store = MEMORY;
|
PRAGMA temp_store = MEMORY;
|
||||||
|
",
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(Self { conn })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_schema(&self) -> Result<()> {
|
||||||
|
self.conn.execute_batch(
|
||||||
|
"
|
||||||
CREATE TABLE IF NOT EXISTS schema_meta (
|
CREATE TABLE IF NOT EXISTS schema_meta (
|
||||||
key TEXT PRIMARY KEY,
|
key TEXT PRIMARY KEY,
|
||||||
value TEXT NOT NULL
|
value TEXT NOT NULL
|
||||||
@@ -107,11 +110,18 @@ impl YomitanWriter {
|
|||||||
|
|
||||||
self.conn.execute_batch(
|
self.conn.execute_batch(
|
||||||
"
|
"
|
||||||
CREATE INDEX IF NOT EXISTS dictionaries_lang ON dictionaries (lang);
|
CREATE INDEX IF NOT EXISTS dictionaries_lang ON dictionaries (lang);
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS terms_term ON terms (term);
|
CREATE INDEX IF NOT EXISTS terms_dict_id ON terms (dict_id);
|
||||||
CREATE INDEX IF NOT EXISTS terms_reading ON terms (reading);
|
CREATE INDEX IF NOT EXISTS terms_term ON terms (term);
|
||||||
",
|
CREATE INDEX IF NOT EXISTS terms_reading ON terms (reading);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS term_meta_dict_id ON term_meta (dict_id);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS tags_dict_id ON tags (dict_id);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS kanji_dict_id ON kanji (dict_id);
|
||||||
|
",
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let mut stmt = self
|
let mut stmt = self
|
||||||
@@ -131,12 +141,15 @@ impl YomitanWriter {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn import_bundled_zip_file(
|
pub fn import_dictionary_zip_file(
|
||||||
&mut self,
|
&mut self,
|
||||||
zip_file: PathBuf,
|
zip_file: PathBuf,
|
||||||
lang: &str,
|
lang: &str,
|
||||||
) -> anyhow::Result<YomitanZipImportResult, CJDicError> {
|
) -> anyhow::Result<YomitanZipImportResult, CJDicError> {
|
||||||
self.create_schema()?;
|
self.create_schema()?; // < 1 ms
|
||||||
|
|
||||||
|
let start_time = Instant::now();
|
||||||
|
self.conn.execute_batch("PRAGMA foreign_keys = off")?;
|
||||||
|
|
||||||
let f =
|
let f =
|
||||||
File::open(&zip_file).with_context(|| format!("opening zip {}", zip_file.display()))?;
|
File::open(&zip_file).with_context(|| format!("opening zip {}", zip_file.display()))?;
|
||||||
@@ -159,36 +172,57 @@ impl YomitanWriter {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let title = index_file
|
fn remove_timestamp(s: &str) -> String {
|
||||||
.get("title")
|
if let Some(pos) = s.rfind('[') {
|
||||||
.and_then(Value::as_str)
|
// Trim whitespace only before the '[' and return the substring
|
||||||
.unwrap_or("")
|
let trimmed_before_bracket = s[..pos].trim_end();
|
||||||
.to_string();
|
trimmed_before_bracket.to_string()
|
||||||
|
} else {
|
||||||
|
s.to_string() // Return the original string if no '[' is found
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let title = remove_timestamp(
|
||||||
|
&index_file
|
||||||
|
.get("title")
|
||||||
|
.and_then(Value::as_str)
|
||||||
|
.unwrap_or("")
|
||||||
|
.to_string(),
|
||||||
|
);
|
||||||
|
|
||||||
let revision = index_file
|
let revision = index_file
|
||||||
.get("revision")
|
.get("revision")
|
||||||
.and_then(Value::as_str)
|
.and_then(Value::as_str)
|
||||||
.unwrap_or("")
|
.unwrap_or("")
|
||||||
.to_string();
|
.to_string();
|
||||||
|
|
||||||
// Skip if already installed
|
{
|
||||||
let exists: bool = self
|
let title: &str = &title;
|
||||||
.conn
|
// Skip if already installed
|
||||||
.prepare("SELECT 1 FROM dictionaries WHERE title = ?1 AND revision = ?2")?
|
let mut stmt = self
|
||||||
.exists([title, revision])?;
|
.conn
|
||||||
if exists {
|
.prepare("SELECT (revision < ?2) FROM dictionaries WHERE title = ?1")?;
|
||||||
return Ok(YomitanZipImportResult {
|
let mut rows = stmt.query(params![title, revision])?;
|
||||||
exists: true,
|
if let Some(row) = rows.next()? {
|
||||||
load: false,
|
let is_outdated: bool = row.get(0)?;
|
||||||
error: None,
|
if is_outdated {
|
||||||
});
|
self.drop_dictionary(title)?;
|
||||||
|
} else {
|
||||||
|
return Ok(YomitanZipImportResult {
|
||||||
|
exists: true,
|
||||||
|
load: false,
|
||||||
|
error: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let tx = self.conn.transaction()?;
|
let tx = self.conn.transaction()?;
|
||||||
tx.execute(
|
tx.execute(
|
||||||
"INSERT INTO dictionaries (title, revision, author, url, description, lang) VALUES (?1, ?2, ?3, ?4, ?5, ?6)",
|
"INSERT INTO dictionaries (title, revision, author, url, description, lang) VALUES (?1, ?2, ?3, ?4, ?5, ?6)",
|
||||||
params![
|
params![
|
||||||
index_file.get("title").and_then(Value::as_str),
|
title,
|
||||||
index_file.get("revision").and_then(Value::as_str),
|
revision,
|
||||||
index_file.get("author").and_then(Value::as_str),
|
index_file.get("author").and_then(Value::as_str),
|
||||||
index_file.get("url").and_then(Value::as_str),
|
index_file.get("url").and_then(Value::as_str),
|
||||||
index_file.get("description").and_then(Value::as_str),
|
index_file.get("description").and_then(Value::as_str),
|
||||||
@@ -377,7 +411,20 @@ impl YomitanWriter {
|
|||||||
|
|
||||||
tx.commit()?;
|
tx.commit()?;
|
||||||
|
|
||||||
self.conn.execute_batch("VACUUM;")?;
|
println!(
|
||||||
|
"Importing {:?} takes {:.2?}",
|
||||||
|
zip_file,
|
||||||
|
start_time.elapsed()
|
||||||
|
);
|
||||||
|
|
||||||
|
self.conn.execute_batch(
|
||||||
|
"
|
||||||
|
PRAGMA foreign_keys = on;
|
||||||
|
PRAGMA check_foreign_keys;
|
||||||
|
",
|
||||||
|
)?; // < 1 ms
|
||||||
|
|
||||||
|
// self.conn.execute_batch("VACUUM;")?; // Not needed, twice time taken than import
|
||||||
|
|
||||||
Ok(YomitanZipImportResult {
|
Ok(YomitanZipImportResult {
|
||||||
exists: true,
|
exists: true,
|
||||||
@@ -385,6 +432,13 @@ impl YomitanWriter {
|
|||||||
error: None,
|
error: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn drop_dictionary(&self, title: &str) -> Result<()> {
|
||||||
|
self.conn
|
||||||
|
.execute("DELETE FROM dictionaries WHERE title = ?1", [title])?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn intern(
|
fn intern(
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ impl AppService {
|
|||||||
|
|
||||||
pub fn get_yomitan_writer(&self) -> Result<YomitanWriter, CJDicError> {
|
pub fn get_yomitan_writer(&self) -> Result<YomitanWriter, CJDicError> {
|
||||||
let conn = Connection::open(self.db.dir.join("yomitan.db"))?;
|
let conn = Connection::open(self.db.dir.join("yomitan.db"))?;
|
||||||
let writer = YomitanWriter::new(conn);
|
let writer = YomitanWriter::new(conn)?;
|
||||||
writer.create_schema()?;
|
writer.create_schema()?;
|
||||||
Ok(writer)
|
Ok(writer)
|
||||||
}
|
}
|
||||||
@@ -53,6 +53,13 @@ impl AppService {
|
|||||||
zip_file: PathBuf,
|
zip_file: PathBuf,
|
||||||
lang: &str,
|
lang: &str,
|
||||||
) -> Result<YomitanZipImportResult, CJDicError> {
|
) -> Result<YomitanZipImportResult, CJDicError> {
|
||||||
Ok(writer.import_bundled_zip_file(zip_file, lang)?)
|
Ok(writer.import_dictionary_zip_file(zip_file, lang)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_yomitan_dictionary(
|
||||||
|
writer: &mut YomitanWriter,
|
||||||
|
title: &str,
|
||||||
|
) -> Result<(), CJDicError> {
|
||||||
|
Ok(writer.drop_dictionary(title)?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 254 B |
@@ -1,9 +1,24 @@
|
|||||||
|
a {
|
||||||
|
text-decoration: underline;
|
||||||
|
color: teal;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
display: inline-flex;
|
||||||
|
gap: 0.5em;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
:root {
|
:root {
|
||||||
color: #f6f6f6;
|
color: #f6f6f6;
|
||||||
background-color: #2f2f2f;
|
background-color: #2f2f2f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: lightgray;
|
||||||
|
}
|
||||||
|
|
||||||
a:hover {
|
a:hover {
|
||||||
color: #24c8db;
|
color: #24c8db;
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-6
@@ -68,12 +68,20 @@ function App() {
|
|||||||
<Card.Root>
|
<Card.Root>
|
||||||
<Card.Header>{JSON.stringify(it)}</Card.Header>
|
<Card.Header>{JSON.stringify(it)}</Card.Header>
|
||||||
<Card.Body>
|
<Card.Body>
|
||||||
{JSON.parse(glossary_json).map((g: any) => (
|
<div
|
||||||
<Glossary
|
style={{
|
||||||
glossary={g}
|
gap: "1em",
|
||||||
onTermClicked={(t) => setQ(t + " ")}
|
display: "flex",
|
||||||
/>
|
flexDirection: "column",
|
||||||
))}
|
}}
|
||||||
|
>
|
||||||
|
{JSON.parse(glossary_json).map((g: any) => (
|
||||||
|
<Glossary
|
||||||
|
glossary={g}
|
||||||
|
onTermClicked={(t) => setQ(t + " ")}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</Card.Body>
|
</Card.Body>
|
||||||
</Card.Root>
|
</Card.Root>
|
||||||
</details>
|
</details>
|
||||||
|
|||||||
Reference in new issue
Block a user