feat(search): has:file / has:link / in:thread operators (LC-530) #503

Merged
longjacksonle merged 3 commits from feat/LC-530-search-filters into main 2026-07-03 03:38:50 +02:00

Adds three search operators - has:file, has:link, in:thread - to the room/enclave/home search box, filling the concrete gap left after LC-280 (which added from:/before:/after:).

has:file narrows to messages with an attachment (an EXISTS over file_uploads), has:link to messages whose body contains an http(s) URL, and in:thread to thread replies (parent_id IS NOT NULL). All three are bind-free SQL clauses layered on the existing access-scoped FTS query, so they compose with the current scope + operator binds without disturbing bind order. Unknown has:/in: values stay as free text, matching the before:/after: fallback; operator-only queries still collapse (FTS needs a text term), same as from: alone today.

Shipped as three small logical commits: db columns + SQL, operator parsing + wiring, and tests.

Tests: parser unit tests (new operators + unknown-value fallback) and a db-layer integration test asserting each flag narrows to exactly the matching message. just test + just test-saas + just check green.

Not operator-visible (no env/config/security change).

🤖 Generated with Claude Code

Adds three search operators - `has:file`, `has:link`, `in:thread` - to the room/enclave/home search box, filling the concrete gap left after LC-280 (which added `from:`/`before:`/`after:`). `has:file` narrows to messages with an attachment (an EXISTS over `file_uploads`), `has:link` to messages whose body contains an http(s) URL, and `in:thread` to thread replies (`parent_id IS NOT NULL`). All three are bind-free SQL clauses layered on the existing access-scoped FTS query, so they compose with the current scope + operator binds without disturbing bind order. Unknown `has:`/`in:` values stay as free text, matching the `before:`/`after:` fallback; operator-only queries still collapse (FTS needs a text term), same as `from:` alone today. Shipped as three small logical commits: db columns + SQL, operator parsing + wiring, and tests. Tests: parser unit tests (new operators + unknown-value fallback) and a db-layer integration test asserting each flag narrows to exactly the matching message. `just test` + `just test-saas` + `just check` green. Not operator-visible (no env/config/security change). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Extends SearchFilters with three bind-free boolean refinements and their SQL clauses in search_messages_filtered: has:file (an attachment exists in file_uploads), has:link (body contains an http(s) URL), and in:thread (parent_id IS NOT NULL). The clauses carry no `?` placeholders, so the existing positional bind order is untouched. Not yet wired to the operator parser (all default to false), so behavior is unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NTRhc4n7A2uB2Y5PHV2ewL
Refactors parse_operators to return a ParsedQuery struct and recognizes three new tokens (has:file, has:link, in:thread), wiring them into SearchFilters so the room/enclave/home search box now narrows by attachment, link, or thread-reply. Unknown has:/in: values stay as free text, matching the existing before:/after: fallback. Operator-only queries still collapse (FTS needs a text term), same as from: alone today. Parser unit tests updated to the struct and cover the new operators.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NTRhc4n7A2uB2Y5PHV2ewL
test(search): cover has:file / has:link / in:thread filters (LC-530)
All checks were successful
check-secrets / Nosey parker (pull_request) Successful in 6s
check-secrets / TruffleHog (pull_request) Successful in 9s
check-secrets / Kingfisher (pull_request) Successful in 11s
Check / clippy + fmt + tests (pull_request) Successful in 6m32s
Create release / Create release from merged PR (pull_request) Has been skipped
check-secrets / Nosey parker (push) Successful in 7s
check-secrets / TruffleHog (push) Successful in 8s
check-secrets / Kingfisher (push) Successful in 10s
aad0af9f20
Adds a db-layer test that seeds four "report" messages (one with a file_uploads attachment, one with an http(s) URL, one thread reply, one plain) and asserts each new SearchFilters flag narrows to exactly the matching message, with the unfiltered query returning all four. Also backfills `..Default::default()` on three pre-existing SearchFilters literals in this file that the LC-530 struct change left short (CI runs only `cargo test --lib`, so the integration binary did not flag them at commit time).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NTRhc4n7A2uB2Y5PHV2ewL
longjacksonle deleted branch feat/LC-530-search-filters 2026-07-03 03:38:50 +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!503
No description provided.