mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-15 04:51:06 +02:00
Progress on advanced filters
This commit is contained in:
@@ -24,10 +24,15 @@ const getList = async (
|
||||
res: Response
|
||||
) => {
|
||||
const { serverId } = req.params;
|
||||
const { take, skip, serverUrlId } = req.query;
|
||||
const { take, skip, serverUrlId, advancedFilters } = req.query;
|
||||
|
||||
const decodedAdvancedFilters = advancedFilters && decodeURI(advancedFilters);
|
||||
const jsonAdvancedFilters =
|
||||
decodedAdvancedFilters && JSON.parse(decodedAdvancedFilters);
|
||||
|
||||
const albums = await service.albums.findMany({
|
||||
...req.query,
|
||||
advancedFilters: jsonAdvancedFilters,
|
||||
serverId,
|
||||
skip: Number(skip),
|
||||
take: Number(take),
|
||||
@@ -66,6 +71,7 @@ const getDetailSongList = async (
|
||||
|
||||
const albums = await service.albums.findMany({
|
||||
...req.query,
|
||||
advancedFilters: undefined,
|
||||
serverId,
|
||||
skip: Number(skip),
|
||||
take: Number(take),
|
||||
|
||||
@@ -57,7 +57,7 @@ const sort = (sortBy: AlbumSort, orderBy: SortOrder) => {
|
||||
break;
|
||||
|
||||
case AlbumSort.DATE_RELEASED:
|
||||
order = { releaseDate: orderBy, year: orderBy };
|
||||
order = { releaseDate: orderBy };
|
||||
break;
|
||||
|
||||
case AlbumSort.DATE_RELEASED_YEAR:
|
||||
@@ -80,7 +80,204 @@ const sort = (sortBy: AlbumSort, orderBy: SortOrder) => {
|
||||
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 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('.');
|
||||
|
||||
if (field === 'ratings') {
|
||||
if (table === 'albums') {
|
||||
query[rootType].push({
|
||||
[field]: {
|
||||
some: {
|
||||
[relationField]: {
|
||||
[rule.operator]: rule.value,
|
||||
},
|
||||
userId: user.id,
|
||||
},
|
||||
},
|
||||
});
|
||||
} else {
|
||||
query[rootType].push({
|
||||
[table]: {
|
||||
some: {
|
||||
[field]: {
|
||||
some: {
|
||||
[relationField]: {
|
||||
[rule.operator]: rule.value,
|
||||
},
|
||||
userId: user.id,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
} else if (table === 'albums') {
|
||||
const obj = {
|
||||
[field]: {
|
||||
[rule.operator]: rule.value,
|
||||
mode: 'insensitive',
|
||||
},
|
||||
};
|
||||
|
||||
query[rootType].push(obj);
|
||||
} else {
|
||||
const obj = {
|
||||
[table]: {
|
||||
some: {
|
||||
[field]: {
|
||||
[rule.operator]: rule.value,
|
||||
mode: 'insensitive',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
query[rootType].push(obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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[],
|
||||
};
|
||||
|
||||
const operatorMap = {
|
||||
'!=': 'not',
|
||||
'!~': 'contains',
|
||||
$: 'endsWith',
|
||||
'<': 'lt',
|
||||
'<=': 'lte',
|
||||
'=': 'equals',
|
||||
'>': 'gt',
|
||||
'>=': 'gte',
|
||||
'^': 'startsWith',
|
||||
'~': 'contains',
|
||||
};
|
||||
|
||||
for (const rule of filter.rules) {
|
||||
if (rule.field && rule.operator) {
|
||||
let [table, field, relationField] = rule.field.split('.');
|
||||
const condition = rule.operator === '!~' ? 'none' : 'some';
|
||||
const op = operatorMap[rule.operator as keyof typeof operatorMap];
|
||||
|
||||
switch (table) {
|
||||
case 'albums':
|
||||
if (field === 'ratings') {
|
||||
rootQuery[rootQueryType].push({
|
||||
[field]: {
|
||||
[condition]: {
|
||||
[relationField]: {
|
||||
[op]: rule.value,
|
||||
},
|
||||
userId: user.id,
|
||||
},
|
||||
},
|
||||
});
|
||||
break;
|
||||
}
|
||||
rootQuery[rootQueryType].push({
|
||||
[field]: {
|
||||
mode: 'insensitive',
|
||||
[op]: rule.value,
|
||||
},
|
||||
});
|
||||
break;
|
||||
|
||||
default:
|
||||
if (field === 'ratings') {
|
||||
rootQuery[rootQueryType].push({
|
||||
[table]: {
|
||||
some: {
|
||||
[field]: {
|
||||
some: {
|
||||
[relationField]: {
|
||||
[op]: rule.value,
|
||||
},
|
||||
userId: user.id,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
rootQuery[rootQueryType].push({
|
||||
[table]: {
|
||||
[condition]: {
|
||||
[field]: {
|
||||
mode: 'insensitive',
|
||||
[op]: rule.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,
|
||||
};
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { Prisma } from '@prisma/client';
|
||||
import { AuthUser } from '@/middleware';
|
||||
import { OffsetPagination, SortOrder } from '@/types/types';
|
||||
import { ApiError } from '@/utils';
|
||||
import { AlbumSort } from '@helpers/albums.helpers';
|
||||
import { AdvancedFilterGroup, AlbumSort } from '@helpers/albums.helpers';
|
||||
import { helpers } from '@helpers/index';
|
||||
import { prisma } from '@lib/prisma';
|
||||
|
||||
@@ -24,6 +25,7 @@ const findById = async (user: AuthUser, options: { id: string }) => {
|
||||
};
|
||||
|
||||
export type AlbumFindManyOptions = {
|
||||
advancedFilters?: AdvancedFilterGroup;
|
||||
orderBy: SortOrder;
|
||||
serverFolderId?: string[];
|
||||
serverId: string;
|
||||
@@ -32,8 +34,16 @@ export type AlbumFindManyOptions = {
|
||||
} & OffsetPagination;
|
||||
|
||||
const findMany = async (options: AlbumFindManyOptions) => {
|
||||
const { take, serverFolderId, skip, sortBy, orderBy, user, serverId } =
|
||||
options;
|
||||
const {
|
||||
take,
|
||||
serverFolderId,
|
||||
skip,
|
||||
sortBy,
|
||||
orderBy,
|
||||
user,
|
||||
serverId,
|
||||
advancedFilters,
|
||||
} = options;
|
||||
|
||||
const serverFolderIds =
|
||||
serverFolderId ||
|
||||
@@ -42,6 +52,9 @@ const findMany = async (options: AlbumFindManyOptions) => {
|
||||
let totalEntries = 0;
|
||||
let albums;
|
||||
|
||||
const advancedFiltersQuery =
|
||||
advancedFilters && helpers.albums.advancedFilter(advancedFilters, user);
|
||||
|
||||
if (sortBy === AlbumSort.RATING) {
|
||||
const [count, result] = await prisma.$transaction([
|
||||
prisma.albumRating.count({
|
||||
@@ -92,14 +105,24 @@ const findMany = async (options: AlbumFindManyOptions) => {
|
||||
} else {
|
||||
[totalEntries, albums] = await prisma.$transaction([
|
||||
prisma.album.count({
|
||||
where: { OR: helpers.shared.serverFolderFilter(serverFolderIds) },
|
||||
where: {
|
||||
AND: [
|
||||
helpers.shared.serverFolderFilter(serverFolderIds),
|
||||
advancedFiltersQuery as Prisma.AlbumWhereInput,
|
||||
],
|
||||
},
|
||||
}),
|
||||
prisma.album.findMany({
|
||||
include: helpers.albums.include(user, { songs: false }),
|
||||
orderBy: [helpers.albums.sort(sortBy, orderBy)],
|
||||
skip,
|
||||
take,
|
||||
where: { OR: helpers.shared.serverFolderFilter(serverFolderIds) },
|
||||
where: {
|
||||
AND: [
|
||||
helpers.shared.serverFolderFilter(serverFolderIds),
|
||||
advancedFiltersQuery as Prisma.AlbumWhereInput,
|
||||
],
|
||||
},
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ const list = {
|
||||
...serverFolderIdValidation,
|
||||
...orderByValidation,
|
||||
...serverUrlIdValidation,
|
||||
advancedFilters: z.optional(z.string()),
|
||||
sortBy: z.nativeEnum(AlbumSort),
|
||||
}),
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user