diff --git a/desktop/src-tauri/src/commands/local.rs b/desktop/src-tauri/src/commands/local.rs index 36f108a..e2e4a29 100644 --- a/desktop/src-tauri/src/commands/local.rs +++ b/desktop/src-tauri/src/commands/local.rs @@ -190,12 +190,6 @@ pub fn notes_titles(db: State<'_, Db>) -> Result, String> { store::titles(&conn).map_err(|e| e.to_string()) } -#[tauri::command] -pub fn notes_search(q: String, db: State<'_, Db>) -> Result, String> { - let conn = db.0.lock().map_err(|e| e.to_string())?; - store::search(&conn, &q).map_err(|e| e.to_string()) -} - #[tauri::command] pub fn labels_list(db: State<'_, Db>) -> Result, String> { let conn = db.0.lock().map_err(|e| e.to_string())?; diff --git a/desktop/src-tauri/src/lib.rs b/desktop/src-tauri/src/lib.rs index 0f7ca81..51dd52f 100644 --- a/desktop/src-tauri/src/lib.rs +++ b/desktop/src-tauri/src/lib.rs @@ -119,7 +119,6 @@ pub fn run() { commands::local::notes_restore_revision, commands::local::notes_reminders, commands::local::notes_titles, - commands::local::notes_search, commands::local::labels_list, commands::local::labels_create, commands::local::labels_rename, diff --git a/frontend/src/adapters/local.ts b/frontend/src/adapters/local.ts index ed414af..64142a9 100644 --- a/frontend/src/adapters/local.ts +++ b/frontend/src/adapters/local.ts @@ -6,7 +6,7 @@ // parameters (e.g. labelIds -> label_ids). A few operations have no offline meaning // yet (account auth, device linking, attachment upload, URL unfurl, file import) — // those reject with a clear message rather than silently failing; the board, editor, -// capture, search, filters, labels, checklists and reminders all work fully offline. +// capture, filters, labels, checklists and reminders all work fully offline. import { invoke } from "../desktop/bridge"; import type { Note, NoteRevision } from "../stores/notes"; @@ -71,7 +71,6 @@ export const local: Repo = { restoreRevision: (id, revId) => invoke("notes_restore_revision", { id, revId }), reminders: () => invoke("notes_reminders"), titles: () => invoke("notes_titles"), - search: (q) => invoke("notes_search", { q }), }, savedFilters: { diff --git a/frontend/src/adapters/repo.ts b/frontend/src/adapters/repo.ts index bdc3ca3..3573952 100644 --- a/frontend/src/adapters/repo.ts +++ b/frontend/src/adapters/repo.ts @@ -111,7 +111,6 @@ export interface NotesRepo { restoreRevision(id: string, revId: string): Promise; reminders(): Promise; titles(): Promise; - search(q: string): Promise; } export interface SavedFiltersRepo { diff --git a/frontend/src/adapters/rest.ts b/frontend/src/adapters/rest.ts index 4b161a5..12efa73 100644 --- a/frontend/src/adapters/rest.ts +++ b/frontend/src/adapters/rest.ts @@ -99,7 +99,6 @@ export const rest: Repo = { restoreRevision: (id, revId) => api.post(`/api/notes/${id}/revisions/${revId}/restore`), reminders: async () => (await api.get<{ notes: Note[] }>("/api/notes/reminders")).notes, titles: async () => (await api.get<{ titles: TitleEntry[] }>("/api/notes/titles")).titles, - search: async (q) => (await api.get<{ notes: Note[] }>(`/api/notes/search?q=${encodeURIComponent(q)}`)).notes, }, savedFilters: { diff --git a/frontend/src/components/AppShell.vue b/frontend/src/components/AppShell.vue index 0932fee..9db9c1b 100644 --- a/frontend/src/components/AppShell.vue +++ b/frontend/src/components/AppShell.vue @@ -1,6 +1,6 @@ - - diff --git a/src/thoughtsync/notes/__init__.py b/src/thoughtsync/notes/__init__.py index 719a49f..82f8490 100644 --- a/src/thoughtsync/notes/__init__.py +++ b/src/thoughtsync/notes/__init__.py @@ -141,8 +141,9 @@ async def list_notes(): stmt = stmt.where(Note.created_at < before_dt) if query_text: # Full-text match over the note's name + body (generated tsvector, - # migrations 0005/0026), ranked — so the facet bar's text box searches, - # not just filters. + # migrations 0005/0026), ranked. This is the ONLY text search now: the + # separate facet-less `/search` route was removed because landing on it + # was the one place you could not also narrow by tag (note 2930). tsquery = func.websearch_to_tsquery("english", query_text) search_col = literal_column("notes.search_vector") stmt = stmt.where(search_col.op("@@")(tsquery)).order_by( @@ -156,30 +157,6 @@ async def list_notes(): return jsonify({"notes": await _serialize_notes(db, notes)}) -@bp.get("/search") -@login_required -async def search_notes(): - q = (request.args.get("q") or "").strip() - if not q: - return jsonify({"notes": []}) - async with session_scope() as db: - tsquery = func.websearch_to_tsquery("english", q) - # search_vector is a generated column (migration 0005), not mapped on the ORM. - search_col = literal_column("notes.search_vector") - stmt = ( - select(Note) - .where( - visible_to_user("note", Note.owner_id, Note.id, g.user_id), - Note.deleted_at.is_(None), - search_col.op("@@")(tsquery), - ) - .order_by(func.ts_rank(search_col, tsquery).desc(), Note.updated_at.desc()) - .limit(100) - ) - notes = (await db.scalars(stmt)).all() - return jsonify({"notes": await _serialize_notes(db, notes)}) - - @bp.get("/reminders") @login_required async def list_reminders(): diff --git a/tests/test_notes.py b/tests/test_notes.py index 33fa6d7..30b9181 100644 --- a/tests/test_notes.py +++ b/tests/test_notes.py @@ -37,7 +37,7 @@ def test_all_note_routes_registered(app): expected = { f"notes.{name}" for name in ( - "list_notes", "search_notes", "list_reminders", "complete_reminder", + "list_notes", "list_reminders", "complete_reminder", "snooze_reminder", "export_notes", "import_notes", "list_titles", "reorder_notes", "create_note", "get_note", "update_note", "list_revisions", "restore_revision",