Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 34 additions & 20 deletions backend/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ const typeDefs = `#graphql
): WinePage!
# Viner til smakskartet. Bare viner med både fylde og syre kommer med.
tasteMap(filter: TasteFilter, limit: Int = 150): WinePage!
tasteMap(filter: TasteFilter, limit: Int = 100): WinePage!
tasteOptions: TasteOptions!
}
Expand Down Expand Up @@ -349,7 +349,7 @@ const resolvers = {
_: unknown,
args: { filter?: TasteFilter | null; limit?: number },
) => {
const limit = args.limit ?? 150;
const limit = args.limit ?? 100;
if (!Number.isInteger(limit) || limit < 1 || limit > TASTE_MAP_MAX) {
throw new GraphQLError(`limit must be between 1 and ${TASTE_MAP_MAX}`, {
extensions: { code: 'BAD_USER_INPUT' },
Expand All @@ -360,26 +360,40 @@ const resolvers = {

// Vi viser de vinene flest har vurdert, så kartet fylles med kjente
// viner i stedet for viner med én enkelt vurdering på 5.0.
// De mest vurderte er nesten bare rødvin, så vi tar dem på omgang per
// type: den mest vurderte av hver type først, så den nest mest vurderte
// osv. Uten typefilter blir kartet en jevn miks (100 viner = 20 av hver
// av de fem typene); med typefilter finnes bare én type, og rekkefølgen
// blir som før. 'Dessert' og 'Dessert/Port' regnes som én type, slik
// som i fargeforklaringen på kartet.
const winesResult = await pool.query(
`
SELECT
w.id,
w.name,
w.type,
w.abv::float AS abv,
w.body,
w.acidity,
wi.name AS winery,
r.name AS region,
c.name AS country,
w.avg_rating::float AS "avgRating",
w.rating_count AS "ratingCount"
FROM wines w
JOIN wineries wi ON w.winery_id = wi.id
JOIN regions r ON w.region_id = r.id
JOIN countries c ON w.country_code = c.code
WHERE ${sql}
ORDER BY w.rating_count DESC, w.id
SELECT *
FROM (
SELECT
w.id,
w.name,
w.type,
w.abv::float AS abv,
w.body,
w.acidity,
wi.name AS winery,
r.name AS region,
c.name AS country,
w.avg_rating::float AS "avgRating",
w.rating_count AS "ratingCount",
ROW_NUMBER() OVER (
PARTITION BY
CASE WHEN w.type LIKE 'Dessert%' THEN 'Dessert' ELSE w.type END
ORDER BY w.rating_count DESC, w.id
) AS type_rank
FROM wines w
JOIN wineries wi ON w.winery_id = wi.id
JOIN regions r ON w.region_id = r.id
JOIN countries c ON w.country_code = c.code
WHERE ${sql}
) ranked
ORDER BY type_rank, "ratingCount" DESC, id
LIMIT $${params.length + 1}
`,
[...params, limit],
Expand Down
19 changes: 9 additions & 10 deletions client/src/components/ExploreByTaste.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ function toVariables(filter: TasteFilter) {
* only display what they get as props (same split as WineCatalog/WineList).
*
* Scalability: the map never loads the whole dataset. The backend returns at
* most TASTE_MAP_LIMIT wines (the most-rated ones that match the filters),
* most TASTE_MAP_LIMIT wines (the most-rated ones that match the filters,
* taken in turn from each wine type so the map is not almost all red),
* and totalCount tells how many match in total.
*/
export default function ExploreByTaste() {
Expand Down Expand Up @@ -92,17 +93,15 @@ export default function ExploreByTaste() {
aria-label="Taste map"
aria-busy={loading}
>
<h2 className="taste-count">Wine map</h2>
{/* role="status" announces the new count when filters change */}
<p className="taste-count" role="status">
{shown
? `${shown.totalCount.toLocaleString('en')} wines found`
: 'Loading wines …'}
<p className="taste-note" role="status">
{!shown
? 'Loading wines …'
: shown.totalCount > wines.length
? `Showing ${wines.length} wines`
: `${shown.totalCount.toLocaleString('en')} ${shown.totalCount === 1 ? 'wine' : 'wines'} found.`}
</p>
{shown && shown.totalCount > wines.length && (
<p className="taste-note">
The map shows the {wines.length} most-rated of them.
</p>
)}

{error && (
<p role="alert">Could not load wines. Please try again later.</p>
Expand Down
2 changes: 1 addition & 1 deletion client/src/data/taste.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export function wineTypeColor(type: string): string {
}

/** How many wines the map shows at most. The backend allows up to 300. */
export const TASTE_MAP_LIMIT = 150;
export const TASTE_MAP_LIMIT = 100;

/** "Only popular wines" means at least this many ratings. */
export const POPULAR_MIN_RATINGS = 100;
Expand Down
3 changes: 2 additions & 1 deletion client/src/graphql/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ export type WinesVariables = {
/**
* Wines for the Explore by taste map.
*
* The backend returns the most-rated wines that match the filters, at most
* The backend returns the most-rated wines that match the filters, taken in
* turn from each wine type so all types show up, at most
* `limit` of them. totalCount is the number of all matching wines.
*/
export const TASTE_MAP_QUERY = gql`
Expand Down