feat: rating stars on recipe cards (Search, Explore)

New lib/recipe-ratings.ts helper batches avg score + review count for
a set of recipe ids in one grouped query, kept separate from the main
paginated/sorted search query rather than joining+grouping it directly
(would've forced grouping by every selected column). Wired into
/api/v1/search and the Explore page's trending/recent queries;
SearchResultCard renders the star only when a recipe has at least one
rating.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Arnaud
2026-07-13 22:43:58 +02:00
parent 95a4dccce8
commit c21157fc93
9 changed files with 70 additions and 7 deletions
+20
View File
@@ -0,0 +1,20 @@
import { db, ratings, inArray, sql } from "@epicure/db";
export type RatingSummary = { avgRating: number; ratingCount: number };
/** Average score + review count per recipe, for a batch of recipe ids — used to show rating stars on list/search cards without a per-row join. */
export async function getAvgRatingsByRecipeId(recipeIds: string[]): Promise<Map<string, RatingSummary>> {
if (recipeIds.length === 0) return new Map();
const rows = await db
.select({
recipeId: ratings.recipeId,
avgScore: sql<number>`avg(${ratings.score})::float`,
count: sql<number>`count(*)::int`,
})
.from(ratings)
.where(inArray(ratings.recipeId, recipeIds))
.groupBy(ratings.recipeId);
return new Map(rows.map((r) => [r.recipeId, { avgRating: r.avgScore, ratingCount: r.count }]));
}