/** * ───────────────────────────────────────────── * SPB Drive — Personal Cloud Drive * ───────────────────────────────────────────── * Author : Simon-Pierre Boucher * Contact : contact@spboucher.ai * File : src/search/index-sync.mjs * Purpose : Keep the FTS5 index in sync with node names/tags/content * License : MIT © Simon-Pierre Boucher * ───────────────────────────────────────────── */ import { getDb } from '../db/db.mjs'; /** Current tag names for a node, space-joined for the FTS tags column. */ function tagsText(nodeId) { return getDb() .prepare( `SELECT GROUP_CONCAT(t.name, ' ') AS tags FROM node_tags nt JOIN tags t ON t.id = nt.tag_id WHERE nt.node_id = ?`, ) .get(nodeId)?.tags ?? ''; } /** Insert or refresh the FTS row for a node's name (keeps existing content). */ export function upsertFtsName(nodeId, name) { const db = getDb(); const existing = db.prepare('SELECT content FROM fts WHERE rowid = ?').get(nodeId); db.prepare('DELETE FROM fts WHERE rowid = ?').run(nodeId); db.prepare('INSERT INTO fts (rowid, name, tags, content) VALUES (?, ?, ?, ?)').run( nodeId, name, tagsText(nodeId), existing?.content ?? '', ); } /** Refresh only the tags column for a node. */ export function updateFtsTags(nodeId) { const db = getDb(); const node = db.prepare('SELECT name FROM nodes WHERE id = ?').get(nodeId); if (node) upsertFtsName(nodeId, node.name); } /** Store extracted text content for a node (search-inside-files). */ export function setFtsContent(nodeId, content, contentSha) { const db = getDb(); const node = db.prepare('SELECT name FROM nodes WHERE id = ?').get(nodeId); if (!node) return; db.prepare('DELETE FROM fts WHERE rowid = ?').run(nodeId); db.prepare('INSERT INTO fts (rowid, name, tags, content) VALUES (?, ?, ?, ?)').run( nodeId, node.name, tagsText(nodeId), (content ?? '').slice(0, 1_000_000), ); db.prepare( `INSERT INTO fts_state (node_id, content_sha) VALUES (?, ?) ON CONFLICT(node_id) DO UPDATE SET content_sha = excluded.content_sha`, ).run(nodeId, contentSha ?? null); } /** Remove a node from the index entirely. */ export function deleteFtsEntry(nodeId) { const db = getDb(); db.prepare('DELETE FROM fts WHERE rowid = ?').run(nodeId); db.prepare('DELETE FROM fts_state WHERE node_id = ?').run(nodeId); }