209 lines
5.8 KiB
TypeScript
209 lines
5.8 KiB
TypeScript
import { parameterize } from '@/utility/string';
|
|
import { PhotoSetCategory } from '../../category';
|
|
import { Camera } from '@/camera';
|
|
import { Lens } from '@/lens';
|
|
import { APP_DEFAULT_SORT_BY, SortBy } from '../sort';
|
|
|
|
export const GENERATE_STATIC_PARAMS_LIMIT = 1000;
|
|
export const PHOTO_DEFAULT_LIMIT = 100;
|
|
|
|
// These must mirror utility/string.ts parameterization
|
|
const CHARACTERS_TO_REMOVE = [',', '/'];
|
|
const CHARACTERS_TO_REPLACE = ['+', '&', '|', ':', '_', ' '];
|
|
|
|
const parameterizeForDb = (field: string) =>
|
|
`REGEXP_REPLACE(
|
|
REGEXP_REPLACE(
|
|
LOWER(TRIM(${field})),
|
|
'[${CHARACTERS_TO_REMOVE.join('')}]', '', 'g'
|
|
),
|
|
'[${CHARACTERS_TO_REPLACE.join('')}]', '-', 'g'
|
|
)`;
|
|
|
|
export type PhotoQueryOptions = {
|
|
sortBy?: SortBy
|
|
sortWithPriority?: boolean
|
|
limit?: number
|
|
offset?: number
|
|
query?: string
|
|
maximumAspectRatio?: number
|
|
takenBefore?: Date
|
|
takenAfterInclusive?: Date
|
|
updatedBefore?: Date
|
|
excludeFromFeeds?: boolean
|
|
hidden?: 'exclude' | 'include' | 'only'
|
|
} & Omit<PhotoSetCategory, 'camera' | 'lens'> & {
|
|
camera?: Partial<Camera>
|
|
lens?: Partial<Lens>
|
|
};
|
|
|
|
export const areOptionsSensitive = (options: PhotoQueryOptions) =>
|
|
options.hidden === 'include' || options.hidden === 'only';
|
|
|
|
export const getWheresFromOptions = (
|
|
options: PhotoQueryOptions,
|
|
initialValuesIndex = 1,
|
|
) => {
|
|
const {
|
|
hidden = 'exclude',
|
|
excludeFromFeeds,
|
|
takenBefore,
|
|
takenAfterInclusive,
|
|
updatedBefore,
|
|
query,
|
|
maximumAspectRatio,
|
|
recent,
|
|
year,
|
|
tag,
|
|
camera,
|
|
lens,
|
|
film,
|
|
recipe,
|
|
focal,
|
|
} = 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 (excludeFromFeeds) {
|
|
wheres.push('exclude_from_feeds IS NOT TRUE');
|
|
}
|
|
if (takenBefore) {
|
|
wheres.push(`taken_at < $${valuesIndex++}`);
|
|
wheresValues.push(takenBefore.toISOString());
|
|
}
|
|
if (takenAfterInclusive) {
|
|
wheres.push(`taken_at >= $${valuesIndex++}`);
|
|
wheresValues.push(takenAfterInclusive.toISOString());
|
|
}
|
|
if (updatedBefore) {
|
|
wheres.push(`updated_at < $${valuesIndex++}`);
|
|
wheresValues.push(updatedBefore.toISOString());
|
|
}
|
|
if (query) {
|
|
// eslint-disable-next-line max-len
|
|
wheres.push(`CONCAT(title, ' ', caption, ' ', semantic_description) ILIKE $${valuesIndex++}`);
|
|
wheresValues.push(`%${query.toLocaleLowerCase()}%`);
|
|
}
|
|
if (maximumAspectRatio) {
|
|
wheres.push(`aspect_ratio <= $${valuesIndex++}`);
|
|
wheresValues.push(maximumAspectRatio);
|
|
}
|
|
if (recent) {
|
|
// Newest upload must be within past 2 weeks
|
|
// eslint-disable-next-line max-len
|
|
wheres.push('(SELECT MAX(created_at) FROM photos) >= (now() - INTERVAL \'14 days\')');
|
|
// Selects must be within 1 week of newest upload
|
|
// eslint-disable-next-line max-len
|
|
wheres.push('created_at >= (SELECT MAX(created_at) - INTERVAL \'7 days\' FROM photos)');
|
|
}
|
|
if (year) {
|
|
wheres.push(`EXTRACT(YEAR FROM taken_at) = $${valuesIndex++}`);
|
|
wheresValues.push(year);
|
|
}
|
|
if (camera?.make) {
|
|
wheres.push(`${parameterizeForDb('make')}=$${valuesIndex++}`);
|
|
wheresValues.push(parameterize(camera.make));
|
|
}
|
|
if (camera?.model) {
|
|
wheres.push(`${parameterizeForDb('model')}=$${valuesIndex++}`);
|
|
wheresValues.push(parameterize(camera.model));
|
|
}
|
|
if (lens?.make) {
|
|
wheres.push(`${parameterizeForDb('lens_make')}=$${valuesIndex++}`);
|
|
wheresValues.push(parameterize(lens.make));
|
|
}
|
|
if (lens?.model) {
|
|
wheres.push(`${parameterizeForDb('lens_model')}=$${valuesIndex++}`);
|
|
// Ensure unique queries for lenses missing makes
|
|
if (!lens.make) { wheres.push('lens_make IS NULL'); }
|
|
wheresValues.push(parameterize(lens.model));
|
|
}
|
|
if (tag) {
|
|
wheres.push(`$${valuesIndex++}=ANY(tags)`);
|
|
wheresValues.push(tag);
|
|
}
|
|
if (film) {
|
|
wheres.push(`film=$${valuesIndex++}`);
|
|
wheresValues.push(film);
|
|
}
|
|
if (recipe) {
|
|
wheres.push(`recipe_title=$${valuesIndex++}`);
|
|
wheresValues.push(recipe);
|
|
}
|
|
if (focal) {
|
|
wheres.push(`focal_length=$${valuesIndex++}`);
|
|
wheresValues.push(focal);
|
|
}
|
|
|
|
return {
|
|
wheres: wheres.length > 0
|
|
? `WHERE ${wheres.join(' AND ')}`
|
|
: '',
|
|
wheresValues,
|
|
lastValuesIndex: valuesIndex,
|
|
};
|
|
};
|
|
|
|
export const getOrderByFromOptions = (options: PhotoQueryOptions) => {
|
|
const {
|
|
sortBy = APP_DEFAULT_SORT_BY,
|
|
sortWithPriority,
|
|
} = options;
|
|
|
|
switch (sortBy) {
|
|
case 'takenAt':
|
|
return sortWithPriority
|
|
? 'ORDER BY priority_order ASC, taken_at DESC'
|
|
: 'ORDER BY taken_at DESC';
|
|
case 'takenAtAsc':
|
|
return sortWithPriority
|
|
? 'ORDER BY priority_order ASC, taken_at ASC'
|
|
: 'ORDER BY taken_at ASC';
|
|
case 'createdAt':
|
|
return sortWithPriority
|
|
? 'ORDER BY priority_order ASC, created_at DESC'
|
|
: 'ORDER BY created_at DESC';
|
|
case 'createdAtAsc':
|
|
return sortWithPriority
|
|
? 'ORDER BY priority_order ASC, created_at ASC'
|
|
: 'ORDER BY created_at ASC';
|
|
// Add date sort to account for photos with same color sort
|
|
case 'color':
|
|
return sortWithPriority
|
|
? 'ORDER BY priority_order ASC, color_sort DESC, taken_at DESC'
|
|
: 'ORDER BY color_sort DESC, taken_at DESC';
|
|
case 'colorAsc':
|
|
return sortWithPriority
|
|
? 'ORDER BY priority_order ASC, color_sort ASC, taken_at ASC'
|
|
: 'ORDER BY color_sort ASC, taken_at ASC';
|
|
}
|
|
};
|
|
|
|
export const getLimitAndOffsetFromOptions = (
|
|
options: PhotoQueryOptions,
|
|
initialValuesIndex = 1,
|
|
) => {
|
|
const {
|
|
limit = PHOTO_DEFAULT_LIMIT,
|
|
offset = 0,
|
|
} = options;
|
|
|
|
let valuesIndex = initialValuesIndex;
|
|
|
|
return {
|
|
limitAndOffset: `LIMIT $${valuesIndex++} OFFSET $${valuesIndex++}`,
|
|
limitAndOffsetValues: [limit, offset],
|
|
};
|
|
};
|