feature/better-search #3

Merged
fredo merged 6 commits from feature/better-search into main 2026-09-11 08:48:09 +02:00
Owner
No description provided.
normalize(): lowercase → NFD decompose → strip combining marks →
collapse punctuation to spaces. Métacam®, METACAM and metacam all
produce "metacam".

jaroWinkler(): standard Jaro-Winkler similarity with 0.1 prefix weight.
Weights shared prefixes heavily, which matches how drug-name typos
behave in practice ("metcam" vs "metacam").

No new dependencies — java.text.Normalizer is part of the JDK.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace SQL LIKE search with a five-tier in-memory ranker:
  1. exact normalized name        (100)
  2. name starts-with             (90)
  3. name contains                (75)
  4. all query tokens match name  (65)
  5. composition match            (35)
Jaro-Winkler ≥ 0.85 results go into SearchResult.fuzzy (shown below
a divider in the UI so they are never mistaken for exact matches).

All comparisons use SearchNormalizer.normalize() on both sides so
Métacam, METACAM® and metacam all match the same query.

SearchResult drives two public StateFlows: medications (direct) and
fuzzyMedications. The debounce(200) on the search query prevents
ranking on every keystroke. rankSearch runs on Dispatchers.Default
to keep the main thread free.

No schema change — normalization is computed at query time. With
~1570 records and 200ms debounce the full ranking including
Jaro-Winkler completes in < 10ms.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
MedicationListScreen receives fuzzyMedications separately from
medications. When non-empty, a centred label between two HorizontalDividers
separates exact/ranked results from approximate (Jaro-Winkler) ones,
so approximate matches are never mistaken for exact ones.

Strings added in both FR ("Résultats similaires") and NL
("Gelijkaardige resultaten").

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The when block checked medications.isEmpty() to show "no results",
but medications only holds direct results. When a typo query has
only Jaro-Winkler matches (fuzzyMedications non-empty), the screen
incorrectly showed "aucun médicament trouvé" and never reached
the LazyColumn that renders the fuzzy section.

Fix: also require fuzzyMedications.isEmpty() before showing
the "no results" message.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Drug names are long multi-word strings like "Métacam 5 mg/mL, solution injectable".
Jaro-Winkler("metcam", full_name) ≈ 0.74 because the match count is divided by the
full string length — so typo queries never cleared the 0.85 threshold.

Comparing against each name token separately (taking the max) gives
JW("metcam", "metacam") ≈ 0.93, which correctly surfaces the drug under
"Résultats similaires".

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two changes diagnosed from logcat (2.4-second Davey frames, 146 skipped frames):

1. The debounce on _searchQuery was a flat 200 ms, which delayed the very first
   medication list load. The screen flashed "no medications found" for ~200 ms on
   every launch before the list appeared. Fix: use a query-dependent timeout —
   0 ms for a blank query (initial load is immediate) and 200 ms while the user
   is actively typing (unchanged behaviour for search).

2. After Item 2 the medications table holds both FR and NL (~3 154 rows). The
   getAllMedications(language) query uses WHERE language = ? ORDER BY name ASC,
   but the composite PK index (id, language) cannot satisfy that filter+sort
   efficiently. SQLite does a full table scan and a separate sort pass on every
   startup. Fix: add a (language, name) index (DB v4 → v5, MIGRATION_4_5). With
   the new index SQLite reads only the target-language rows, already in name
   order, with no sort step.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
fredo merged commit 60d4d1904c into main 2026-09-11 08:48:09 +02:00
fredo deleted branch feature/better-search 2026-09-12 18:49:31 +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
fredo/Compendium!3
No description provided.