From 5672f2e23599541c7aa3984b3cf3c44e040d5c73 Mon Sep 17 00:00:00 2001 From: "Elf M. Sternberg" Date: Thu, 12 Nov 2020 08:44:20 -0800 Subject: [PATCH] REFACTOR "Build references" is now its own separate chunk of code. This is good because in used to be somewhat cut-and-paste, and that's just ugly. There's a lot of commonality between "insert note" and "update content," since both rely heavily on parsing the content in order to establish the web of relationships between notes and pages, so having that algorithm ONCE makes me happier. --- server/nm-store/src/store/private.rs | 100 +++++++++++++++++++++------ server/nm-store/src/store/store.rs | 53 ++------------ 2 files changed, 84 insertions(+), 69 deletions(-) diff --git a/server/nm-store/src/store/private.rs b/server/nm-store/src/store/private.rs index 6b8713a..e5aa345 100644 --- a/server/nm-store/src/store/private.rs +++ b/server/nm-store/src/store/private.rs @@ -4,6 +4,7 @@ use regex::Regex; use slug::slugify; use sqlx::{sqlite::Sqlite, Acquire, Done, Executor, Transaction}; use std::collections::HashSet; +use std::cmp; type SqlResult = sqlx::Result; @@ -125,13 +126,13 @@ pub(crate) async fn insert_note<'a, E>(executor: E, zettle: &NewNote) -> SqlResu where E: Executor<'a, Database = Sqlite>, { - let insert_one_page_sql = concat!( + let insert_one_note_sql = concat!( "INSERT INTO notes (id, content, kind, ", " creation_date, updated_date, lastview_date) ", "VALUES (?, ?, ?, ?, ?, ?);" ); - let _ = sqlx::query(insert_one_page_sql) + let _ = sqlx::query(insert_one_note_sql) .bind(&zettle.id) .bind(&zettle.content) .bind(zettle.kind.to_string()) @@ -143,6 +144,35 @@ where Ok(zettle.id.clone()) } +// Inserts a single note into the notes table. That is all. +pub(crate) async fn bulk_insert_notes<'a, E>(executor: E, notes: &[NewNote]) -> SqlResult<()> +where + E: Executor<'a, Database = Sqlite>, +{ + if notes.is_empty() { + return Ok(()); + } + + let insert_pattern = "VALUES (?, ?, ?, ?, ?, ?)".to_string(); + let insert_bulk_notes_sql = + "INSERT INTO notes (id, content, kind, creation_date, updated_date, lastview_date) ".to_string() + + &[insert_pattern.as_str()] + .repeat(notes.len()) + .join(", ") + &";".to_string(); + + let mut request = sqlx::query(&insert_bulk_notes_sql); + for note in notes { + request = request + .bind(¬e.id) + .bind(¬e.content) + .bind(note.kind.to_string()) + .bind(¬e.creation_date) + .bind(¬e.updated_date) + .bind(¬e.lastview_date); + } + request.execute(executor).await.map(|_| ()) +} + // ___ _ _ _ _ __ _ // | _ )_ _(_) |__| | | |/ /__ _ __| |_ ___ _ _ // | _ \ || | | / _` | | ' ( + executor: E, + note_id: &ParentId, + comp_loc: Option, +) -> SqlResult +where + E: Executor<'a, Database = Sqlite>, +{ + let row_count = assert_max_child_location_for_note(executor, note_id).await? + 1; + Ok(match comp_loc { + Some(location) => cmp::min(row_count, location), + None => row_count + }) +} + pub(crate) async fn assert_max_child_location_for_note<'a, E>( executor: E, note_id: &ParentId, @@ -531,6 +576,33 @@ where Ok(()) } + +// Given a list of references found in the content, generate the +// references that do not previously exist, returning all found +// references. NOTE: The function signature for this is for a +// transaction, and uses a nested transaction. +pub(crate) async fn validate_or_generate_all_found_references( + txi: &mut Transaction<'_, Sqlite>, + references: &[String] +) -> SqlResult> { + let mut tx = txi.begin().await?; + + let found_references = + find_all_kasten_from_list_of_references(&mut tx, &references).await?; + let new_references = diff_references(&references, &found_references); + let mut new_zettlekasten: Vec = vec![]; + for one_reference in new_references.iter() { + let slug = generate_slug(&mut tx, one_reference).await?; + new_zettlekasten.push(create_zettlekasten(&one_reference, &slug)); + } + let _ = bulk_insert_notes(&mut tx, &new_zettlekasten).await?; + + let mut all_reference_ids: Vec = found_references.iter().map(|r| NoteId(r.id.clone())).collect(); + all_reference_ids.append(&mut new_zettlekasten.iter().map(|r| NoteId(r.id.clone())).collect()); + tx.commit().await?; + Ok(all_reference_ids) +} + // __ __ _ // | \/ (_)___ __ // | |\/| | (_-, - note_id: &NoteId, -) -> SqlResult { - let mut txi = tx.begin().await?; +pub(crate) async fn count_existing_note_relationships<'a, E>(executor: E, note_id: &NoteId) -> SqlResult +where + E: Executor<'a, Database = Sqlite>, +{ let count_existing_note_relationships_sql = "SELECT COUNT(*) as count FROM note_relationships WHERE note_id = ?;"; - let _: RowCount = sqlx::query_as(&count_existing_note_relationships_sql) + let count: RowCount = sqlx::query_as(&count_existing_note_relationships_sql) .bind(&**note_id) - .fetch_one(&mut txi) + .fetch_one(executor) .await?; - - let count: RowCount = { - let count_existing_note_relationships_sql = - "SELECT COUNT(*) as count FROM note_relationships WHERE note_id = ?;"; - - sqlx::query_as(&count_existing_note_relationships_sql) - .bind(&**note_id) - .fetch_one(&mut txi) - .await? - }; - txi.commit().await?; Ok(count.count) } diff --git a/server/nm-store/src/store/store.rs b/server/nm-store/src/store/store.rs index d9e591d..c9cd8ea 100644 --- a/server/nm-store/src/store/store.rs +++ b/server/nm-store/src/store/store.rs @@ -56,8 +56,6 @@ use crate::reference_parser::build_references; use crate::store::private::*; use crate::structs::*; use sqlx::sqlite::SqlitePool; -use std::cmp; -// use std::collections::HashMap; use std::sync::Arc; /// A handle to our Sqlite database. @@ -182,9 +180,7 @@ impl NoteStore { let _ = delete_note_to_note_relationship(&mut tx, &old_parent_id, ¬e_id).await?; let _ = close_hole_for_deleted_note(&mut tx, &old_parent_id, old_note_location).await?; - let parent_max_location = - assert_max_child_location_for_note(&mut tx, &new_parent_id).await?; - let new_location = cmp::min(parent_max_location + 1, new_location); + let new_location = determine_max_child_location_for_note(&mut tx, &new_parent_id, Some(new_location)).await?; let _ = make_room_for_new_note(&mut tx, &new_parent_id, new_location).await?; let _ = insert_note_to_note_relationship( &mut tx, @@ -207,23 +203,7 @@ impl NoteStore { let mut tx = self.0.begin().await?; let _ = update_note_content(&mut tx, ¬e_id, &content).await?; let _ = delete_bulk_note_to_kasten_relationships(&mut tx, ¬e_id).await?; - let found_references = - find_all_kasten_from_list_of_references(&mut tx, &references).await?; - let new_references = diff_references(&references, &found_references); - let mut known_reference_ids: Vec = Vec::new(); - for one_reference in new_references.iter() { - let slug = generate_slug(&mut tx, one_reference).await?; - let zettlekasten = create_zettlekasten(&one_reference, &slug); - let _ = insert_note(&mut tx, &zettlekasten).await?; - known_reference_ids.push(NoteId(slug)); - } - - known_reference_ids.append( - &mut found_references - .iter() - .map(|r| NoteId(r.id.clone())) - .collect(), - ); + let known_reference_ids = validate_or_generate_all_found_references(&mut tx, &references).await?; let _ = insert_bulk_note_to_kasten_relationships(&mut tx, ¬e_id, &known_reference_ids) .await?; tx.commit().await?; @@ -292,37 +272,12 @@ impl NoteStore { let references = build_references(¬e.content); let mut tx = self.0.begin().await?; - let location = { - let max_child = assert_max_child_location_for_note(&mut tx, parent_id).await? + 1; - if let Some(location) = location { - cmp::min(max_child, location) - } else { - max_child - } - }; - + let location = determine_max_child_location_for_note(&mut tx, parent_id, location).await?; let note_id = NoteId(note.id.clone()); insert_note(&mut tx, ¬e).await?; make_room_for_new_note(&mut tx, &parent_id, location).await?; insert_note_to_note_relationship(&mut tx, &parent_id, ¬e_id, location, &kind).await?; - - let found_references = - find_all_kasten_from_list_of_references(&mut tx, &references).await?; - let new_references = diff_references(&references, &found_references); - let mut known_reference_ids: Vec = Vec::new(); - for one_reference in new_references.iter() { - let slug = generate_slug(&mut tx, one_reference).await?; - let zettlekasten = create_zettlekasten(&one_reference, &slug); - let _ = insert_note(&mut tx, &zettlekasten).await?; - known_reference_ids.push(NoteId(slug)); - } - - known_reference_ids.append( - &mut found_references - .iter() - .map(|r| NoteId(r.id.clone())) - .collect(), - ); + let known_reference_ids = validate_or_generate_all_found_references(&mut tx, &references).await?; let _ = insert_bulk_note_to_kasten_relationships(&mut tx, ¬e_id, &known_reference_ids) .await?; tx.commit().await?;