From d3fb69cf129fe70c932a5d82fdd1bcc613544b5b Mon Sep 17 00:00:00 2001 From: Joris Date: Sun, 27 Nov 2022 15:38:39 +0100 Subject: Speed up deck synchronization Apply changes for what has been inserted, updated, removed. Also use transactions to speed up multi-writing. --- src/db/mod.rs | 116 +++++++++++++++++++++++----------------- src/db/sql/3-drop-deck-read.sql | 1 + 2 files changed, 68 insertions(+), 49 deletions(-) create mode 100644 src/db/sql/3-drop-deck-read.sql (limited to 'src/db') diff --git a/src/db/mod.rs b/src/db/mod.rs index c2749dc..434d74a 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -1,81 +1,99 @@ -use crate::{ - model::{card::Card, entry::Entry}, - space_repetition, - util::serialization, -}; use anyhow::Result; use rusqlite::{params, Connection}; use rusqlite_migration::{Migrations, M}; +use crate::model::DbEntry; use crate::util::time; +use crate::{ + model::{Card, Question}, + space_repetition, + util::serialization, +}; pub fn init(database: String) -> Result { let mut conn = Connection::open(database)?; let migrations = Migrations::new(vec![ M::up(include_str!("sql/1-init.sql")), M::up(include_str!("sql/2-primary-key-question-responses.sql")), + M::up(include_str!("sql/3-drop-deck-read.sql")), ]); migrations.to_latest(&mut conn)?; Ok(conn) } -pub fn last_deck_read(conn: &Connection) -> Option { - let mut stmt = conn - .prepare("SELECT deck_read FROM cards ORDER BY deck_read DESC LIMIT 1") - .ok()?; - - let mut rows = stmt.query([]).ok()?; - let row = rows.next().ok()??; - row.get(0).ok()? +pub fn all(conn: &Connection) -> Result> { + let mut stmt = conn.prepare("SELECT question, responses, deleted FROM cards")?; + + let res: Result, rusqlite::Error> = stmt + .query_map([], |row| { + let responses: String = row.get(1)?; + Ok(DbEntry { + question: row.get(0)?, + responses: serialization::line_to_words(&responses), + deleted: row.get(2)?, + }) + })? + .collect(); + + Ok(res?) } -/// Synchronize the DB with the deck: -/// -/// - insert new cards, -/// - keep existing cards, -/// - hide unused cards (keep state in case the card is added back afterward). -pub fn synchronize(conn: &Connection, entries: Vec) -> Result<()> { +pub fn insert(conn: &mut Connection, questions: &Vec) -> Result<()> { let now = time::seconds_since_unix_epoch()?; - let state = serde_json::to_string(&space_repetition::init())?; - for entry in entries { - let concat_1 = serialization::words_to_line(&entry.part_1); - let concat_2 = serialization::words_to_line(&entry.part_2); - - for w in entry.part_1.iter() { - insert(conn, now, w, &concat_2, &state)?; - } - - for w in entry.part_2.iter() { - insert(conn, now, w, &concat_1, &state)?; - } + let tx = conn.transaction()?; + for Question { + question, + responses, + } in questions + { + let responses = serialization::words_to_line(responses); + tx.execute( + " + INSERT INTO cards (question, responses, state, created, ready) + VALUES (?, ?, ?, ?, ?) + ", + params![question, responses, state, now, now], + )?; } - - delete_read_before(conn, now)?; - + tx.commit()?; Ok(()) } -fn insert(conn: &Connection, now: u64, question: &str, responses: &str, state: &str) -> Result<()> { - conn.execute( - " - INSERT INTO cards (question, responses, state, created, deck_read, ready) - VALUES (?, ?, ?, ?, ?, ?) - ON CONFLICT (question, responses) DO UPDATE SET deck_read = ?, deleted = null - ", - params![question, responses, state, now, now, now, now], - )?; +pub fn delete(conn: &mut Connection, questions: &Vec) -> Result<()> { + let now = time::seconds_since_unix_epoch()?; + let tx = conn.transaction()?; + for Question { + question, + responses, + } in questions + { + let responses = serialization::words_to_line(responses); + tx.execute( + "UPDATE cards SET deleted = ? WHERE question = ? AND responses = ?", + params![now, question, responses], + )?; + } + tx.commit()?; Ok(()) } -fn delete_read_before(conn: &Connection, t: u64) -> Result<()> { - conn.execute( - "UPDATE cards SET deleted = ? WHERE deck_read < ?", - params![t, t], - )?; - +pub fn undelete(conn: &mut Connection, questions: &Vec) -> Result<()> { + let tx = conn.transaction()?; + for Question { + question, + responses, + } in questions + { + let responses = serialization::words_to_line(responses); + tx.execute( + "UPDATE cards SET deleted = NULL WHERE question = ? AND responses = ?", + params![question, responses], + )?; + } + tx.commit()?; Ok(()) } diff --git a/src/db/sql/3-drop-deck-read.sql b/src/db/sql/3-drop-deck-read.sql new file mode 100644 index 0000000..1ca23d1 --- /dev/null +++ b/src/db/sql/3-drop-deck-read.sql @@ -0,0 +1 @@ +ALTER TABLE cards DROP COLUMN deck_read; -- cgit v1.2.3