Standardize, rename queries

This commit is contained in:
Sam Becker 2024-05-20 11:48:56 -05:00
parent 17532465db
commit baa3edcf9f
2 changed files with 81 additions and 91 deletions

View File

@ -2,11 +2,11 @@
import {
GetPhotosOptions,
sqlDeletePhoto,
sqlInsertPhoto,
sqlDeletePhotoTagGlobally,
sqlUpdatePhoto,
sqlRenamePhotoTagGlobally,
deletePhoto,
insertPhoto,
deletePhotoTagGlobally,
updatePhoto,
renamePhotoTagGlobally,
getPhoto,
getPhotos,
} from '@/photo/db';
@ -53,7 +53,7 @@ export const createPhotoAction = async (formData: FormData) =>
if (updatedUrl) {
photo.url = updatedUrl;
await sqlInsertPhoto(photo);
await insertPhoto(photo);
revalidateAllKeysAndPaths();
redirect(PATH_ADMIN_PHOTOS);
}
@ -71,7 +71,7 @@ export const updatePhotoAction = async (formData: FormData) =>
if (url) { photo.url = url; }
}
await sqlUpdatePhoto(photo);
await updatePhoto(photo);
revalidatePhoto(photo.id);
@ -89,7 +89,7 @@ export const toggleFavoritePhotoAction = async (
photo.tags = tags.some(tag => tag === TAG_FAVS)
? tags.filter(tag => !isTagFavs(tag))
: [...tags, TAG_FAVS];
await sqlUpdatePhoto(convertPhotoToPhotoDbInsert(photo));
await updatePhoto(convertPhotoToPhotoDbInsert(photo));
revalidateAllKeysAndPaths();
if (shouldRedirect) {
redirect(pathForPhoto(photoId));
@ -103,7 +103,7 @@ export const deletePhotoAction = async (
shouldRedirect?: boolean,
) =>
runAuthenticatedAdminServerAction(async () => {
await sqlDeletePhoto(photoId).then(() => deleteStorageUrl(photoUrl));
await deletePhoto(photoId).then(() => deleteStorageUrl(photoUrl));
revalidateAllKeysAndPaths();
if (shouldRedirect) {
redirect(PATH_ROOT);
@ -122,7 +122,7 @@ export const deletePhotoTagGloballyAction = async (formData: FormData) =>
runAuthenticatedAdminServerAction(async () => {
const tag = formData.get('tag') as string;
await sqlDeletePhotoTagGlobally(tag);
await deletePhotoTagGlobally(tag);
revalidatePhotosKey();
revalidateAdminPaths();
@ -134,7 +134,7 @@ export const renamePhotoTagGloballyAction = async (formData: FormData) =>
const updatedTag = formData.get('updatedTag') as string;
if (tag && updatedTag && tag !== updatedTag) {
await sqlRenamePhotoTagGlobally(tag, updatedTag);
await renamePhotoTagGlobally(tag, updatedTag);
revalidatePhotosKey();
revalidateTagsKey();
redirect(PATH_ADMIN_TAGS);
@ -185,7 +185,7 @@ export const syncPhotoExifDataAction = async (formData: FormData) =>
...convertPhotoToFormData(photo),
...photoFormExif,
});
await sqlUpdatePhoto(photoFormDbInsert);
await updatePhoto(photoFormDbInsert);
revalidatePhotosKey();
}
}

View File

@ -21,7 +21,20 @@ export const GENERATE_STATIC_PARAMS_LIMIT = 1000;
const PHOTO_DEFAULT_LIMIT = 100;
const sqlCreatePhotosTable = () =>
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'
}
const createPhotosTable = () =>
sql`
CREATE TABLE IF NOT EXISTS photos (
id VARCHAR(8) PRIMARY KEY,
@ -56,7 +69,7 @@ const sqlCreatePhotosTable = () =>
// Migration 01
const MIGRATION_FIELDS_01 = ['caption', 'semantic_description'];
const sqlRunMigration01 = () =>
const runMigration01 = () =>
sql`
ALTER TABLE photos
ADD COLUMN IF NOT EXISTS caption TEXT,
@ -64,7 +77,7 @@ const sqlRunMigration01 = () =>
`;
// Must provide id as 8-character nanoid
export const sqlInsertPhoto = (photo: PhotoDbInsert) =>
export const insertPhoto = (photo: PhotoDbInsert) =>
safelyQueryPhotos(() => sql`
INSERT INTO photos (
id,
@ -120,9 +133,9 @@ export const sqlInsertPhoto = (photo: PhotoDbInsert) =>
${photo.takenAt},
${photo.takenAtNaive}
)
`, 'sqlInsertPhoto');
`, 'insertPhoto');
export const sqlUpdatePhoto = (photo: PhotoDbInsert) =>
export const updatePhoto = (photo: PhotoDbInsert) =>
safelyQueryPhotos(() => sql`
UPDATE photos SET
url=${photo.url},
@ -151,36 +164,33 @@ export const sqlUpdatePhoto = (photo: PhotoDbInsert) =>
taken_at_naive=${photo.takenAtNaive},
updated_at=${(new Date()).toISOString()}
WHERE id=${photo.id}
`, 'sqlUpdatePhoto');
`, 'updatePhoto');
export const sqlDeletePhotoTagGlobally = (tag: string) =>
export const deletePhotoTagGlobally = (tag: string) =>
safelyQueryPhotos(() => sql`
UPDATE photos
SET tags=ARRAY_REMOVE(tags, ${tag})
WHERE ${tag}=ANY(tags)
`, 'sqlDeletePhotoTagGlobally');
`, 'deletePhotoTagGlobally');
export const sqlRenamePhotoTagGlobally = (tag: string, updatedTag: string) =>
export const renamePhotoTagGlobally = (tag: string, updatedTag: string) =>
safelyQueryPhotos(() => sql`
UPDATE photos
SET tags=ARRAY_REPLACE(tags, ${tag}, ${updatedTag})
WHERE ${tag}=ANY(tags)
`, 'sqlRenamePhotoTagGlobally');
`, 'renamePhotoTagGlobally');
export const sqlDeletePhoto = (id: string) =>
export const deletePhoto = (id: string) =>
safelyQueryPhotos(
() => sql`DELETE FROM photos WHERE id=${id}`,
'sqlDeletePhoto',
'deletePhoto',
);
const sqlGetPhoto = (id: string, includeHidden?: boolean) => includeHidden
? sql<PhotoDb>`SELECT * FROM photos WHERE id=${id} LIMIT 1`
// eslint-disable-next-line max-len
: sql<PhotoDb>`SELECT * FROM photos WHERE id=${id} AND hidden IS NOT TRUE LIMIT 1`;
const sqlGetPhotosMostRecentUpdate = async () => sql`
SELECT updated_at FROM photos ORDER BY updated_at DESC LIMIT 1
`.then(({ rows }) => rows[0] ? rows[0].updated_at as Date : undefined);
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');
export const getUniqueTags = async () =>
safelyQueryPhotos(() => sql`
@ -193,8 +203,7 @@ export const getUniqueTags = async () =>
tag: tag as string,
count: parseInt(count, 10),
}))),
'getUniqueTags',
);
'getUniqueTags');
export const getUniqueTagsHidden = async () =>
safelyQueryPhotos(() => sql`
@ -206,8 +215,7 @@ export const getUniqueTagsHidden = async () =>
tag: tag as string,
count: parseInt(count, 10),
}))),
'getUniqueTagsHidden',
);
'getUniqueTagsHidden');
export const getUniqueCameras = async () =>
safelyQueryPhotos(() => sql`
@ -223,8 +231,7 @@ export const getUniqueCameras = async () =>
camera: { make, model },
count: parseInt(count, 10),
}))),
'getUniqueCameras',
);
'getUniqueCameras');
export const getUniqueFilmSimulations = async () =>
safelyQueryPhotos(() => sql`
@ -238,21 +245,7 @@ export const getUniqueFilmSimulations = async () =>
simulation: film_simulation as FilmSimulation,
count: parseInt(count, 10),
}))),
'getUniqueFilmSimulations',
);
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'
}
'getUniqueFilmSimulations');
const safelyQueryPhotos = async <T>(
callback: () => Promise<T>,
@ -270,12 +263,12 @@ const safelyQueryPhotos = async <T>(
'i',
).test(e.message))) {
console.log('Running migration 01 ...');
await sqlRunMigration01();
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 sqlCreatePhotosTable();
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)');
@ -426,9 +419,9 @@ export const getPhotos = async (options: GetPhotosOptions = {}) =>
sql.push(limitAndOffset);
values.push(...limitAndOffsetValues);
return query(sql.join(' '), values);
}, 'getPhotos')
.then(({ rows }) => rows.map(parsePhotoFromDb));
return query(sql.join(' '), values)
.then(({ rows }) => rows.map(parsePhotoFromDb));
}, 'getPhotos');
export const getPhotosNearId = async (
photoId: string,
@ -460,16 +453,16 @@ export const getPhotosNearId = async (
LIMIT $${valuesIndex++}
`,
[...wheresValues, photoId, limit]
);
}, `getPhotosNearId: ${photoId}`)
.then(({ rows }) => {
const photo = rows.find(({ id }) => id === photoId);
const indexNumber = photo ? parseInt(photo.row_number) : undefined;
return {
photos: rows.map(parsePhotoFromDb),
indexNumber,
};
});
)
.then(({ rows }) => {
const photo = rows.find(({ id }) => id === photoId);
const indexNumber = photo ? parseInt(photo.row_number) : undefined;
return {
photos: rows.map(parsePhotoFromDb),
indexNumber,
};
});
}, `getPhotosNearId: ${photoId}`);
export const getPhotosMeta = (options: GetPhotosOptions = {}) =>
safelyQueryPhotos(async () => {
@ -477,36 +470,33 @@ export const getPhotosMeta = (options: GetPhotosOptions = {}) =>
let sql = 'SELECT COUNT(*), MIN(taken_at_naive) as start, MAX(taken_at_naive) as end FROM photos';
const { wheres, wheresValues } = getWheresFromOptions(options);
if (wheres) { sql += ` ${wheres}`; }
return query(sql, wheresValues);
}, 'getPhotosMeta')
.then(({ rows }) => ({
count: parseInt(rows[0].count, 10),
...rows[0]?.start && rows[0]?.end
? { dateRange: rows[0] as PhotoDateRange }
: undefined,
}));
return query(sql, wheresValues)
.then(({ rows }) => ({
count: parseInt(rows[0].count, 10),
...rows[0]?.start && rows[0]?.end
? { dateRange: rows[0] as PhotoDateRange }
: undefined,
}));
}, 'getPhotosMeta');
export const getPhotoIds = async ({ limit }: { limit?: number }) =>
safelyQueryPhotos(() => limit
safelyQueryPhotos(() => (limit
? sql`SELECT id FROM photos LIMIT ${limit}`
: sql`SELECT id FROM photos`,
'getPhotoIds')
.then(({ rows }) => rows.map(({ id }) => id as string));
: sql`SELECT id FROM photos`)
.then(({ rows }) => rows.map(({ id }) => id as string)),
'getPhotoIds');
export const getPhoto = async (
id: string,
includeHidden?: boolean,
): Promise<Photo | undefined> =>
safelyQueryPhotos(() => {
safelyQueryPhotos(async () => {
// Check for photo id forwarding and convert short ids to uuids
const photoId = translatePhotoId(id);
return sqlGetPhoto(photoId, includeHidden);
}, 'sqlGetPhoto')
.then(({ rows }) => rows.map(parsePhotoFromDb))
.then(photos => photos.length > 0 ? photos[0] : undefined);
export const getPhotosMostRecentUpdate = () =>
safelyQueryPhotos(
sqlGetPhotosMostRecentUpdate,
'getPhotosMostRecentUpdate',
);
return (includeHidden
? sql<PhotoDb>`SELECT * FROM photos WHERE id=${photoId} LIMIT 1`
// eslint-disable-next-line max-len
: sql<PhotoDb>`SELECT * FROM photos WHERE id=${photoId} AND hidden IS NOT TRUE LIMIT 1`)
.then(({ rows }) => rows.map(parsePhotoFromDb))
.then(photos => photos.length > 0 ? photos[0] : undefined);
}, 'getPhoto');