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?;