feat(ui): search operators from: and before:/after: (LC-280) #318

Merged
longjacksonle merged 1 commit from feat/lc-280-search-operators into main 2026-06-14 17:50:25 +02:00

What

Message search now understands typed operators that refine a text query (LC-280):

  • from:<username> - only that author's messages
  • before:<YYYY-MM-DD> / after:<YYYY-MM-DD> - date-bounded

They combine with the free text and with the existing room-scoped (LC-268) and enclave scopes, e.g. from:alice before:2024-06-01 deploy.

How

  • DB: new SearchFilters { author_id, before, after } + search_messages_filtered(...) which appends AND m.user_id = ?, AND m.created_at < ?, AND m.created_at >= ? (and their binds, last, in that order) to the existing query. search_messages stays a thin shim delegating with default filters, so all existing callers/tests are untouched (the bind order is positional and brittle - this keeps it intact).
  • Route (routes::search): parse_operators pulls from:/before:/after: tokens out of the query; dates are validated with chrono and an invalid value is left as plain text (no silent filtering). from:<name> resolves via find_user_by_username (an unknown user yields no results); the remaining text goes through the unchanged sanitize_fts_query path. Filters only narrow within the caller's already-access-scoped set - no new disclosure.

Scope notes: in:<room> was dropped (room names are not unique across enclaves, and the LC-268 room-header search box already covers "this room"); a pure-operator query with no free text collapses the popover, matching today's empty-query behavior (the FTS path requires a MATCH term). No UI/i18n change - operators are typed by power users.

No new DB column or env. Not operator-visible.

Tests

  • db_search: author filter returns only that author's hits; before/after bound by date.
  • routes_search_room: from:bob needle returns only bob's hit; an unknown from: returns no results.
  • routes::search unit tests: operator parsing + YYYY-MM-DD validation.

just test and just test-saas pass.

QA note

Mostly server-side and test-covered. Worth a quick manual check in the sidebar message search: from:<someone> <term> narrows to their messages; before:2020-01-01 <term> returns nothing recent; an invalid date like before:soon <term> just searches the text; plain text and the room-header search are unchanged. (No placeholder hint yet - operators are undocumented in the UI for now.)

## What Message search now understands typed operators that refine a text query (LC-280): - `from:<username>` - only that author's messages - `before:<YYYY-MM-DD>` / `after:<YYYY-MM-DD>` - date-bounded They combine with the free text and with the existing room-scoped (LC-268) and enclave scopes, e.g. `from:alice before:2024-06-01 deploy`. ## How - DB: new `SearchFilters { author_id, before, after }` + `search_messages_filtered(...)` which appends `AND m.user_id = ?`, `AND m.created_at < ?`, `AND m.created_at >= ?` (and their binds, last, in that order) to the existing query. `search_messages` stays a thin shim delegating with default filters, so all existing callers/tests are untouched (the bind order is positional and brittle - this keeps it intact). - Route (`routes::search`): `parse_operators` pulls `from:`/`before:`/`after:` tokens out of the query; dates are validated with chrono and an invalid value is left as plain text (no silent filtering). `from:<name>` resolves via `find_user_by_username` (an unknown user yields no results); the remaining text goes through the unchanged `sanitize_fts_query` path. Filters only narrow within the caller's already-access-scoped set - no new disclosure. Scope notes: `in:<room>` was dropped (room names are not unique across enclaves, and the LC-268 room-header search box already covers "this room"); a pure-operator query with no free text collapses the popover, matching today's empty-query behavior (the FTS path requires a MATCH term). No UI/i18n change - operators are typed by power users. No new DB column or env. Not operator-visible. ## Tests - `db_search`: author filter returns only that author's hits; `before`/`after` bound by date. - `routes_search_room`: `from:bob needle` returns only bob's hit; an unknown `from:` returns no results. - `routes::search` unit tests: operator parsing + `YYYY-MM-DD` validation. `just test` and `just test-saas` pass. ## QA note Mostly server-side and test-covered. Worth a quick manual check in the sidebar message search: `from:<someone> <term>` narrows to their messages; `before:2020-01-01 <term>` returns nothing recent; an invalid date like `before:soon <term>` just searches the text; plain text and the room-header search are unchanged. (No placeholder hint yet - operators are undocumented in the UI for now.)
feat(ui): search operators from: and before:/after: (LC-280)
All checks were successful
check-secrets / Nosey parker (push) Successful in 4s
check-secrets / Kingfisher (push) Successful in 5s
check-secrets / TruffleHog (push) Successful in 5s
check-secrets / Nosey parker (pull_request) Successful in 4s
check-secrets / TruffleHog (pull_request) Successful in 6s
check-secrets / Kingfisher (pull_request) Successful in 5s
Check / clippy + fmt + tests (pull_request) Successful in 2m44s
Create release / Create release from merged PR (pull_request) Has been skipped
ac257f3f1d
Message search now understands typed operators that refine a text query: from:<username> (only that author's messages) and before:/after:<YYYY-MM-DD> (date-bounded). They combine with the free text and with the existing room-scoped (LC-268) and enclave scopes.

Adds db::chat::SearchFilters + search_messages_filtered, which appends author/date clauses (and their binds, last, in order) to the existing query; search_messages stays as a thin shim so all existing callers are untouched. The route parses operator tokens out of the query (dates validated via chrono; invalid date values stay as plain text rather than silently filtering), resolves from:<name> via find_user_by_username (an unknown user yields no results), and passes the remaining text through the unchanged FTS path. Filters only narrow within the caller's already-access-scoped result set, so there is no new disclosure.

Scope notes: in:<room> was dropped (room names are not unique and the LC-268 room-search box already covers it); a pure-operator query with no text collapses the popover, matching today's empty-query behavior (the FTS path needs a MATCH term). No UI/i18n change - operators are typed.

Tests: db-level author + date-range filtering, route-level from: (filters by author; unknown user empty), and parse_operators unit tests. just test and just test-saas pass.

#LC-280
#LC-281

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
longjacksonle deleted branch feat/lc-280-search-operators 2026-06-14 17:50:26 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/lets-chat!318
No description provided.