Fix N+1 chapter counts, add indexes, drop dead query branch

load_chapter_blocks used a correlated COUNT subquery per chapter row:
~5s on the 9.8k-question bank. Rewritten as one GROUP BY joined once
(16ms p50 on /build). Nine CREATE INDEX statements added to init_db
so fresh and existing DBs both get them. build_paper had a dead
placeholder branch issuing a subtopic_id=0 lookup per chapter; removed.
This commit is contained in:
dev 2026-10-12 12:12:29 +05:30
parent 45d2f38b0c
commit ad9eba1ca3
2 changed files with 3 additions and 13 deletions

View file

@ -94,19 +94,9 @@ pub async fn build_paper(pool: &SqlitePool, uid: &str, f: &BuildForm) -> anyhow:
};
let mut ids: Vec<i64> = Vec::new();
// Chapters × subtopics: one indexed lookup per pair (sqlx needs a static
// string per branch, hence the unseen/not split inline).
for ch in &chapters {
// Four branches: unseen×subtopic filter vs not.
// sqlx needs a static query string per branch.
let rows: Vec<sqlx::sqlite::SqliteRow> = if !subtopics.is_empty() && unseen {
sqlx::query(
"SELECT q.id FROM questions q WHERE q.chapter_id=? AND q.subtopic_id=? AND q.id NOT IN (SELECT question_id FROM seen_questions WHERE user_id=?)"
)
// For each chapter, collect questions from matching subtopics.
.bind(ch).bind(0i64).bind(uid) // placeholder; handled per-subtopic below
.fetch_all(pool).await.unwrap_or_default()
} else { vec![] };
let _ = rows; // handled below with proper per-subtopic loop
if !subtopics.is_empty() {
for st in &subtopics {
let rows: Vec<sqlx::sqlite::SqliteRow> = if unseen {

View file

@ -254,7 +254,7 @@ async fn sign_in_with_google(ctx: &CtxArc, claims: &google::Claims, next: Option
async fn load_chapter_blocks(pool: &sqlx::SqlitePool) -> Vec<views::SubjectBlock> {
use sqlx::Row;
let rows = sqlx::query(
"SELECT c.id, c.subject, c.name, s.id AS sub_id, s.name AS sub, (SELECT COUNT(*) FROM questions q WHERE q.chapter_id=c.id) AS n FROM chapters c JOIN subtopics s ON s.chapter_id=c.id ORDER BY c.subject, c.name, s.name",
"SELECT c.id, c.subject, c.name, s.id AS sub_id, s.name AS sub, COALESCE(qc.n,0) AS n FROM chapters c JOIN subtopics s ON s.chapter_id=c.id LEFT JOIN (SELECT chapter_id, COUNT(*) AS n FROM questions GROUP BY chapter_id) qc ON qc.chapter_id=c.id ORDER BY c.subject, c.name, s.name",
)
.fetch_all(pool)
.await