From 8b70b749d353fcc66cedc7b79defa04dd5de3eaa Mon Sep 17 00:00:00 2001 From: Peter Werry Date: Fri, 26 Jun 2026 14:54:11 -0700 Subject: [PATCH] Step 6: full-text search --- src/db.ts | 3 +++ src/synthesis/prompt.ts | 4 ++-- src/validation/validate.ts | 2 ++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/db.ts b/src/db.ts index e821e05..fe3dcfe 100644 --- a/src/db.ts +++ b/src/db.ts @@ -45,12 +45,15 @@ export async function ensureIndexes(): Promise { await prs.createIndex({ state: 1 }); await prs.createIndex({ merged_at: -1 }); await prs.createIndex({ created_at: -1 }); + // Full-text index over content, enabling {$text: {$search: ...}} topic queries. + await prs.createIndex({ title: "text", body: "text" }); const issues = await getCollection("issues"); await issues.createIndex({ repo: 1, number: 1 }, { unique: true }); await issues.createIndex({ "user.login": 1 }); await issues.createIndex({ state: 1 }); await issues.createIndex({ created_at: -1 }); + await issues.createIndex({ title: "text", body: "text" }); const users = await getUsersCollection(); await users.createIndex({ login: 1 }, { unique: true }); diff --git a/src/synthesis/prompt.ts b/src/synthesis/prompt.ts index a8cac9a..b54df4a 100644 --- a/src/synthesis/prompt.ts +++ b/src/synthesis/prompt.ts @@ -9,8 +9,8 @@ ${schemaContext} - Use ONLY fields that appear in the schema above. Never invent fields. - Pick the single collection that best answers the question. - Allowed pipeline stages: $match, $sort, $limit, $count, $group, $project, $unwind. -- Allowed filter operators: $eq, $ne, $gt, $gte, $lt, $lte, $in, $nin, $and, $or, $not, $exists, $size, $elemMatch. -- Do NOT use $regex on free-text fields (title, body, comments.body). Match exact field values instead. +- Allowed filter operators: $eq, $ne, $gt, $gte, $lt, $lte, $in, $nin, $and, $or, $not, $exists, $size, $elemMatch, $text. +- For topic/keyword search over PR or issue content, use a text search: put {$text: {$search: "keywords"}} inside a $match (backed by a text index on title + body). Combine it with other filters in the SAME $match (author login, state, dates). Use a phrase like "\"code review\"" for exact phrases. Never use $regex on text fields. $text searches only title and body. - Timestamps are ISO-8601 strings; ISO strings sort lexicographically, so compare them directly with $gte/$lte. - For relative dates ("today", "last week", "past 3 days"), compute the cutoff from the "Current time" given in the user message — never guess the current date. - A PR is "merged" when merged is true (or merged_at is not null). "Open"/"closed" use the state field. diff --git a/src/validation/validate.ts b/src/validation/validate.ts index 2f4c552..b4776df 100644 --- a/src/validation/validate.ts +++ b/src/validation/validate.ts @@ -16,6 +16,8 @@ const ALLOWED_OPERATORS = new Set([ // query operators "$eq", "$ne", "$gt", "$gte", "$lt", "$lte", "$in", "$nin", "$and", "$or", "$not", "$nor", "$exists", "$size", "$elemMatch", + // full-text search (backed by the title/body text index) + "$text", "$search", "$language", "$caseSensitive", "$diacriticSensitive", // accumulators / group expressions "$sum", "$avg", "$min", "$max", "$first", "$last", "$push", "$addToSet", ]);