Refinements to the tree structure.
This commit is contained in:
parent
4776541df4
commit
83d5858a45
|
@ -0,0 +1,2 @@
|
||||||
|
[ ] Add RelationshipKind to Notes passed out
|
||||||
|
[ ] Add KastenKind to Backreferences passed out
|
|
@ -6,7 +6,8 @@ mod structs;
|
||||||
|
|
||||||
pub use crate::errors::NoteStoreError;
|
pub use crate::errors::NoteStoreError;
|
||||||
pub use crate::store::NoteStore;
|
pub use crate::store::NoteStore;
|
||||||
pub use crate::structs::{Note, NoteKind};
|
pub use crate::structs::{Note, NoteKind, NoteRelationship, KastenRelationship};
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
@ -81,22 +82,22 @@ mod tests {
|
||||||
// <- 2 <- 4
|
// <- 2 <- 4
|
||||||
|
|
||||||
let note1 = make_new_note("1");
|
let note1 = make_new_note("1");
|
||||||
let note1_id = storagepool.add_note(¬e1, &root.id, 0).await;
|
let note1_id = storagepool.add_note(¬e1, &root.id, Some(0)).await;
|
||||||
assert!(note1_id.is_ok(), "{:?}", note1_id);
|
assert!(note1_id.is_ok(), "{:?}", note1_id);
|
||||||
let note1_id = note1_id.unwrap();
|
let note1_id = note1_id.unwrap();
|
||||||
|
|
||||||
let note2 = make_new_note("2");
|
let note2 = make_new_note("2");
|
||||||
let note2_id = storagepool.add_note(¬e2, &root.id, 0).await;
|
let note2_id = storagepool.add_note(¬e2, &root.id, Some(0)).await;
|
||||||
assert!(note2_id.is_ok(), "{:?}", note2_id);
|
assert!(note2_id.is_ok(), "{:?}", note2_id);
|
||||||
let note2_id = note2_id.unwrap();
|
let note2_id = note2_id.unwrap();
|
||||||
|
|
||||||
let note3 = make_new_note("3");
|
let note3 = make_new_note("3");
|
||||||
let note3_id = storagepool.add_note(¬e3, ¬e1_id, 0).await;
|
let note3_id = storagepool.add_note(¬e3, ¬e1_id, Some(0)).await;
|
||||||
assert!(note3_id.is_ok(), "{:?}", note3_id);
|
assert!(note3_id.is_ok(), "{:?}", note3_id);
|
||||||
let _note3_id = note3_id.unwrap();
|
let _note3_id = note3_id.unwrap();
|
||||||
|
|
||||||
let note4 = make_new_note("4");
|
let note4 = make_new_note("4");
|
||||||
let note4_id = storagepool.add_note(¬e4, ¬e2_id, 0).await;
|
let note4_id = storagepool.add_note(¬e4, ¬e2_id, Some(0)).await;
|
||||||
assert!(note4_id.is_ok(), "{:?}", note4_id);
|
assert!(note4_id.is_ok(), "{:?}", note4_id);
|
||||||
let _note4_id = note4_id.unwrap();
|
let _note4_id = note4_id.unwrap();
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ CREATE TABLE notes (
|
||||||
CREATE INDEX note_ids ON notes (id);
|
CREATE INDEX note_ids ON notes (id);
|
||||||
|
|
||||||
CREATE TABLE favorites (
|
CREATE TABLE favorites (
|
||||||
id TEXT NOT NULL,
|
id TEXT NOT NULL UNIQUE,
|
||||||
location INTEGER NOT NULL,
|
location INTEGER NOT NULL,
|
||||||
FOREIGN KEY (id) REFERENCES notes (id) ON DELETE CASCADE
|
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.
|
-- 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 (note_id) REFERENCES notes (id) ON DELETE CASCADE,
|
||||||
FOREIGN KEY (parent_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)
|
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.
|
-- 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 (note_id) REFERENCES notes (id) ON DELETE CASCADE,
|
||||||
FOREIGN KEY (kasten_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)
|
CHECK (note_id <> kasten_id)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -137,7 +137,7 @@ impl NoteStore {
|
||||||
Ok((vec![Note::from(zettlekasten)], vec![]))
|
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(
|
self.insert_note(
|
||||||
note,
|
note,
|
||||||
&ParentId(parent_id.to_string()),
|
&ParentId(parent_id.to_string()),
|
||||||
|
@ -229,14 +229,16 @@ impl NoteStore {
|
||||||
&self,
|
&self,
|
||||||
note: &NewNote,
|
note: &NewNote,
|
||||||
parent_id: &ParentId,
|
parent_id: &ParentId,
|
||||||
location: i64,
|
location: Option<i64>,
|
||||||
kind: RelationshipKind,
|
kind: RelationshipKind,
|
||||||
) -> NoteResult<String> {
|
) -> NoteResult<String> {
|
||||||
|
if let Some(location) = location {
|
||||||
if location < 0 {
|
if location < 0 {
|
||||||
return Err(NoteStoreError::InvalidNoteStructure(
|
return Err(NoteStoreError::InvalidNoteStructure(
|
||||||
"Add note: A negative position is not valid.".to_string(),
|
"Add note: A negative location is not valid.".to_string(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if parent_id.is_empty() {
|
if parent_id.is_empty() {
|
||||||
return Err(NoteStoreError::InvalidNoteStructure(
|
return Err(NoteStoreError::InvalidNoteStructure(
|
||||||
|
@ -259,10 +261,14 @@ impl NoteStore {
|
||||||
let references = build_references(¬e.content);
|
let references = build_references(¬e.content);
|
||||||
|
|
||||||
let mut tx = self.0.begin().await?;
|
let mut tx = self.0.begin().await?;
|
||||||
let location = cmp::min(
|
let location = {
|
||||||
assert_max_child_location_for_note(&mut tx, parent_id).await? + 1,
|
let max_child = assert_max_child_location_for_note(&mut tx, parent_id).await? + 1;
|
||||||
location,
|
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, ¬e).await?;
|
insert_note(&mut tx, ¬e).await?;
|
||||||
|
|
|
@ -198,7 +198,7 @@ pub(crate) struct NoteRelationshipRow {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub(crate) struct NoteRelationship {
|
pub struct NoteRelationship {
|
||||||
pub parent_id: String,
|
pub parent_id: String,
|
||||||
pub note_id: String,
|
pub note_id: String,
|
||||||
pub location: i64,
|
pub location: i64,
|
||||||
|
@ -224,7 +224,7 @@ pub(crate) struct KastenRelationshipRow {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub(crate) struct KastenRelationship {
|
pub struct KastenRelationship {
|
||||||
pub note_id: String,
|
pub note_id: String,
|
||||||
pub kasten_id: String,
|
pub kasten_id: String,
|
||||||
pub kind: KastenRelationshipKind,
|
pub kind: KastenRelationshipKind,
|
||||||
|
|
|
@ -13,28 +13,41 @@ mod make_tree;
|
||||||
mod structs;
|
mod structs;
|
||||||
|
|
||||||
use nm_store::{NoteStore, NoteStoreError, NewNote};
|
use nm_store::{NoteStore, NoteStoreError, NewNote};
|
||||||
use crate::structs::Page;
|
use crate::structs::{Page, Note};
|
||||||
use crate::make_tree::make_tree;
|
use crate::make_tree::{make_note_tree, make_backreferences};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Notesmachine(pub(crate) NoteStore);
|
pub struct Notesmachine(pub(crate) NoteStore);
|
||||||
|
|
||||||
type Result<T> = core::result::Result<T, NoteStoreError>;
|
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 {
|
impl Notesmachine {
|
||||||
pub async fn new(url: &str) -> Result<Self> {
|
pub async fn new(url: &str) -> Result<Self> {
|
||||||
let notestore = NoteStore::new(url).await?;
|
let notestore = NoteStore::new(url).await?;
|
||||||
Ok(Notesmachine(notestore))
|
Ok(Notesmachine(notestore))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_box_via_slug(&self, slug: &str) -> Result<Page> {
|
pub async fn get_page_via_slug(&self, slug: &str) -> Result<Page> {
|
||||||
let (rawpage, rawnotes) = self.0.get_page_by_slug(slug).await?;
|
let (rawtree, rawbackreferences) = self.0.get_kasten_by_slug(slug).await?;
|
||||||
Ok(make_tree(&rawpage, &rawnotes))
|
Ok(make_page(&make_note_tree(&rawtree), make_backreferences(&rawbackreferences)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_box(&self, title: &str) -> Result<Page> {
|
pub async fn get_page(&self, title: &str) -> Result<Page> {
|
||||||
let (rawpage, rawnotes) = self.0.get_page_by_title(title).await?;
|
let (rawtree, rawbackreferences) = self.0.get_kasten_by_title(title).await?;
|
||||||
Ok(make_tree(&rawpage, &rawnotes))
|
Ok(make_page(&make_note_tree(&rawtree), make_backreferences(&rawbackreferences)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO:
|
// TODO:
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
use crate::structs::{Note, Page};
|
use crate::structs::{Note, Page};
|
||||||
use nm_store::{RawNote, RawPage};
|
use nm_store::NoteKind;
|
||||||
|
|
||||||
fn make_note_tree(rawnotes: &[RawNote], root: i64) -> Note {
|
fn make_note_tree_from(rawnotes: &[nm_store::Note], root_id: &str) -> Note {
|
||||||
let the_note = rawnotes.iter().find(|note| note.id == root).unwrap().clone();
|
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
|
// The special case of the root node must be filtered out here to
|
||||||
// prevent the first pass from smashing the stack in an infinite
|
// 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.
|
// are faster.
|
||||||
let mut children = rawnotes
|
let mut children = rawnotes
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|note| note.parent_id == root && note.id != root)
|
.filter(|note| note.parent_id.is_some() && note.parent_id.unwrap() == root_id && note.id != the_note.id)
|
||||||
.map(|note| make_note_tree(rawnotes, note.id))
|
.map(|note| make_note_tree_from(rawnotes, ¬e.id))
|
||||||
.collect::<Vec<Note>>();
|
.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 {
|
Note {
|
||||||
uuid: the_note.uuid,
|
id: the_note.id,
|
||||||
parent_uuid: the_note.parent_uuid,
|
parent_id: the_note.parent_id,
|
||||||
content: the_note.content,
|
content: the_note.content,
|
||||||
notetype: the_note.notetype,
|
kind: the_note.kind.to_string(),
|
||||||
position: the_note.position,
|
location: the_note.location,
|
||||||
creation_date: the_note.creation_date,
|
creation_date: the_note.creation_date,
|
||||||
updated_date: the_note.updated_date,
|
updated_date: the_note.updated_date,
|
||||||
lastview_date: the_note.updated_date,
|
lastview_date: the_note.updated_date,
|
||||||
deleted_date: the_note.deleted_date,
|
deleted_date: the_note.deleted_date,
|
||||||
|
children: children,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
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![],
|
children: vec![],
|
||||||
|
});
|
||||||
|
add_child(rawnotes, acc, &c.id)
|
||||||
|
} else {
|
||||||
|
acc.to_vec()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn make_tree(rawpage: &RawPage, rawnotes: &[RawNote]) -> Page {
|
pub(crate) fn make_backreferences(rawnotes: &[nm_store::Note]) -> Vec<Vec<Note>> {
|
||||||
let the_page = rawpage.clone();
|
rawnotes
|
||||||
|
.iter()
|
||||||
Page {
|
.filter(|note| note.parent_id.is_none() && note.kind == NoteKind::Kasten)
|
||||||
slug: the_page.slug,
|
.map(|root| add_child(rawnotes, &mut Vec::<Note>::new(), &root.id))
|
||||||
title: the_page.title,
|
.collect()
|
||||||
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),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,11 +3,11 @@ use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Clone, Serialize, Deserialize, Debug)]
|
#[derive(Clone, Serialize, Deserialize, Debug)]
|
||||||
pub struct Note {
|
pub struct Note {
|
||||||
pub uuid: String,
|
pub id: String,
|
||||||
pub parent_uuid: String,
|
pub parent_id: Option<String>,
|
||||||
pub content: String,
|
pub content: String,
|
||||||
pub position: i64,
|
pub location: i64,
|
||||||
pub notetype: String,
|
pub kind: String,
|
||||||
pub creation_date: DateTime<Utc>,
|
pub creation_date: DateTime<Utc>,
|
||||||
pub updated_date: DateTime<Utc>,
|
pub updated_date: DateTime<Utc>,
|
||||||
pub lastview_date: DateTime<Utc>,
|
pub lastview_date: DateTime<Utc>,
|
||||||
|
@ -15,7 +15,6 @@ pub struct Note {
|
||||||
pub children: Vec<Note>,
|
pub children: Vec<Note>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Serialize, Deserialize, Debug)]
|
|
||||||
pub struct Page {
|
pub struct Page {
|
||||||
pub slug: String,
|
pub slug: String,
|
||||||
pub title: String,
|
pub title: String,
|
||||||
|
@ -23,5 +22,6 @@ pub struct Page {
|
||||||
pub updated_date: DateTime<Utc>,
|
pub updated_date: DateTime<Utc>,
|
||||||
pub lastview_date: DateTime<Utc>,
|
pub lastview_date: DateTime<Utc>,
|
||||||
pub deleted_date: Option<DateTime<Utc>>,
|
pub deleted_date: Option<DateTime<Utc>>,
|
||||||
pub root_note: Note,
|
pub notes: Vec<Note>,
|
||||||
|
pub backreferences: Vec<Vec<Note>>,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue