Feature: Search Feature Implementation (FEAT-58)
Metadata
- Issue ID: FEAT-58
- Status: Done
- Owner: Kzu0-afk
- Related PRs: TBD
- Canonical feature doc:
docs/features/FEAT-58-Search-Feature-Implementation.md
Overview
FEAT-58 improves search and ranking quality across discovery surfaces:
- Explore page
- Institutions directory
- Courses directory
- Documents directory
The objective is stronger discoverability and monetization support while preserving relevance, stability, and SEO-friendly behavior.
Frontend Behavior
- Uses URL-driven search state (
q,sort,page) on directory pages for shareability and crawl consistency. - Keeps entity-specific search + sort controls with clear empty-result and fallback states.
- Preserves active filters across pagination links.
- Maintains current Explore-page strategy:
- lightweight client-side filter for preloaded cards in v1;
- deeper backend-powered search integration planned for follow-up phase.
- Allows optional local persistence only for non-sensitive UX preferences (recent query/sort context), with URL state remaining authoritative.
Backend Behavior
- Existing catalog APIs already support paginated query + sort:
GET /institutionsGET /coursesGET /documents- plus scoped variants (
/institutions/:slug/courses,/courses/:slug/documents)
- Ranking/signals currently used:
- documents: quality, downloads, views, trending/engagement, topic tags
- courses: popularity, document depth, trending
- institutions: course/document breadth and location/name relevance
- FEAT-58 planning includes continued relevance tuning, deterministic tie-breakers, and bounded query behavior.
QA Test Scenarios
| Scenario ID | Description | Steps | Input | Expected Result |
|---|---|---|---|---|
| FEAT-58-01 | Happy path: document search by title | Open /documents, enter known title term, submit | q=<known title token> | Relevant documents appear first; pagination and sort remain functional |
| FEAT-58-02 | Happy path: course search by code | Open /courses, search by course code | q=CS101 | Matching course cards are returned with stable ordering |
| FEAT-58-03 | Happy path: institution search by location | Open /institutions, search by city/state | q=<city/state> | Institutions in that location appear; no irrelevant crash/fallback |
| FEAT-58-04 | Validation: blank query | Submit empty query with active sort | q="", sort=popular | Default ranked listing returns; no API error |
| FEAT-58-05 | Validation: very long query | Submit long free text query | long string (>= 300 chars) | API responds safely, no timeout/crash, UI shows empty/limited results gracefully |
| FEAT-58-06 | Validation: unsupported sort value | Force unknown sort in URL | sort=unknown_sort | API falls back to safe default sort (popular); UI still renders |
| FEAT-58-07 | Backend failure handling | Simulate backend unavailable for listing endpoint | API 5xx or connection failure | UI shows error-safe fallback state and does not white-screen |
| FEAT-58-08 | Edge case: no matches | Query nonexistent token | q=zzzz_nonexistent | "No results" state appears with clear message per entity |
| FEAT-58-09 | Edge case: query persistence with pagination | Search then move to page 2 | q=<term>&page=2 | Query + sort are preserved in URL and pagination links |
| FEAT-58-10 | Monetization safety | Search results page with ad slot enabled | normal query flow | Search remains usable; ad placement does not block controls/content |
| FEAT-58-11 | Sort option persistence across pagination | Select "Trending" sort, paginate to page 3, reload | sort=trending&page=3 | Sort dropdown reflects "Trending"; correct sort applied to API call |
| FEAT-58-12 | Explore page: client-side institution filter | Open /, type partial institution name in search bar | search input text | Matching institutions appear immediately (client-side); no page reload |
| FEAT-58-13 | Explore page: empty client filter | Open /, type nonexistent institution name | search input text (no match) | "No institutions match your search right now." message appears |
| FEAT-58-14 | Phase 2: document search by topic tag | Open /documents, search by a known tag term | q=<tag term> | Documents with matching topic_tags appear in results (requires Phase 2 topic_tags search extension) |
| FEAT-58-15 | Pagination reset on new query | On /documents?q=old&sort=popular&page=3, enter new query, submit | new q value | URL resets to page=1 (form omits page param); results reflect new query |
Edge Cases
- Long or symbol-heavy queries.
- Query/sort persistence across pagination transitions.
- URL vs localStorage preference mismatch (URL wins).
- Ranking drift when engagement signals update between page loads.
- Explore-page partial preload vs deep catalog search expectations.
Notes
- This SBDocs page is a compact index view; the canonical implementation details remain in the FEAT-58 feature doc.
- No mock/placeholder search data is introduced.
- No auth/session model changes are part of FEAT-58 search scope.
- #279 update — Explore region filter (backend-powered): the Explore region chips (Near You / Northeast / Southeast / Midwest / West / Canada) are now wired to a real, URL-driven (
?region=<slug>) location filter onGET /institutions— the backend follow-up to the client-side v1 noted above. Region slugs map toinstitution_country/institution_statefull-name values server-side (backend/src/institutions/institution-regions.ts) using the dedicated location indexes. "Near You" is a no-filter default in Phase 1 (geolocation deferred to Phase 2). Canonical feature doc:docs/features/279-location-region-filter.md.