feat: ingredient/tag search on My Recipes, QR code on printed recipes
My Recipes search previously only matched title/description; now also matches ingredient rawName and tags, mirroring the public search improvement from earlier. Hit and fixed a real bug along the way: embedding drizzle column proxies from a foreign table inside a raw sql`` fragment passed to db.query.recipes.findMany's `where` gets rewritten to the wrong table alias by the relational query builder, producing broken SQL — worked fine in the plain query builder used by /api/v1/search, but not here. Fixed by using literal SQL identifiers for the ingredients subquery instead of column proxies. Recipe print pages get the same QR-code treatment shopping lists got earlier, gated on visibility !== "private" (both public and unlisted resolve at /r/[id]). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -55,11 +55,25 @@ export default async function RecipesPage({ searchParams }: { searchParams: Sear
|
||||
: undefined;
|
||||
const batchCookFilter = batchCook === "1" || batchCook === "0" ? batchCook : undefined;
|
||||
|
||||
// Escape ilike wildcard chars (% and _) so a search like "100%" matches literally.
|
||||
const escapedQuery = query.replace(/[\\%_]/g, (c) => `\\${c}`);
|
||||
|
||||
const where = and(
|
||||
eq(recipes.authorId, session.user.id),
|
||||
batchCookFilter ? eq(recipes.isBatchCook, batchCookFilter === "1") : undefined,
|
||||
query
|
||||
? or(ilike(recipes.title, `%${query}%`), ilike(recipes.description, `%${query}%`))
|
||||
? or(
|
||||
ilike(recipes.title, `%${escapedQuery}%`),
|
||||
ilike(recipes.description, `%${escapedQuery}%`),
|
||||
// Plain SQL identifiers rather than drizzle's column proxies —
|
||||
// inside db.query.recipes.findMany's `where`, the relational query
|
||||
// builder rewrites embedded column refs to the outer query's own
|
||||
// table alias regardless of which table they actually belong to,
|
||||
// producing broken SQL (recipes.raw_name doesn't exist). Using
|
||||
// literal identifiers for the ingredients subquery sidesteps that.
|
||||
sql`exists (select 1 from recipe_ingredients ri where ri.recipe_id = ${recipes.id} and ri.raw_name ilike ${`%${escapedQuery}%`})`,
|
||||
sql`exists (select 1 from unnest(${recipes.tags}) as tag where tag ilike ${`%${escapedQuery}%`})`
|
||||
)
|
||||
: undefined,
|
||||
visibilityFilter ? eq(recipes.visibility, visibilityFilter) : undefined,
|
||||
difficultyFilter ? eq(recipes.difficulty, difficultyFilter) : undefined,
|
||||
|
||||
Reference in New Issue
Block a user