89 lines
3.9 KiB
SQL
89 lines
3.9 KiB
SQL
-- Notes.
|
|||
|
|
--
|
||
|
|
-- Markdown in a column, not rich text and not a format of our own. Export was a
|
||
|
|
-- requirement before a line was written, and if the stored form *is* the exported
|
||
|
|
-- form there is no conversion left to lose anything in — a note stays readable by
|
||
|
|
-- any editor on the day this program is gone. It is also what makes full-text
|
||
|
|
-- search and anything an AI does over notes cheap: both read text.
|
||
|
|
--
|
||
|
|
-- Under a project rather than in a folder tree of its own. Projects, groups and
|
||
|
|
-- labels already exist and are already shared, already have roles, already have a
|
||
|
|
-- trash. A second hierarchy to file things in is one too many, and a note that
|
||
|
|
-- belongs to a project inherits every one of those answers instead of needing its
|
||
|
|
-- own. A note with no project is loose, the way a task in the inbox is.
|
||
|
|
--
|
||
|
|
-- `deleted_at` and `created_by` follow tasks exactly, including SET NULL: an
|
||
|
|
-- account can be removed without taking somebody else's notes with it.
|
||
|
|
CREATE TABLE notes (
|
||
|
|
id TEXT PRIMARY KEY,
|
||
|
|
project_id TEXT REFERENCES projects (id) ON DELETE CASCADE,
|
||
|
|
title TEXT NOT NULL DEFAULT '',
|
||
|
|
body TEXT NOT NULL DEFAULT '',
|
||
|
|
created_by TEXT REFERENCES users (id) ON DELETE SET NULL,
|
||
|
|
pinned INTEGER NOT NULL DEFAULT 0,
|
||
|
|
created_at INTEGER NOT NULL,
|
||
|
|
updated_at INTEGER NOT NULL,
|
||
|
|
deleted_at INTEGER,
|
||
|
|
|
||
|
|
-- The same Danish transliteration as tasks carry, and for the same reason:
|
||
|
|
-- unicode61 folds diacritics, so "grøn" is findable as "gron", but it does
|
||
|
|
-- nothing for æ→ae or å→aa, which is how Danes actually type when the keyboard
|
||
|
|
-- is not theirs.
|
||
|
|
fold TEXT GENERATED ALWAYS AS (
|
||
|
|
replace(replace(replace(replace(replace(replace(
|
||
|
|
title || ' ' || body,
|
||
|
|
'ø', 'o'), 'Ø', 'o'),
|
||
|
|
'æ', 'ae'), 'Æ', 'ae'),
|
||
|
|
'å', 'aa'), 'Å', 'aa')
|
||
|
|
) STORED
|
||
|
|
);
|
||
|
|
|
||
|
|
CREATE INDEX notes_by_project ON notes (project_id, deleted_at, updated_at DESC);
|
||
|
|
|
||
|
|
-- What a note points at: a task, another note, a project.
|
||
|
|
--
|
||
|
|
-- Derived from the body every time it is saved, never edited directly. The text
|
||
|
|
-- stays the truth; this is an index over it — which is what makes the question
|
||
|
|
-- worth asking backwards. "What refers to this task" must not mean searching every
|
||
|
|
-- note's prose, or the panel that shows it would be too slow to leave open.
|
||
|
|
--
|
||
|
|
-- `target_id` is deliberately not a foreign key. A link to something that has been
|
||
|
|
-- deleted is a fact about the note, and it should read as a dead link rather than
|
||
|
|
-- vanish or block the delete.
|
||
|
|
CREATE TABLE note_links (
|
||
|
|
note_id TEXT NOT NULL REFERENCES notes (id) ON DELETE CASCADE,
|
||
|
|
kind TEXT NOT NULL CHECK (kind IN ('task', 'note', 'project')),
|
||
|
|
target_id TEXT NOT NULL,
|
||
|
|
PRIMARY KEY (note_id, kind, target_id)
|
||
|
|
);
|
||
|
|
|
||
|
|
CREATE INDEX note_links_backwards ON note_links (kind, target_id);
|
||
|
|
|
||
|
|
CREATE VIRTUAL TABLE notes_fts USING fts5 (
|
||
|
|
title,
|
||
|
|
body,
|
||
|
|
fold,
|
||
|
|
id UNINDEXED,
|
||
|
|
project_id UNINDEXED,
|
||
|
|
content = 'notes',
|
||
|
|
content_rowid = 'rowid',
|
||
|
|
tokenize = 'unicode61 remove_diacritics 2'
|
||
|
|
);
|
||
|
|
|
||
|
|
CREATE TRIGGER notes_fts_insert AFTER INSERT ON notes BEGIN
|
||
|
|
INSERT INTO notes_fts (rowid, title, body, fold, id, project_id)
|
||
|
|
VALUES (new.rowid, new.title, new.body, new.fold, new.id, new.project_id);
|
||
|
|
END;
|
||
|
|
|
||
|
|
CREATE TRIGGER notes_fts_delete AFTER DELETE ON notes BEGIN
|
||
|
|
INSERT INTO notes_fts (notes_fts, rowid, title, body, fold, id, project_id)
|
||
|
|
VALUES ('delete', old.rowid, old.title, old.body, old.fold, old.id, old.project_id);
|
||
|
|
END;
|
||
|
|
|
||
|
|
CREATE TRIGGER notes_fts_update AFTER UPDATE ON notes BEGIN
|
||
|
|
INSERT INTO notes_fts (notes_fts, rowid, title, body, fold, id, project_id)
|
||
|
|
VALUES ('delete', old.rowid, old.title, old.body, old.fold, old.id, old.project_id);
|
||
|
|
INSERT INTO notes_fts (rowid, title, body, fold, id, project_id)
|
||
|
|
VALUES (new.rowid, new.title, new.body, new.fold, new.id, new.project_id);
|
||
|
|
END;
|