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 sqlx::{sqlite::Sqlite, Acquire, Done, Executor, Transaction};
use std::collections::HashSet;
use std::cmp;
type SqlResult<T> = sqlx::Result<T>;
@ -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(&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(())
}
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>(
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<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!
pub(crate) async fn count_existing_note_relationships(
tx: &mut Transaction<'_, Sqlite>,
note_id: &NoteId,
) -> SqlResult<i64> {
let mut txi = tx.begin().await?;
pub(crate) async fn count_existing_note_relationships<'a, E>(executor: E, note_id: &NoteId) -> SqlResult<i64>
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)
}

View File

@ -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, &note_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, &note_id, &content).await?;
let _ = delete_bulk_note_to_kasten_relationships(&mut tx, &note_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<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 known_reference_ids = validate_or_generate_all_found_references(&mut tx, &references).await?;
let _ = insert_bulk_note_to_kasten_relationships(&mut tx, &note_id, &known_reference_ids)
.await?;
tx.commit().await?;
@ -292,37 +272,12 @@ impl NoteStore {
let references = build_references(&note.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, &note).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?;
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 known_reference_ids = validate_or_generate_all_found_references(&mut tx, &references).await?;
let _ = insert_bulk_note_to_kasten_relationships(&mut tx, &note_id, &known_reference_ids)
.await?;
tx.commit().await?;