Files
feishin/server/helpers/albums.helpers.ts
T
2022-11-03 14:45:00 -07:00

356 lines
8.3 KiB
TypeScript

import { AuthUser } from '@/middleware';
import { SortOrder } from '@/types/types';
import { songHelpers } from '@helpers/songs.helpers';
export enum AlbumSort {
DATE_ADDED = 'added',
DATE_ADDED_REMOTE = 'addedRemote',
DATE_RELEASED = 'released',
DATE_RELEASED_YEAR = 'year',
FAVORITE = 'favorite',
NAME = 'name',
RANDOM = 'random',
RATING = 'rating',
}
const include = (user: AuthUser, options: { songs?: boolean }) => {
// Prisma.AlbumInclude
const props = {
_count: {
select: {
favorites: true,
songs: true,
},
},
albumArtists: true,
artists: true,
favorites: { where: { userId: user?.id } },
genres: true,
images: true,
ratings: {
where: {
userId: user?.id,
},
},
server: true,
serverFolders: { where: { enabled: true } },
songs: options?.songs && songHelpers.findMany(user),
};
return props;
};
const sort = (sortBy: AlbumSort, orderBy: SortOrder) => {
let order;
switch (sortBy) {
case AlbumSort.NAME:
order = { name: orderBy };
break;
case AlbumSort.DATE_ADDED:
order = { createdAt: orderBy };
break;
case AlbumSort.DATE_ADDED_REMOTE:
order = { remoteCreatedAt: orderBy };
break;
case AlbumSort.DATE_RELEASED:
order = { releaseDate: orderBy };
break;
case AlbumSort.DATE_RELEASED_YEAR:
order = { releaseYear: orderBy };
break;
case AlbumSort.RATING:
order = { rating: orderBy };
break;
case AlbumSort.FAVORITE:
order = { favorite: orderBy };
break;
default:
order = { title: orderBy };
break;
}
return order;
};
export enum FilterGroupType {
AND = 'AND',
OR = 'OR',
}
export type AdvancedFilterRule = {
field: string | null;
operator: string | null;
uniqueId: string;
value: string | number | Date | undefined | null | any;
};
export type AdvancedFilterGroup = {
group: AdvancedFilterGroup[];
rules: AdvancedFilterRule[];
type: FilterGroupType;
uniqueId: string;
};
const operatorMap = {
'!=': 'not',
'!~': 'contains',
$: 'endsWith',
'<': 'lt',
'<=': 'lte',
'=': 'equals',
'>': 'gt',
'>=': 'gte',
'^': 'startsWith',
'~': 'contains',
};
const insensitiveFields = ['name'];
const advancedFilterGroup = (
groups: AdvancedFilterGroup[],
user: AuthUser,
data: any[]
) => {
if (groups.length === 0) {
return data;
}
const filterGroups: any[] = [];
for (const group of groups) {
const rootType = group.type.toUpperCase();
const query: any = {
[rootType]: [],
};
for (const rule of group.rules) {
if (rule.field && rule.operator) {
const [table, field, relationField] = rule.field.split('.');
const condition =
rule.operator === '!~' || rule.operator === '!=' ? 'none' : 'some';
const op = operatorMap[rule.operator as keyof typeof operatorMap];
const value =
field !== 'releaseDate' ? rule.value : new Date(rule.value);
switch (table) {
case 'albums':
if (field === 'ratings') {
query[rootType].push({
[field]: {
[condition]: {
[relationField]: {
[op]: value,
},
userId: user.id,
},
},
});
break;
}
if (field === 'genres') {
query[rootType].push({
[field]: {
[condition]: {
[relationField]: {
equals: value,
},
},
},
});
break;
}
query[rootType].push({
[field]: {
mode: insensitiveFields.includes(field)
? 'insensitive'
: undefined,
[op]: value,
},
});
break;
default:
if (field === 'ratings') {
query[rootType].push({
[table]: {
some: {
[field]: {
some: {
[relationField]: {
[op]: value,
},
userId: user.id,
},
},
},
},
});
break;
}
if (field === 'genres') {
query[rootType].push({
[table]: {
some: {
[field]: {
[condition]: {
[relationField]: {
equals: value,
},
},
},
},
},
});
break;
}
query[rootType].push({
[table]: {
[condition]: {
[field]: {
mode: 'insensitive',
[op]: value,
},
},
},
});
break;
}
}
}
if (group.group.length > 0) {
const b = advancedFilterGroup(group.group, user, data);
b.forEach((c) => query[rootType].push(c));
}
data.push(query);
filterGroups.push(query);
}
return filterGroups;
};
const advancedFilter = (filter: AdvancedFilterGroup, user: AuthUser) => {
const rootQueryType = filter.type.toUpperCase();
const rootQuery = {
[rootQueryType]: [] as any[],
};
for (const rule of filter.rules) {
if (rule.field && rule.operator) {
let [table, field, relationField] = rule.field.split('.');
const condition =
rule.operator === '!~' || rule.operator === '!=' ? 'none' : 'some';
const op = operatorMap[rule.operator as keyof typeof operatorMap];
const value = field !== 'releaseDate' ? rule.value : new Date(rule.value);
switch (table) {
case 'albums':
if (field === 'ratings') {
rootQuery[rootQueryType].push({
[field]: {
[condition]: {
[relationField]: {
[op]: value,
},
userId: user.id,
},
},
});
break;
}
if (field === 'genres') {
rootQuery[rootQueryType].push({
[field]: {
[condition]: {
[relationField]: {
equals: value,
},
},
},
});
break;
}
rootQuery[rootQueryType].push({
[field]: {
mode: insensitiveFields.includes(field)
? 'insensitive'
: undefined,
[op]: value,
},
});
break;
default:
if (field === 'ratings') {
rootQuery[rootQueryType].push({
[table]: {
some: {
[field]: {
some: {
[relationField]: {
[op]: value,
},
userId: user.id,
},
},
},
},
});
break;
}
if (field === 'genres') {
rootQuery[rootQueryType].push({
[table]: {
some: {
[field]: {
[condition]: {
[relationField]: {
equals: value,
},
},
},
},
},
});
break;
}
rootQuery[rootQueryType].push({
[table]: {
[condition]: {
[field]: {
mode: 'insensitive',
[op]: value,
},
},
},
});
break;
}
}
}
const groups = advancedFilterGroup(filter.group, user, []);
for (const group of groups) {
rootQuery[rootQueryType].push(group);
}
return rootQuery;
};
export const albumHelpers = {
advancedFilter,
include,
sort,
};