feat(search): Unicode-aware case-insensitive search via casefold columns #234

Merged
JMR-dev merged 13 commits from feat-unicode-search-casefold into main 2026-07-03 23:29:04 +00:00
13 Commits
Author SHA1 Message Date
Jason Ross 0fd3954f79 Merge main into feat-unicode-search-casefold 2026-07-03 17:38:56 -05:00
Jason Ross 0a462fda0c Merge main into feat-unicode-search-casefold 2026-07-03 17:24:57 -05:00
Jason Ross 7543350330 Merge main into feat-unicode-search-casefold 2026-07-03 17:11:12 -05:00
Jason Ross 50db69b060 Merge main into feat-unicode-search-casefold 2026-07-03 16:49:25 -05:00
Jason Ross de04bc2cae Merge main into feat-unicode-search-casefold 2026-07-03 16:17:07 -05:00
Jason Ross 40067a069f Merge main into feat-unicode-search-casefold 2026-07-03 16:09:15 -05:00
Jason Ross 853b40f419 Merge main into feat-unicode-search-casefold 2026-07-03 14:53:28 -05:00
Jason Ross 4c852a1083 Merge main into feat-unicode-search-casefold 2026-07-03 14:48:14 -05:00
Jason Ross 1ea50ea316 Merge main into feat-unicode-search-casefold 2026-07-03 14:10:08 -05:00
Jason Ross c86e8acf2f Merge main into feat-unicode-search-casefold 2026-07-03 13:15:29 -05:00
Jason Ross 78fa555d87 Merge main into feat-unicode-search-casefold 2026-07-03 12:55:50 -05:00
Jason Ross 276e08840a Merge main into feat-unicode-search-casefold 2026-07-03 12:43:07 -05:00
JMR-devandClaude Opus 4.8 aae4f2218b feat(search): Unicode-aware case-insensitive search via casefold columns
Restores Unicode-aware case-insensitive substring search (approach A of #227).
Paging (#223) moved search to a SQL LIKE scan whose case-folding is ASCII-only,
so non-ASCII terms stopped matching case-insensitively.

Adds per-field casefold columns to `messages` (senderFold/senderEmailFold/
subjectFold/snippetFold), each = Kotlin lowercase() of its source (Unicode-aware).
Per-field (not one concatenated column) because the fields are maintained by
partial UPDATEs that don't carry all four: toEntity sets all folds, updateBody
keeps snippetFold in sync, updateHeaderContent keeps the header folds -- via thin
DAO default-method wrappers so the five call sites are unchanged. Search matches
the fold columns with a pattern built from the lowercased query.

Schema v18->v19 (additive; ASCII lower() backfill, non-ASCII rows re-fold on next
write). Adds a MigrationTest v18->v19 case and a repo test asserting the query is
casefolded.

Closes #232

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 11:41:53 -05:00