mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-08 21:10:12 +02:00
356 lines
8.3 KiB
TypeScript
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,
|
|
};
|