Refinements to the tree structure.

This commit is contained in:
Elf M. Sternberg 2020-11-05 08:30:02 -08:00
parent 4776541df4
commit 83d5858a45
8 changed files with 109 additions and 55 deletions

View File

@ -0,0 +1,2 @@
[ ] Add RelationshipKind to Notes passed out
[ ] Add KastenKind to Backreferences passed out

View File

@ -6,7 +6,8 @@ mod structs;
pub use crate::errors::NoteStoreError;
pub use crate::store::NoteStore;
pub use crate::structs::{Note, NoteKind};
pub use crate::structs::{Note, NoteKind, NoteRelationship, KastenRelationship};
#[cfg(test)]
mod tests {
@ -81,22 +82,22 @@ mod tests {
// <- 2 <- 4
let note1 = make_new_note("1");
let note1_id = storagepool.add_note(&note1, &root.id, 0).await;
let note1_id = storagepool.add_note(&note1, &root.id, Some(0)).await;
assert!(note1_id.is_ok(), "{:?}", note1_id);
let note1_id = note1_id.unwrap();
let note2 = make_new_note("2");
let note2_id = storagepool.add_note(&note2, &root.id, 0).await;
let note2_id = storagepool.add_note(&note2, &root.id, Some(0)).await;
assert!(note2_id.is_ok(), "{:?}", note2_id);
let note2_id = note2_id.unwrap();
let note3 = make_new_note("3");
let note3_id = storagepool.add_note(&note3, &note1_id, 0).await;
let note3_id = storagepool.add_note(&note3, &note1_id, Some(0)).await;
assert!(note3_id.is_ok(), "{:?}", note3_id);
let _note3_id = note3_id.unwrap();
let note4 = make_new_note("4");
let note4_id = storagepool.add_note(&note4, &note2_id, 0).await;
let note4_id = storagepool.add_note(&note4, &note2_id, Some(0)).await;
assert!(note4_id.is_ok(), "{:?}", note4_id);
let _note4_id = note4_id.unwrap();

View File

@ -16,7 +16,7 @@ CREATE TABLE notes (
CREATE INDEX note_ids ON notes (id);
CREATE TABLE favorites (
id TEXT NOT NULL,
id TEXT NOT NULL UNIQUE,
location INTEGER NOT NULL,
FOREIGN KEY (id) REFERENCES notes (id) ON DELETE CASCADE
);
@ -33,6 +33,7 @@ CREATE TABLE note_relationships (
-- If either note disappears, we want all the edges to disappear as well.
FOREIGN KEY (note_id) REFERENCES notes (id) ON DELETE CASCADE,
FOREIGN KEY (parent_id) REFERENCES notes (id) ON DELETE CASCADE,
UNIQUE (note_id, parent_id),
CHECK (note_id <> parent_id)
);
@ -45,6 +46,7 @@ CREATE TABLE note_kasten_relationships (
-- If either note disappears, we want all the edges to disappear as well.
FOREIGN KEY (note_id) REFERENCES notes (id) ON DELETE CASCADE,
FOREIGN KEY (kasten_id) REFERENCES notes (id) ON DELETE CASCADE,
UNIQUE (note_id, kasten_id),
CHECK (note_id <> kasten_id)
);

View File

@ -137,7 +137,7 @@ impl NoteStore {
Ok((vec![Note::from(zettlekasten)], vec![]))
}
pub async fn add_note(&self, note: &NewNote, parent_id: &str, location: i64) -> NoteResult<String> {
pub async fn add_note(&self, note: &NewNote, parent_id: &str, location: Option<i64>) -> NoteResult<String> {
self.insert_note(
note,
&ParentId(parent_id.to_string()),
@ -229,16 +229,18 @@ impl NoteStore {
&self,
note: &NewNote,
parent_id: &ParentId,
location: i64,
location: Option<i64>,
kind: RelationshipKind,
) -> NoteResult<String> {
if location < 0 {
return Err(NoteStoreError::InvalidNoteStructure(
"Add note: A negative position is not valid.".to_string(),
));
}
if let Some(location) = location {
if location < 0 {
return Err(NoteStoreError::InvalidNoteStructure(
"Add note: A negative location is not valid.".to_string(),
));
}
}
if parent_id.is_empty() {
if parent_id.is_empty() {
return Err(NoteStoreError::InvalidNoteStructure(
"Add note: A parent note ID is required.".to_string(),
));
@ -259,10 +261,14 @@ impl NoteStore {
let references = build_references(&note.content);
let mut tx = self.0.begin().await?;
let location = cmp::min(
assert_max_child_location_for_note(&mut tx, parent_id).await? + 1,
location,
);
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 note_id = NoteId(note.id.clone());
insert_note(&mut tx, &note).await?;

View File

@ -198,7 +198,7 @@ pub(crate) struct NoteRelationshipRow {
}
#[derive(Clone, Debug)]
pub(crate) struct NoteRelationship {
pub struct NoteRelationship {
pub parent_id: String,
pub note_id: String,
pub location: i64,
@ -224,7 +224,7 @@ pub(crate) struct KastenRelationshipRow {
}
#[derive(Clone, Debug)]
pub(crate) struct KastenRelationship {
pub struct KastenRelationship {
pub note_id: String,
pub kasten_id: String,
pub kind: KastenRelationshipKind,

View File

@ -13,28 +13,41 @@ mod make_tree;
mod structs;
use nm_store::{NoteStore, NoteStoreError, NewNote};
use crate::structs::Page;
use crate::make_tree::make_tree;
use crate::structs::{Page, Note};
use crate::make_tree::{make_note_tree, make_backreferences};
#[derive(Debug)]
pub struct Notesmachine(pub(crate) NoteStore);
type Result<T> = core::result::Result<T, NoteStoreError>;
pub fn make_page(foundtree: &Note, backreferences: Vec<Vec<Note>>) -> Page {
Page {
slug: foundtree.id,
title: foundtree.content,
creation_date: foundtree.creation_date,
updated_date: foundtree.updated_date,
lastview_date: foundtree.lastview_date,
deleted_date: foundtree.deleted_date,
notes: foundtree.children,
backreferences: backreferences
}
}
impl Notesmachine {
pub async fn new(url: &str) -> Result<Self> {
let notestore = NoteStore::new(url).await?;
Ok(Notesmachine(notestore))
}
pub async fn get_box_via_slug(&self, slug: &str) -> Result<Page> {
let (rawpage, rawnotes) = self.0.get_page_by_slug(slug).await?;
Ok(make_tree(&rawpage, &rawnotes))
pub async fn get_page_via_slug(&self, slug: &str) -> Result<Page> {
let (rawtree, rawbackreferences) = self.0.get_kasten_by_slug(slug).await?;
Ok(make_page(&make_note_tree(&rawtree), make_backreferences(&rawbackreferences)))
}
pub async fn get_box(&self, title: &str) -> Result<Page> {
let (rawpage, rawnotes) = self.0.get_page_by_title(title).await?;
Ok(make_tree(&rawpage, &rawnotes))
pub async fn get_page(&self, title: &str) -> Result<Page> {
let (rawtree, rawbackreferences) = self.0.get_kasten_by_title(title).await?;
Ok(make_page(&make_note_tree(&rawtree), make_backreferences(&rawbackreferences)))
}
// TODO:

View File

@ -1,8 +1,12 @@
use crate::structs::{Note, Page};
use nm_store::{RawNote, RawPage};
use nm_store::NoteKind;
fn make_note_tree(rawnotes: &[RawNote], root: i64) -> Note {
let the_note = rawnotes.iter().find(|note| note.id == root).unwrap().clone();
fn make_note_tree_from(rawnotes: &[nm_store::Note], root_id: &str) -> Note {
let the_note = {
let foundroots: Vec<&nm_store::Note> = rawnotes.iter().filter(|note| note.id == root_id).collect();
debug_assert!(foundroots.len() == 1);
foundroots.iter().next().unwrap().clone()
};
// The special case of the root node must be filtered out here to
// prevent the first pass from smashing the stack in an infinite
@ -11,35 +15,61 @@ fn make_note_tree(rawnotes: &[RawNote], root: i64) -> Note {
// are faster.
let mut children = rawnotes
.iter()
.filter(|note| note.parent_id == root && note.id != root)
.map(|note| make_note_tree(rawnotes, note.id))
.filter(|note| note.parent_id.is_some() && note.parent_id.unwrap() == root_id && note.id != the_note.id)
.map(|note| make_note_tree_from(rawnotes, &note.id))
.collect::<Vec<Note>>();
children.sort_unstable_by(|a, b| a.position.cmp(&b.position));
children.sort_unstable_by(|a, b| a.location.cmp(&b.location));
Note {
uuid: the_note.uuid,
parent_uuid: the_note.parent_uuid,
id: the_note.id,
parent_id: the_note.parent_id,
content: the_note.content,
notetype: the_note.notetype,
position: the_note.position,
kind: the_note.kind.to_string(),
location: the_note.location,
creation_date: the_note.creation_date,
updated_date: the_note.updated_date,
lastview_date: the_note.updated_date,
deleted_date: the_note.deleted_date,
children: vec![],
children: children,
}
}
pub(crate) fn make_tree(rawpage: &RawPage, rawnotes: &[RawNote]) -> Page {
let the_page = rawpage.clone();
pub(crate) fn make_note_tree(rawnotes: &[nm_store::Note]) -> Note {
let the_root = {
let foundroots: Vec<&nm_store::Note> = rawnotes.iter().filter(|note| note.kind == NoteKind::Kasten).collect();
debug_assert!(foundroots.len() == 1);
foundroots.iter().next().unwrap().clone()
};
make_note_tree_from(&rawnotes, &the_root.id)
}
Page {
slug: the_page.slug,
title: the_page.title,
creation_date: the_page.creation_date,
updated_date: the_page.updated_date,
lastview_date: the_page.updated_date,
deleted_date: the_page.deleted_date,
root_note: make_note_tree(rawnotes, rawpage.note_id),
fn add_child(rawnotes: &[nm_store::Note], acc: &mut Vec<Note>, note_id: &str) -> Vec<Note> {
let child = rawnotes
.iter()
.find(|note| note.parent_id.is_some() && note.parent_id.unwrap() == note_id);
if let Some(c) = child {
acc.push(Note {
id: c.id,
parent_id: Some(note_id.to_string()),
content: c.content,
kind: c.kind.to_string(),
location: c.location,
creation_date: c.creation_date,
updated_date: c.updated_date,
lastview_date: c.updated_date,
deleted_date: c.deleted_date,
children: vec![],
});
add_child(rawnotes, acc, &c.id)
} else {
acc.to_vec()
}
}
pub(crate) fn make_backreferences(rawnotes: &[nm_store::Note]) -> Vec<Vec<Note>> {
rawnotes
.iter()
.filter(|note| note.parent_id.is_none() && note.kind == NoteKind::Kasten)
.map(|root| add_child(rawnotes, &mut Vec::<Note>::new(), &root.id))
.collect()
}

View File

@ -3,11 +3,11 @@ use serde::{Deserialize, Serialize};
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct Note {
pub uuid: String,
pub parent_uuid: String,
pub id: String,
pub parent_id: Option<String>,
pub content: String,
pub position: i64,
pub notetype: String,
pub location: i64,
pub kind: String,
pub creation_date: DateTime<Utc>,
pub updated_date: DateTime<Utc>,
pub lastview_date: DateTime<Utc>,
@ -15,7 +15,6 @@ pub struct Note {
pub children: Vec<Note>,
}
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct Page {
pub slug: String,
pub title: String,
@ -23,5 +22,6 @@ pub struct Page {
pub updated_date: DateTime<Utc>,
pub lastview_date: DateTime<Utc>,
pub deleted_date: Option<DateTime<Utc>>,
pub root_note: Note,
pub notes: Vec<Note>,
pub backreferences: Vec<Vec<Note>>,
}