Refactor photo/db
This commit is contained in:
parent
baa3edcf9f
commit
12d051ae2c
@ -4,7 +4,7 @@ import SiteGrid from '@/components/SiteGrid';
|
||||
import AdminUploadsTable from '@/admin/AdminUploadsTable';
|
||||
import { PRO_MODE_ENABLED } from '@/site/config';
|
||||
import { getStoragePhotoUrlsNoStore } from '@/services/storage/cache';
|
||||
import { getPhotos } from '@/photo/db';
|
||||
import { getPhotos } from '@/photo/db/query';
|
||||
import { revalidatePath } from 'next/cache';
|
||||
import AdminPhotosTable from '@/admin/AdminPhotosTable';
|
||||
import AdminPhotosTableInfinite from
|
||||
|
||||
@ -4,7 +4,7 @@ import { getPhotosCached } from '@/photo/cache';
|
||||
import TagForm from '@/tag/TagForm';
|
||||
import { PATH_ADMIN, PATH_ADMIN_TAGS, pathForTag } from '@/site/paths';
|
||||
import PhotoLightbox from '@/photo/PhotoLightbox';
|
||||
import { getPhotosMeta } from '@/photo/db';
|
||||
import { getPhotosMeta } from '@/photo/db/query';
|
||||
import AdminTagBadge from '@/admin/AdminTagBadge';
|
||||
|
||||
const MAX_PHOTO_TO_SHOW = 6;
|
||||
|
||||
@ -6,7 +6,7 @@ import PhotosEmptyState from '@/photo/PhotosEmptyState';
|
||||
import { Metadata } from 'next/types';
|
||||
import PhotoGridSidebar from '@/photo/PhotoGridSidebar';
|
||||
import { getPhotoSidebarData } from '@/photo/data';
|
||||
import { getPhotos } from '@/photo/db';
|
||||
import { getPhotos } from '@/photo/db/query';
|
||||
import { cache } from 'react';
|
||||
import PhotoGridPage from '@/photo/PhotoGridPage';
|
||||
import { PATH_GRID } from '@/site/paths';
|
||||
|
||||
@ -3,7 +3,7 @@ import {
|
||||
INFINITE_SCROLL_GRID_PHOTO_MULTIPLE,
|
||||
} from '@/photo';
|
||||
import { getPhotosCached } from '@/photo/cache';
|
||||
import { getPhotosMeta } from '@/photo/db';
|
||||
import { getPhotosMeta } from '@/photo/db/query';
|
||||
import StaggeredOgPhotos from '@/photo/StaggeredOgPhotos';
|
||||
import StaggeredOgPhotosInfinite from '@/photo/StaggeredOgPhotosInfinite';
|
||||
|
||||
|
||||
@ -5,7 +5,8 @@ import { getIBMPlexMonoMedium } from '@/site/font';
|
||||
import { ImageResponse } from 'next/og';
|
||||
import { getImageResponseCacheControlHeaders } from '@/image-response/cache';
|
||||
import { IS_PRODUCTION, STATICALLY_OPTIMIZED_OG_IMAGES } from '@/site/config';
|
||||
import { GENERATE_STATIC_PARAMS_LIMIT, getPhotoIds } from '@/photo/db';
|
||||
import { getPhotoIds } from '@/photo/db/query';
|
||||
import { GENERATE_STATIC_PARAMS_LIMIT } from '@/photo/db';
|
||||
import { isNextImageReadyBasedOnPhotos } from '@/photo';
|
||||
|
||||
export let generateStaticParams:
|
||||
|
||||
@ -13,7 +13,8 @@ import {
|
||||
import PhotoDetailPage from '@/photo/PhotoDetailPage';
|
||||
import { getPhotosNearIdCached } from '@/photo/cache';
|
||||
import { IS_PRODUCTION, STATICALLY_OPTIMIZED_PAGES } from '@/site/config';
|
||||
import { GENERATE_STATIC_PARAMS_LIMIT, getPhotoIds } from '@/photo/db';
|
||||
import { getPhotoIds } from '@/photo/db/query';
|
||||
import { GENERATE_STATIC_PARAMS_LIMIT } from '@/photo/db';
|
||||
import { ReactNode, cache } from 'react';
|
||||
|
||||
const getPhotosNearIdCachedCached = cache((photoId: string) =>
|
||||
|
||||
@ -8,7 +8,7 @@ import { Metadata } from 'next/types';
|
||||
import { MAX_PHOTOS_TO_SHOW_OG } from '@/image-response';
|
||||
import PhotosLarge from '@/photo/PhotosLarge';
|
||||
import { cache } from 'react';
|
||||
import { getPhotos, getPhotosMeta } from '@/photo/db';
|
||||
import { getPhotos, getPhotosMeta } from '@/photo/db/query';
|
||||
import PhotosLargeInfinite from '@/photo/PhotosLargeInfinite';
|
||||
|
||||
export const dynamic = 'force-static';
|
||||
|
||||
@ -13,7 +13,7 @@ import {
|
||||
import PhotoDetailPage from '@/photo/PhotoDetailPage';
|
||||
import { getPhotosNearIdCached } from '@/photo/cache';
|
||||
import { ReactNode, cache } from 'react';
|
||||
import { getPhotosMeta } from '@/photo/db';
|
||||
import { getPhotosMeta } from '@/photo/db/query';
|
||||
|
||||
const getPhotosNearIdCachedCached = cache((photoId: string, tag: string) =>
|
||||
getPhotosNearIdCached(
|
||||
|
||||
@ -7,7 +7,7 @@ import PhotoDetailPage from '@/photo/PhotoDetailPage';
|
||||
import {
|
||||
getPhotosNearIdCached,
|
||||
} from '@/photo/cache';
|
||||
import { getPhotosMeta } from '@/photo/db';
|
||||
import { getPhotosMeta } from '@/photo/db/query';
|
||||
import { PATH_ROOT, absolutePathForPhoto } from '@/site/paths';
|
||||
import { TAG_HIDDEN } from '@/tag';
|
||||
import { Metadata } from 'next';
|
||||
|
||||
@ -3,7 +3,7 @@ import Banner from '@/components/Banner';
|
||||
import SiteGrid from '@/components/SiteGrid';
|
||||
import PhotoGrid from '@/photo/PhotoGrid';
|
||||
import { getPhotosNoStore } from '@/photo/cache';
|
||||
import { getPhotosMeta } from '@/photo/db';
|
||||
import { getPhotosMeta } from '@/photo/db/query';
|
||||
import { absolutePathForTag } from '@/site/paths';
|
||||
import { TAG_HIDDEN, descriptionForTaggedPhotos, titleForTag } from '@/tag';
|
||||
import HiddenHeader from '@/tag/HiddenHeader';
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
'use server';
|
||||
|
||||
import {
|
||||
GetPhotosOptions,
|
||||
deletePhoto,
|
||||
insertPhoto,
|
||||
deletePhotoTagGlobally,
|
||||
@ -9,7 +8,8 @@ import {
|
||||
renamePhotoTagGlobally,
|
||||
getPhoto,
|
||||
getPhotos,
|
||||
} from '@/photo/db';
|
||||
} from '@/photo/db/query';
|
||||
import { GetPhotosOptions } from './db';
|
||||
import {
|
||||
PhotoFormData,
|
||||
convertFormDataToPhotoDbInsert,
|
||||
|
||||
@ -5,7 +5,6 @@ import {
|
||||
unstable_noStore,
|
||||
} from 'next/cache';
|
||||
import {
|
||||
GetPhotosOptions,
|
||||
getPhoto,
|
||||
getPhotos,
|
||||
getUniqueCameras,
|
||||
@ -15,7 +14,8 @@ import {
|
||||
getPhotosNearId,
|
||||
getPhotosMostRecentUpdate,
|
||||
getPhotosMeta,
|
||||
} from '@/photo/db';
|
||||
} from '@/photo/db/query';
|
||||
import { GetPhotosOptions } from './db';
|
||||
import { parseCachedPhotoDates, parseCachedPhotosDates } from '@/photo';
|
||||
import { createCameraKey } from '@/camera';
|
||||
import {
|
||||
|
||||
@ -9,7 +9,7 @@ import {
|
||||
getUniqueCameras,
|
||||
getUniqueFilmSimulations,
|
||||
getUniqueTags,
|
||||
} from '@/photo/db';
|
||||
} from '@/photo/db/query';
|
||||
import { SHOW_FILM_SIMULATIONS } from '@/site/config';
|
||||
import { sortTagsObject } from '@/tag';
|
||||
|
||||
|
||||
116
src/photo/db/index.ts
Normal file
116
src/photo/db/index.ts
Normal file
@ -0,0 +1,116 @@
|
||||
import { Camera } from '@/camera';
|
||||
import { FilmSimulation } from '@/simulation';
|
||||
import { PRIORITY_ORDER_ENABLED } from '@/site/config';
|
||||
import { parameterize } from '@/utility/string';
|
||||
|
||||
export const GENERATE_STATIC_PARAMS_LIMIT = 1000;
|
||||
export const PHOTO_DEFAULT_LIMIT = 100;
|
||||
|
||||
export type GetPhotosOptions = {
|
||||
sortBy?: 'createdAt' | 'takenAt' | 'priority';
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
query?: string;
|
||||
tag?: string;
|
||||
camera?: Camera;
|
||||
simulation?: FilmSimulation;
|
||||
takenBefore?: Date;
|
||||
takenAfterInclusive?: Date;
|
||||
hidden?: 'exclude' | 'include' | 'only';
|
||||
};
|
||||
|
||||
export const getWheresFromOptions = (
|
||||
options: GetPhotosOptions,
|
||||
initialValuesIndex = 1
|
||||
) => {
|
||||
const {
|
||||
hidden = 'exclude',
|
||||
takenBefore,
|
||||
takenAfterInclusive,
|
||||
query,
|
||||
tag,
|
||||
camera,
|
||||
simulation,
|
||||
} = options;
|
||||
|
||||
const wheres = [] as string[];
|
||||
const wheresValues = [] as (string | number)[];
|
||||
let valuesIndex = initialValuesIndex;
|
||||
|
||||
switch (hidden) {
|
||||
case 'exclude':
|
||||
wheres.push('hidden IS NOT TRUE');
|
||||
break;
|
||||
case 'only':
|
||||
wheres.push('hidden IS TRUE');
|
||||
break;
|
||||
}
|
||||
|
||||
if (takenBefore) {
|
||||
wheres.push(`taken_at > $${valuesIndex++}`);
|
||||
wheresValues.push(takenBefore.toISOString());
|
||||
}
|
||||
if (takenAfterInclusive) {
|
||||
wheres.push(`taken_at <= $${valuesIndex++}`);
|
||||
wheresValues.push(takenAfterInclusive.toISOString());
|
||||
}
|
||||
if (query) {
|
||||
// eslint-disable-next-line max-len
|
||||
wheres.push(`CONCAT(title, ' ', caption, ' ', semantic_description) ILIKE $${valuesIndex++}`);
|
||||
wheresValues.push(`%${query.toLocaleLowerCase()}%`);
|
||||
}
|
||||
if (tag) {
|
||||
wheres.push(`$${valuesIndex++}=ANY(tags)`);
|
||||
wheresValues.push(tag);
|
||||
}
|
||||
if (camera) {
|
||||
wheres.push(`LOWER(REPLACE(make, ' ', '-'))=$${valuesIndex++}`);
|
||||
wheres.push(`LOWER(REPLACE(model, ' ', '-'))=$${valuesIndex++}`);
|
||||
wheresValues.push(parameterize(camera.make, true));
|
||||
wheresValues.push(parameterize(camera.model, true));
|
||||
}
|
||||
if (simulation) {
|
||||
wheres.push(`film_simulation=$${valuesIndex++}`);
|
||||
wheresValues.push(simulation);
|
||||
}
|
||||
|
||||
return {
|
||||
wheres: wheres.length > 0
|
||||
? `WHERE ${wheres.join(' AND ')}`
|
||||
: '',
|
||||
wheresValues,
|
||||
lastValuesIndex: valuesIndex,
|
||||
};
|
||||
};
|
||||
|
||||
export const getOrderByFromOptions = (options: GetPhotosOptions) => {
|
||||
const {
|
||||
sortBy = PRIORITY_ORDER_ENABLED ? 'priority' : 'takenAt',
|
||||
} = options;
|
||||
|
||||
switch (sortBy) {
|
||||
case 'createdAt':
|
||||
return 'ORDER BY created_at DESC';
|
||||
case 'takenAt':
|
||||
return 'ORDER BY taken_at DESC';
|
||||
case 'priority':
|
||||
return 'ORDER BY priority_order ASC, taken_at DESC';
|
||||
}
|
||||
};
|
||||
|
||||
export const getLimitAndOffsetFromOptions = (
|
||||
options: GetPhotosOptions,
|
||||
initialValuesIndex = 1,
|
||||
) => {
|
||||
const {
|
||||
limit = PHOTO_DEFAULT_LIMIT,
|
||||
offset = 0,
|
||||
} = options;
|
||||
|
||||
let valuesIndex = initialValuesIndex;
|
||||
|
||||
return {
|
||||
limitAndOffset: `LIMIT $${valuesIndex++} OFFSET $${valuesIndex++}`,
|
||||
limitAndOffsetValues: [limit, offset],
|
||||
};
|
||||
};
|
||||
@ -11,28 +11,16 @@ import {
|
||||
Photo,
|
||||
PhotoDateRange,
|
||||
} from '@/photo';
|
||||
import { Camera, Cameras, createCameraKey } from '@/camera';
|
||||
import { parameterize } from '@/utility/string';
|
||||
import { Cameras, createCameraKey } from '@/camera';
|
||||
import { TagsWithMeta } from '@/tag';
|
||||
import { FilmSimulation, FilmSimulations } from '@/simulation';
|
||||
import { SHOULD_DEBUG_SQL, PRIORITY_ORDER_ENABLED } from '@/site/config';
|
||||
|
||||
export const GENERATE_STATIC_PARAMS_LIMIT = 1000;
|
||||
|
||||
const PHOTO_DEFAULT_LIMIT = 100;
|
||||
|
||||
export type GetPhotosOptions = {
|
||||
sortBy?: 'createdAt' | 'takenAt' | 'priority'
|
||||
limit?: number
|
||||
offset?: number
|
||||
query?: string
|
||||
tag?: string
|
||||
camera?: Camera
|
||||
simulation?: FilmSimulation
|
||||
takenBefore?: Date
|
||||
takenAfterInclusive?: Date
|
||||
hidden?: 'exclude' | 'include' | 'only'
|
||||
}
|
||||
import { SHOULD_DEBUG_SQL } from '@/site/config';
|
||||
import {
|
||||
GetPhotosOptions,
|
||||
getLimitAndOffsetFromOptions,
|
||||
getOrderByFromOptions,
|
||||
} from '.';
|
||||
import { getWheresFromOptions } from '.';
|
||||
|
||||
const createPhotosTable = () =>
|
||||
sql`
|
||||
@ -76,6 +64,55 @@ const runMigration01 = () =>
|
||||
ADD COLUMN IF NOT EXISTS semantic_description TEXT
|
||||
`;
|
||||
|
||||
// Wrapper for most queries for JIT table creation/migration running
|
||||
const safelyQueryPhotos = async <T>(
|
||||
callback: () => Promise<T>,
|
||||
debugMessage: string
|
||||
): Promise<T> => {
|
||||
let result: T;
|
||||
|
||||
const start = new Date();
|
||||
|
||||
try {
|
||||
result = await callback();
|
||||
} catch (e: any) {
|
||||
if (MIGRATION_FIELDS_01.some(field => new RegExp(
|
||||
`column "${field}" of relation "photos" does not exist`,
|
||||
'i',
|
||||
).test(e.message))) {
|
||||
console.log('Running migration 01 ...');
|
||||
await runMigration01();
|
||||
result = await callback();
|
||||
} else if (/relation "photos" does not exist/i.test(e.message)) {
|
||||
// If the table does not exist, create it
|
||||
console.log('Creating photos table ...');
|
||||
await createPhotosTable();
|
||||
result = await callback();
|
||||
} else if (/endpoint is in transition/i.test(e.message)) {
|
||||
console.log('sql get error: endpoint is in transition (setting timeout)');
|
||||
// Wait 5 seconds and try again
|
||||
await new Promise(resolve => setTimeout(resolve, 5000));
|
||||
try {
|
||||
result = await callback();
|
||||
} catch (e: any) {
|
||||
console.log(`sql get error on retry (after 5000ms): ${e.message} `);
|
||||
throw e;
|
||||
}
|
||||
} else {
|
||||
console.log(`sql get error: ${e.message} `);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
if (SHOULD_DEBUG_SQL && debugMessage) {
|
||||
const time =
|
||||
(((new Date()).getTime() - start.getTime()) / 1000).toFixed(2);
|
||||
console.log(`Executing sql query: ${debugMessage} (${time} seconds)`);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
// Must provide id as 8-character nanoid
|
||||
export const insertPhoto = (photo: PhotoDbInsert) =>
|
||||
safelyQueryPhotos(() => sql`
|
||||
@ -181,16 +218,15 @@ export const renamePhotoTagGlobally = (tag: string, updatedTag: string) =>
|
||||
`, 'renamePhotoTagGlobally');
|
||||
|
||||
export const deletePhoto = (id: string) =>
|
||||
safelyQueryPhotos(
|
||||
() => sql`DELETE FROM photos WHERE id=${id}`,
|
||||
'deletePhoto',
|
||||
);
|
||||
safelyQueryPhotos(() => sql`
|
||||
DELETE FROM photos WHERE id=${id}
|
||||
`, 'deletePhoto');
|
||||
|
||||
export const getPhotosMostRecentUpdate = async () =>
|
||||
safelyQueryPhotos(() => sql`
|
||||
SELECT updated_at FROM photos ORDER BY updated_at DESC LIMIT 1
|
||||
`.then(({ rows }) => rows[0] ? rows[0].updated_at as Date : undefined),
|
||||
'getPhotosMostRecentUpdate');
|
||||
`.then(({ rows }) => rows[0] ? rows[0].updated_at as Date : undefined)
|
||||
, 'getPhotosMostRecentUpdate');
|
||||
|
||||
export const getUniqueTags = async () =>
|
||||
safelyQueryPhotos(() => sql`
|
||||
@ -202,8 +238,8 @@ export const getUniqueTags = async () =>
|
||||
`.then(({ rows }): TagsWithMeta => rows.map(({ tag, count }) => ({
|
||||
tag: tag as string,
|
||||
count: parseInt(count, 10),
|
||||
}))),
|
||||
'getUniqueTags');
|
||||
})))
|
||||
, 'getUniqueTags');
|
||||
|
||||
export const getUniqueTagsHidden = async () =>
|
||||
safelyQueryPhotos(() => sql`
|
||||
@ -214,8 +250,8 @@ export const getUniqueTagsHidden = async () =>
|
||||
`.then(({ rows }): TagsWithMeta => rows.map(({ tag, count }) => ({
|
||||
tag: tag as string,
|
||||
count: parseInt(count, 10),
|
||||
}))),
|
||||
'getUniqueTagsHidden');
|
||||
})))
|
||||
, 'getUniqueTagsHidden');
|
||||
|
||||
export const getUniqueCameras = async () =>
|
||||
safelyQueryPhotos(() => sql`
|
||||
@ -230,8 +266,8 @@ export const getUniqueCameras = async () =>
|
||||
cameraKey: createCameraKey({ make, model }),
|
||||
camera: { make, model },
|
||||
count: parseInt(count, 10),
|
||||
}))),
|
||||
'getUniqueCameras');
|
||||
})))
|
||||
, 'getUniqueCameras');
|
||||
|
||||
export const getUniqueFilmSimulations = async () =>
|
||||
safelyQueryPhotos(() => sql`
|
||||
@ -244,151 +280,8 @@ export const getUniqueFilmSimulations = async () =>
|
||||
.map(({ film_simulation, count }) => ({
|
||||
simulation: film_simulation as FilmSimulation,
|
||||
count: parseInt(count, 10),
|
||||
}))),
|
||||
'getUniqueFilmSimulations');
|
||||
|
||||
const safelyQueryPhotos = async <T>(
|
||||
callback: () => Promise<T>,
|
||||
debugMessage: string
|
||||
): Promise<T> => {
|
||||
let result: T;
|
||||
|
||||
const start = new Date();
|
||||
|
||||
try {
|
||||
result = await callback();
|
||||
} catch (e: any) {
|
||||
if (MIGRATION_FIELDS_01.some(field => new RegExp(
|
||||
`column "${field}" of relation "photos" does not exist`,
|
||||
'i',
|
||||
).test(e.message))) {
|
||||
console.log('Running migration 01 ...');
|
||||
await runMigration01();
|
||||
result = await callback();
|
||||
} else if (/relation "photos" does not exist/i.test(e.message)) {
|
||||
// If the table does not exist, create it
|
||||
console.log('Creating photos table ...');
|
||||
await createPhotosTable();
|
||||
result = await callback();
|
||||
} else if (/endpoint is in transition/i.test(e.message)) {
|
||||
console.log('sql get error: endpoint is in transition (setting timeout)');
|
||||
// Wait 5 seconds and try again
|
||||
await new Promise(resolve => setTimeout(resolve, 5000));
|
||||
try {
|
||||
result = await callback();
|
||||
} catch (e: any) {
|
||||
console.log(`sql get error on retry (after 5000ms): ${e.message} `);
|
||||
throw e;
|
||||
}
|
||||
} else {
|
||||
console.log(`sql get error: ${e.message} `);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
if (SHOULD_DEBUG_SQL && debugMessage) {
|
||||
const time =
|
||||
(((new Date()).getTime() - start.getTime()) / 1000).toFixed(2);
|
||||
console.log(`Executing sql query: ${debugMessage} (${time} seconds)`);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
const getWheresFromOptions = (
|
||||
options: GetPhotosOptions,
|
||||
initialValuesIndex = 1,
|
||||
) => {
|
||||
const {
|
||||
hidden = 'exclude',
|
||||
takenBefore,
|
||||
takenAfterInclusive,
|
||||
query,
|
||||
tag,
|
||||
camera,
|
||||
simulation,
|
||||
} = options;
|
||||
|
||||
const wheres = [] as string[];
|
||||
const wheresValues = [] as (string | number)[];
|
||||
let valuesIndex = initialValuesIndex;
|
||||
|
||||
switch (hidden) {
|
||||
case 'exclude':
|
||||
wheres.push('hidden IS NOT TRUE');
|
||||
break;
|
||||
case 'only':
|
||||
wheres.push('hidden IS TRUE');
|
||||
break;
|
||||
}
|
||||
if (takenBefore) {
|
||||
wheres.push(`taken_at > $${valuesIndex++}`);
|
||||
wheresValues.push(takenBefore.toISOString());
|
||||
}
|
||||
if (takenAfterInclusive) {
|
||||
wheres.push(`taken_at <= $${valuesIndex++}`);
|
||||
wheresValues.push(takenAfterInclusive.toISOString());
|
||||
}
|
||||
if (query) {
|
||||
// eslint-disable-next-line max-len
|
||||
wheres.push(`CONCAT(title, ' ', caption, ' ', semantic_description) ILIKE $${valuesIndex++}`);
|
||||
wheresValues.push(`%${query.toLocaleLowerCase()}%`);
|
||||
}
|
||||
if (tag) {
|
||||
wheres.push(`$${valuesIndex++}=ANY(tags)`);
|
||||
wheresValues.push(tag);
|
||||
}
|
||||
if (camera) {
|
||||
wheres.push(`LOWER(REPLACE(make, ' ', '-'))=$${valuesIndex++}`);
|
||||
wheres.push(`LOWER(REPLACE(model, ' ', '-'))=$${valuesIndex++}`);
|
||||
wheresValues.push(parameterize(camera.make, true));
|
||||
wheresValues.push(parameterize(camera.model, true));
|
||||
}
|
||||
if (simulation) {
|
||||
wheres.push(`film_simulation=$${valuesIndex++}`);
|
||||
wheresValues.push(simulation);
|
||||
}
|
||||
|
||||
return {
|
||||
wheres: wheres.length > 0
|
||||
? `WHERE ${wheres.join(' AND ')}`
|
||||
: '',
|
||||
wheresValues,
|
||||
lastValuesIndex: valuesIndex,
|
||||
};
|
||||
};
|
||||
|
||||
const getOrderByFromOptions = (options: GetPhotosOptions) => {
|
||||
const {
|
||||
sortBy = PRIORITY_ORDER_ENABLED ? 'priority' : 'takenAt',
|
||||
} = options;
|
||||
|
||||
switch (sortBy) {
|
||||
case 'createdAt':
|
||||
return 'ORDER BY created_at DESC';
|
||||
case 'takenAt':
|
||||
return 'ORDER BY taken_at DESC';
|
||||
case 'priority':
|
||||
return 'ORDER BY priority_order ASC, taken_at DESC';
|
||||
}
|
||||
};
|
||||
|
||||
const getLimitAndOffsetFromOptions = (
|
||||
options: GetPhotosOptions,
|
||||
initialValuesIndex = 1,
|
||||
) => {
|
||||
const {
|
||||
limit = PHOTO_DEFAULT_LIMIT,
|
||||
offset = 0,
|
||||
} = options;
|
||||
|
||||
let valuesIndex = initialValuesIndex;
|
||||
|
||||
return {
|
||||
limitAndOffset: `LIMIT $${valuesIndex++} OFFSET $${valuesIndex++}`,
|
||||
limitAndOffsetValues: [limit, offset],
|
||||
};
|
||||
};
|
||||
})))
|
||||
, 'getUniqueFilmSimulations');
|
||||
|
||||
export const getPhotos = async (options: GetPhotosOptions = {}) =>
|
||||
safelyQueryPhotos(async () => {
|
||||
@ -483,8 +376,8 @@ export const getPhotoIds = async ({ limit }: { limit?: number }) =>
|
||||
safelyQueryPhotos(() => (limit
|
||||
? sql`SELECT id FROM photos LIMIT ${limit}`
|
||||
: sql`SELECT id FROM photos`)
|
||||
.then(({ rows }) => rows.map(({ id }) => id as string)),
|
||||
'getPhotoIds');
|
||||
.then(({ rows }) => rows.map(({ id }) => id as string))
|
||||
, 'getPhotoIds');
|
||||
|
||||
export const getPhoto = async (
|
||||
id: string,
|
||||
Loading…
Reference in New Issue
Block a user