mirror of
https://github.com/narrrl/narl-workspace.git
synced 2026-11-03 16:41:27 +01:00
search: a file is found by what it says
The text of every file in the tree that is text, by its type or by its first 8 KiB reading as UTF-8 without a NUL, now goes into `file_text` and a second FTS5 index beside the documents', up to its first 256 KiB, the same part the viewer shows. A search asks both, lists a node found in both once, and marks a file found by its text with `in_text`; the list shows the words around the match under its name. There is one way in, `file_text::catch_up`, which reads every unsealed file that has no row yet. An upload, an import, an SFTP save and a decrypt call it once their own transaction is done, and the server runs it at start for anything that slipped past. A file that is not text gets an empty row, so it is read once and not again. The triggers keep it honest the other way. New bytes drop the old text, so an SFTP save is read again, and `:encrypt` writes a file under a new media id, so its plaintext is gone in the same statement that seals it. The text of a sealed file is refused outright, whatever path tries to store it; otherwise it would sit in `diary.db` and its mirror beside the sealed bytes. PDFs and office documents are not read: their text needs a parser this binary does not carry.
This commit is contained in:
1 parent
6673298cb2
commit
909abf5fcb
13 files changed
+476
-14
No files matched your search
@@ -0,0 +1,48 @@
|
||||
-- The text of the files in the tree, so a search finds a sentence in a `.txt`
|
||||
-- or a `.csv` the way it finds one in a document. One row per file node, read
|
||||
-- from the bytes `media_id` names; a file that is not text has a row with no
|
||||
-- text, so it is read once and not again on every pass.
|
||||
CREATE TABLE file_text (
|
||||
node_id INTEGER PRIMARY KEY REFERENCES nodes (id) ON DELETE CASCADE,
|
||||
media_id TEXT NOT NULL,
|
||||
text TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE VIRTUAL TABLE files_fts USING fts5 (
|
||||
text, content = 'file_text', content_rowid = 'node_id', tokenize = 'unicode61'
|
||||
);
|
||||
|
||||
CREATE TRIGGER file_text_ai AFTER INSERT ON file_text BEGIN
|
||||
INSERT INTO files_fts (rowid, text) VALUES (new.node_id, new.text);
|
||||
END;
|
||||
CREATE TRIGGER file_text_ad AFTER DELETE ON file_text BEGIN
|
||||
INSERT INTO files_fts (files_fts, rowid, text) VALUES ('delete', old.node_id, old.text);
|
||||
END;
|
||||
CREATE TRIGGER file_text_au AFTER UPDATE ON file_text BEGIN
|
||||
INSERT INTO files_fts (files_fts, rowid, text) VALUES ('delete', old.node_id, old.text);
|
||||
INSERT INTO files_fts (rowid, text) VALUES (new.node_id, new.text);
|
||||
END;
|
||||
|
||||
-- New bytes make the old text wrong: an SFTP save, and `:encrypt`, which
|
||||
-- re-writes a file under a new media id. Dropped in the same statement, so
|
||||
-- the plaintext of a file being sealed is gone the moment it is sealed.
|
||||
CREATE TRIGGER file_text_stale AFTER UPDATE OF media_id ON nodes
|
||||
WHEN new.media_id IS NOT old.media_id
|
||||
BEGIN
|
||||
DELETE FROM file_text WHERE node_id = new.id;
|
||||
END;
|
||||
|
||||
CREATE TRIGGER file_text_sealed_media AFTER UPDATE OF encrypted ON media
|
||||
WHEN new.encrypted <> 0
|
||||
BEGIN
|
||||
DELETE FROM file_text WHERE media_id = new.id;
|
||||
END;
|
||||
|
||||
-- The last word, as in 0015: the text of a sealed file is never stored,
|
||||
-- whatever path tries. Otherwise it would sit in `diary.db` and its mirror in
|
||||
-- the clear while the bytes are sealed.
|
||||
CREATE TRIGGER file_text_never_sealed BEFORE INSERT ON file_text
|
||||
WHEN (SELECT encrypted FROM media WHERE id = new.media_id) IS NOT 0
|
||||
BEGIN
|
||||
SELECT RAISE(ABORT, 'a sealed file has no text in the index');
|
||||
END;
|
||||
Reference in new issue
Block a user