From fa998b6dc2a9203feb6f2a1083c7d685ac43e6c3 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Sun, 24 Dec 2023 15:21:58 -0500 Subject: [PATCH] Finalize photo near id query for photo thumbnails --- src/app/(static)/p/[photoId]/layout.tsx | 35 ++++++------------ src/cache/index.ts | 35 +++++++++--------- src/services/vercel-postgres.ts | 48 +++++++++++++++++-------- 3 files changed, 60 insertions(+), 58 deletions(-) diff --git a/src/app/(static)/p/[photoId]/layout.tsx b/src/app/(static)/p/[photoId]/layout.tsx index eac8b33f..06133c24 100644 --- a/src/app/(static)/p/[photoId]/layout.tsx +++ b/src/app/(static)/p/[photoId]/layout.tsx @@ -11,8 +11,7 @@ import { absolutePathForPhotoImage, } from '@/site/paths'; import PhotoDetailPage from '@/photo/PhotoDetailPage'; -import { getPhotoCached, getPhotosCached } from '@/cache'; -import { PRIORITY_ORDER_ENABLED } from '@/site/config'; +import { getPhotoCached, getPhotosNearIdCached } from '@/cache'; interface PhotoProps { params: { photoId: string } @@ -51,38 +50,24 @@ export async function generateMetadata({ export default async function PhotoPage({ params: { photoId }, children, -}: - PhotoProps & { children: React.ReactNode }) { - const photo = await getPhotoCached(photoId); +}: PhotoProps & { children: React.ReactNode }) { + const photos = await getPhotosNearIdCached( + photoId, + GRID_THUMBNAILS_TO_SHOW_MAX + 2, + ); + + const photo = photos.find(p => p.id === photoId); if (!photo) { redirect(PATH_ROOT); } - const [ - photosBefore, - photosAfter, - ] = await Promise.all([ - getPhotosCached({ - ...(PRIORITY_ORDER_ENABLED && photo.priorityOrder !== null) - ? { beforePriorityOrder: photo.priorityOrder } - : { takenBefore: photo.takenAt }, - limit: 1, - }), - getPhotosCached({ - ...(PRIORITY_ORDER_ENABLED && photo.priorityOrder !== null) - ? { afterPriorityOrderInclusive: photo.priorityOrder } - : { takenAfterInclusive: photo.takenAt }, - limit: GRID_THUMBNAILS_TO_SHOW_MAX + 1, - }), - ]); - - const photos = photosBefore.concat(photosAfter); + const index = photos.findIndex(p => p.id === photoId); return <> {children} ; } diff --git a/src/cache/index.ts b/src/cache/index.ts index fea2bf78..db0f2387 100644 --- a/src/cache/index.ts +++ b/src/cache/index.ts @@ -21,6 +21,7 @@ import { getPhotosFilmSimulationDateRange, getPhotosFilmSimulationCount, getPhotosDateRange, + getPhotosNearId, } from '@/services/vercel-postgres'; import { parseCachedPhotoDates, parseCachedPhotosDates } from '@/photo'; import { getBlobPhotoUrls, getBlobUploadUrls } from '@/services/blob'; @@ -45,29 +46,20 @@ const getPhotosCacheKeyForOption = ( option: keyof GetPhotosOptions, ): string | null => { switch (option) { - // Primitive keys - case 'sortBy': - case 'limit': - case 'offset': - case 'tag': - case 'simulation': - case 'beforePriorityOrder': - case 'afterPriorityOrderInclusive': - case 'includeHidden': { - const value = options[option]; - return value !== undefined ? `${option}-${value}` : null; - } - // Date keys - case 'takenBefore': - case 'takenAfterInclusive': { - const value = options[option]; - return value ? `${option}-${value.toISOString()}` : null; - } // Complex keys case 'camera': { const value = options[option]; return value ? `${option}-${createCameraKey(value)}` : null; } + case 'takenBefore': + case 'takenAfterInclusive': { + const value = options[option]; + return value ? `${option}-${value.toISOString()}` : null; + } + // Primitive keys + default: + const value = options[option]; + return value !== undefined ? `${option}-${value}` : null; } }; @@ -122,6 +114,13 @@ export const getPhotosCached = ( [KEY_PHOTOS, ...getPhotosCacheKeys(...args)], )(...args).then(parseCachedPhotosDates); +export const getPhotosNearIdCached = ( + ...args: Parameters +) => unstable_cache( + getPhotosNearId, + [KEY_PHOTOS], +)(...args).then(parseCachedPhotosDates); + export const getPhotosDateRangeCached = unstable_cache( getPhotosDateRange, diff --git a/src/services/vercel-postgres.ts b/src/services/vercel-postgres.ts index 2fb440e2..d3af54aa 100644 --- a/src/services/vercel-postgres.ts +++ b/src/services/vercel-postgres.ts @@ -271,8 +271,6 @@ export type GetPhotosOptions = { simulation?: FilmSimulation takenBefore?: Date takenAfterInclusive?: Date - beforePriorityOrder?: number - afterPriorityOrderInclusive?: number includeHidden?: boolean } @@ -314,8 +312,6 @@ export const getPhotos = async (options: GetPhotosOptions = {}) => { simulation, takenBefore, takenAfterInclusive, - beforePriorityOrder, - afterPriorityOrderInclusive, includeHidden, } = options; @@ -350,15 +346,6 @@ export const getPhotos = async (options: GetPhotosOptions = {}) => { wheres.push(`film_simulation=$${valueIndex++}`); values.push(simulation); } - if (beforePriorityOrder !== undefined) { - wheres.push(`priority_order < $${valueIndex++}`); - values.push(beforePriorityOrder); - } - if (afterPriorityOrderInclusive !== undefined) { - // eslint-disable-next-line max-len - wheres.push(`priority_order >= $${valueIndex++} OR priority_order IS NULL`); - values.push(afterPriorityOrderInclusive); - } if (wheres.length > 0) { sql.push(`WHERE ${wheres.join(' AND ')}`); } @@ -380,9 +367,40 @@ export const getPhotos = async (options: GetPhotosOptions = {}) => { sql.push(`LIMIT $${valueIndex++} OFFSET $${valueIndex++}`); values.push(limit, offset); - const client = await db.connect(); + return safelyQueryPhotos(async () => { + const client = await db.connect(); + return client.query(sql.join(' '), values); + }) + .then(({ rows }) => rows.map(parsePhotoFromDb)); +}; - return safelyQueryPhotos(() => client.query(sql.join(' '), values)) +export const getPhotosNearId = async ( + id: string, + limit: number, +) => { + const orderBy = PRIORITY_ORDER_ENABLED + ? 'ORDER BY priority_order ASC, taken_at DESC' + : 'ORDER BY taken_at DESC'; + + return safelyQueryPhotos(async () => { + const client = await db.connect(); + return client.query( + ` + WITH twi AS ( + SELECT *, row_number() + OVER (${orderBy}) as row_number + FROM photos + WHERE hidden IS NOT TRUE + ), + current AS (SELECT row_number FROM twi WHERE id = $1) + SELECT twi.* + FROM twi, current + WHERE twi.row_number >= current.row_number - 1 + LIMIT $2 + `, + [id, limit] + ); + }) .then(({ rows }) => rows.map(parsePhotoFromDb)); };