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.
This commit is contained in:
Elf M. Sternberg 2020-11-12 08:44:20 -08:00
parent 6bd14057ab
commit 5672f2e235
2 changed files with 84 additions and 69 deletions

View File

@ -4,6 +4,7 @@ use regex::Regex;
use slug::slugify; use slug::slugify;
use sqlx::{sqlite::Sqlite, Acquire, Done, Executor, Transaction}; use sqlx::{sqlite::Sqlite, Acquire, Done, Executor, Transaction};
use std::collections::HashSet; use std::collections::HashSet;
use std::cmp;
type SqlResult<T> = sqlx::Result<T>; type SqlResult<T> = sqlx::Result<T>;
@ -125,13 +126,13 @@ pub(crate) async fn insert_note<'a, E>(executor: E, zettle: &NewNote) -> SqlResu
where where
E: Executor<'a, Database = Sqlite>, E: Executor<'a, Database = Sqlite>,
{ {
let insert_one_page_sql = concat!( let insert_one_note_sql = concat!(
"INSERT INTO notes (id, content, kind, ", "INSERT INTO notes (id, content, kind, ",
" creation_date, updated_date, lastview_date) ", " creation_date, updated_date, lastview_date) ",
"VALUES (?, ?, ?, ?, ?, ?);" "VALUES (?, ?, ?, ?, ?, ?);"
); );
let _ = sqlx::query(insert_one_page_sql) let _ = sqlx::query(insert_one_note_sql)
.bind(&zettle.id) .bind(&zettle.id)
.bind(&zettle.content) .bind(&zettle.content)
.bind(zettle.kind.to_string()) .bind(zettle.kind.to_string())
@ -143,6 +144,35 @@ where
Ok(zettle.id.clone()) 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(&note.id)
.bind(&note.content)
.bind(note.kind.to_string())
.bind(&note.creation_date)
.bind(&note.updated_date)
.bind(&note.lastview_date);
}
request.execute(executor).await.map(|_| ())
}
// ___ _ _ _ _ __ _ // ___ _ _ _ _ __ _
// | _ )_ _(_) |__| | | |/ /__ _ __| |_ ___ _ _ // | _ )_ _(_) |__| | | |/ /__ _ __| |_ ___ _ _
// | _ \ || | | / _` | | ' </ _` (_-< _/ -_) ' \ // | _ \ || | | / _` | | ' </ _` (_-< _/ -_) ' \
@ -318,6 +348,21 @@ where
Ok(()) Ok(())
} }
pub(crate) async fn determine_max_child_location_for_note<'a, E>(
executor: E,
note_id: &ParentId,
comp_loc: Option<i64>,
) -> SqlResult<i64>
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>( pub(crate) async fn assert_max_child_location_for_note<'a, E>(
executor: E, executor: E,
note_id: &ParentId, note_id: &ParentId,
@ -531,6 +576,33 @@ where
Ok(()) 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<Vec<NoteId>> {
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<NewNote> = 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<NoteId> = 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)
}
// __ __ _ // __ __ _
// | \/ (_)___ __ // | \/ (_)___ __
// | |\/| | (_-</ _| // | |\/| | (_-</ _|
@ -539,27 +611,15 @@ where
// The dreaded miscellaneous! // The dreaded miscellaneous!
pub(crate) async fn count_existing_note_relationships( pub(crate) async fn count_existing_note_relationships<'a, E>(executor: E, note_id: &NoteId) -> SqlResult<i64>
tx: &mut Transaction<'_, Sqlite>, where
note_id: &NoteId, E: Executor<'a, Database = Sqlite>,
) -> SqlResult<i64> { {
let mut txi = tx.begin().await?;
let count_existing_note_relationships_sql = let count_existing_note_relationships_sql =
"SELECT COUNT(*) as count FROM note_relationships WHERE note_id = ?;"; "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) .bind(&**note_id)
.fetch_one(&mut txi) .fetch_one(executor)
.await?; .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) Ok(count.count)
} }

View File

@ -56,8 +56,6 @@ use crate::reference_parser::build_references;
use crate::store::private::*; use crate::store::private::*;
use crate::structs::*; use crate::structs::*;
use sqlx::sqlite::SqlitePool; use sqlx::sqlite::SqlitePool;
use std::cmp;
// use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
/// A handle to our Sqlite database. /// A handle to our Sqlite database.
@ -182,9 +180,7 @@ impl NoteStore {
let _ = delete_note_to_note_relationship(&mut tx, &old_parent_id, &note_id).await?; let _ = delete_note_to_note_relationship(&mut tx, &old_parent_id, &note_id).await?;
let _ = close_hole_for_deleted_note(&mut tx, &old_parent_id, old_note_location).await?; let _ = close_hole_for_deleted_note(&mut tx, &old_parent_id, old_note_location).await?;
let parent_max_location = let new_location = determine_max_child_location_for_note(&mut tx, &new_parent_id, Some(new_location)).await?;
assert_max_child_location_for_note(&mut tx, &new_parent_id).await?;
let new_location = cmp::min(parent_max_location + 1, new_location);
let _ = make_room_for_new_note(&mut tx, &new_parent_id, new_location).await?; let _ = make_room_for_new_note(&mut tx, &new_parent_id, new_location).await?;
let _ = insert_note_to_note_relationship( let _ = insert_note_to_note_relationship(
&mut tx, &mut tx,
@ -207,23 +203,7 @@ impl NoteStore {
let mut tx = self.0.begin().await?; let mut tx = self.0.begin().await?;
let _ = update_note_content(&mut tx, &note_id, &content).await?; let _ = update_note_content(&mut tx, &note_id, &content).await?;
let _ = delete_bulk_note_to_kasten_relationships(&mut tx, &note_id).await?; let _ = delete_bulk_note_to_kasten_relationships(&mut tx, &note_id).await?;
let found_references = let known_reference_ids = validate_or_generate_all_found_references(&mut tx, &references).await?;
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<NoteId> = 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 _ = insert_bulk_note_to_kasten_relationships(&mut tx, &note_id, &known_reference_ids) let _ = insert_bulk_note_to_kasten_relationships(&mut tx, &note_id, &known_reference_ids)
.await?; .await?;
tx.commit().await?; tx.commit().await?;
@ -292,37 +272,12 @@ impl NoteStore {
let references = build_references(&note.content); let references = build_references(&note.content);
let mut tx = self.0.begin().await?; let mut tx = self.0.begin().await?;
let location = { let location = determine_max_child_location_for_note(&mut tx, parent_id, location).await?;
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 note_id = NoteId(note.id.clone()); let note_id = NoteId(note.id.clone());
insert_note(&mut tx, &note).await?; insert_note(&mut tx, &note).await?;
make_room_for_new_note(&mut tx, &parent_id, location).await?; make_room_for_new_note(&mut tx, &parent_id, location).await?;
insert_note_to_note_relationship(&mut tx, &parent_id, &note_id, location, &kind).await?; insert_note_to_note_relationship(&mut tx, &parent_id, &note_id, location, &kind).await?;
let known_reference_ids = validate_or_generate_all_found_references(&mut tx, &references).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<NoteId> = 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 _ = insert_bulk_note_to_kasten_relationships(&mut tx, &note_id, &known_reference_ids) let _ = insert_bulk_note_to_kasten_relationships(&mut tx, &note_id, &known_reference_ids)
.await?; .await?;
tx.commit().await?; tx.commit().await?;