Skip to content
How much to read

Knowledge base

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.

This page was written by a model from my own repositories and checked by a second one against the same sources. I did not write it, and no version of it was approved by me before it went up. How that works, and where it can still be wrong.