projects / easemysemester / features
Keyword search over the question bank
v2 shipped Postgres full-text search over question and answer bodies, scoped to enrolled subjects and ranked by recurrence rather than text score, with every result snippet drawn from the field that actually matched.
feature · current · updated 2026-09-15 · EaseMySemester case study
See overview and ranked topics view, whose no-probability rule governs this feature's ranking too.
What it does
Postgres full-text search over question bodies and the condensed and detailed answer bodies,
scoped to the student's enrolled subjects, using plainto_tsquery as a parameter, never
interpolated into the SQL. Results carry chapter and topic context and a snippet with the match
wrapped in <mark> via a computed range, never injected HTML. Input is debounced (about
200ms) and updates the ?q= URL parameter as the student types.
Deliberately not a chatbot: free-form Q&A over the textbooks is treated as a hallucination surface, so the curated question bank stays the interface.
Ranked by recurrence, not text score
Results are ordered by recurrence rather than text-match score, so the same recurrence band chips used elsewhere in the product stay meaningful on the search screen too, and the no-probability rule already locked for the ranked-topics view carries over here.
What review found and fixed
A defect (#59, finding 20) let some results show a snippet that did not contain the match, so
there was no way to see why a result matched. The fix draws the snippet from the same predicate
that admitted the row (condensed, else detailed, else the body), the same field that produced
the match. Verified against real data: 332 snippets checked across twelve search terms, zero
with no marked hit. Six apparent misses were traced to Postgres stemming (clotting to clot),
not a defect.
Subject scoping was also verified against real data with no cross-subject leakage in any combination of enrolled subjects, including the empty-enrollment and empty-query cases, which each correctly return zero results.
What was left out
No tsvector/GIN index. At 898 rows this is fast enough without one; a generated tsvector
column and GIN index is the follow-up if the bank grows past a few thousand rows. The english
text search configuration stems medical and Latin terms adequately, but a multi-word or
hyphenated clinical phrase may not tokenise as expected; a custom dictionary is the fix if that
proves thin in practice.
Status
Shipped and verified 2026-09-09 (d509d0b, snippet fix 7eb598d). Lint, typecheck, and 263
unit tests pass; no schema change, so the database test lane is unaffected.