Current search is in-memory substring filter (JudgeSearchAction.ts does Judge.all().filter(includes(needle))). Won't scale, no fuzzy match, hard 20-row cap. Meilisearch is running locally on 127.0.0.1:7700, the framework already has a driver, the model useSearch traits are declared. The wiring is largely there.
Detailed plan was drafted earlier in a separate analysis pass. Filing as an issue for tracking.
Scope summary
- Phase 0 — Fix config drift:
config/search-engine.ts:11isdriver: 'opensearch', flip to'meilisearch'. - Phase 1 — Model
useSearchblocks + per-modeltoSearchableObject()overrides for relational fields (judge ↔ court denormalization, review ↔ judge denormalization). - Phase 2 — Run
./buddy search-engine:update --flush --settingsfor the initial backfill. - Phase 3 — Replace
JudgeSearchActionwith a Meilisearch-backed call. AddCourtHouseSearchAction. - Phase 4 — Add
/api/searchglobal endpoint that fans out acrossjudges/court_houses/judge_reviewsindexes. Filter reviews onstatus = publishedat query time. - Phase 5 — Migrate
SearchView.stxto use the server-side global search instead of client-side filter.
Related upstream issues
stacksjs/stacks#1917—toSearchableObjectreturns null foruseSearch: true(blocks Phase 1 if we ever use the boolean form).stacksjs/stacks#1918—toSearchableObjectignoresbelongsTorelations (we work around with per-modeltoSearchableObjectoverrides until this lands).
Acceptance
- Typeahead in the header uses Meilisearch — typo tolerance works ("Robets" finds Roberts).
/searchpage is server-side, full-text searches reviews by title + content + judge name.- Pending/rejected reviews never appear in public search results (filter
status = published).