diff --git a/server/helpers/albums.helpers.ts b/server/helpers/albums.helpers.ts index 1b12ff76e..1d086d351 100644 --- a/server/helpers/albums.helpers.ts +++ b/server/helpers/albums.helpers.ts @@ -99,6 +99,21 @@ export type AdvancedFilterGroup = { uniqueId: string; }; +const operatorMap = { + '!=': 'not', + '!~': 'contains', + $: 'endsWith', + '<': 'lt', + '<=': 'lte', + '=': 'equals', + '>': 'gt', + '>=': 'gte', + '^': 'startsWith', + '~': 'contains', +}; + +const insensitiveFields = ['name']; + const advancedFilterGroup = ( groups: AdvancedFilterGroup[], user: AuthUser, @@ -119,57 +134,65 @@ const advancedFilterGroup = ( for (const rule of group.rules) { if (rule.field && rule.operator) { const [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') { + query[rootType].push({ + [field]: { + [condition]: { + [relationField]: { + [op]: rule.value, + }, + userId: user.id, + }, + }, + }); + break; + } - if (field === 'ratings') { - if (table === 'albums') { query[rootType].push({ [field]: { - some: { - [relationField]: { - [rule.operator]: rule.value, - }, - userId: user.id, - }, + mode: insensitiveFields.includes(field) + ? 'insensitive' + : undefined, + [op]: rule.value, }, }); - } else { - query[rootType].push({ - [table]: { - some: { - [field]: { - some: { - [relationField]: { - [rule.operator]: rule.value, + break; + + default: + if (field === 'ratings') { + query[rootType].push({ + [table]: { + some: { + [field]: { + some: { + [relationField]: { + [op]: rule.value, + }, + userId: user.id, }, - userId: user.id, }, }, }, - }, - }); - } - } else if (table === 'albums') { - const obj = { - [field]: { - [rule.operator]: rule.value, - mode: 'insensitive', - }, - }; + }); + break; + } - query[rootType].push(obj); - } else { - const obj = { - [table]: { - some: { - [field]: { - [rule.operator]: rule.value, - mode: 'insensitive', + query[rootType].push({ + [table]: { + [condition]: { + [field]: { + mode: 'insensitive', + [op]: rule.value, + }, }, }, - }, - }; - - query[rootType].push(obj); + }); + break; } } } @@ -192,19 +215,6 @@ const advancedFilter = (filter: AdvancedFilterGroup, user: AuthUser) => { [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('.'); @@ -226,9 +236,12 @@ const advancedFilter = (filter: AdvancedFilterGroup, user: AuthUser) => { }); break; } + rootQuery[rootQueryType].push({ [field]: { - mode: 'insensitive', + mode: insensitiveFields.includes(field) + ? 'insensitive' + : undefined, [op]: rule.value, }, }); diff --git a/src/renderer/features/albums/components/advanced-filters.tsx b/src/renderer/features/albums/components/advanced-filters.tsx index e41afeb18..6500629a7 100644 --- a/src/renderer/features/albums/components/advanced-filters.tsx +++ b/src/renderer/features/albums/components/advanced-filters.tsx @@ -1,4 +1,4 @@ -import { Box, Stack, Group } from '@mantine/core'; +import { Stack, Group } from '@mantine/core'; import dayjs from 'dayjs'; import get from 'lodash/get'; import set from 'lodash/set'; @@ -75,7 +75,7 @@ const FILTER_GROUP_OPTIONS_DATA = [ const FILTER_OPTIONS_DATA = [ { - label: 'Artist Name', + label: 'Artist Title', value: 'artists.name', }, { @@ -87,7 +87,7 @@ const FILTER_OPTIONS_DATA = [ value: 'artists.genre', }, { - label: 'Album Artist Name', + label: 'Album Artist Title', value: 'albumArtists.name', }, { @@ -99,7 +99,7 @@ const FILTER_OPTIONS_DATA = [ value: 'albumArtists.genre', }, { - label: 'Album Name', + label: 'Album Title', value: 'albums.name', }, { @@ -112,7 +112,7 @@ const FILTER_OPTIONS_DATA = [ }, { label: 'Album Year', - value: 'albums.year', + value: 'albums.releaseYear', }, { label: 'Album Release Date', @@ -127,7 +127,7 @@ const FILTER_OPTIONS_DATA = [ value: 'albums.dateAdded', }, { - label: 'Track Name', + label: 'Track Title', value: 'songs.name', }, { @@ -171,7 +171,7 @@ const OPTIONS_MAP = { 'albums.releaseDate': { type: 'date', }, - 'albums.year': { + 'albums.releaseYear': { type: 'number', }, 'artists.genre': { @@ -194,10 +194,47 @@ const OPTIONS_MAP = { }, }; +export const formatAdvancedFiltersGroups = (groups: AdvancedFilterGroup[]) => { + const filterGroups: any[] = []; + + for (const group of groups) { + const rules = group.rules + .filter((rule) => rule.field && rule.operator && rule.value) + .map((rule) => ({ ...rule, uniqueId: undefined })); + + const updatedGroup = { ...group, rules, uniqueId: undefined }; + + if (group.group.length > 0) { + const nestedRuleGroup = formatAdvancedFiltersGroups(group.group); + nestedRuleGroup.forEach((group) => groups.push(group)); + } + + if (updatedGroup.rules.length > 0) { + filterGroups.push(updatedGroup); + } + } + + return filterGroups; +}; + +// Prevent query key from constantly changing due to empty rules or groups +export const formatAdvancedFiltersQuery = (filter: AdvancedFilterGroup) => { + const updatedFilter = { + ...filter, + group: formatAdvancedFiltersGroups(filter.group), + rules: filter.rules + .filter((rule) => rule.field && rule.operator && rule.value) + .map((rule) => ({ ...rule, uniqueId: undefined })), + }; + + return updatedFilter; +}; + interface FilterOptionProps { data: AdvancedFilterRule; groupIndex: number[]; level: number; + noRemove: boolean; onChangeField: (args: any) => void; onChangeOperator: (args: any) => void; onChangeValue: (args: any) => void; @@ -209,6 +246,7 @@ const FilterOption = ({ level, onDeleteRule, groupIndex, + noRemove, onChangeField, onChangeOperator, onChangeValue, @@ -254,37 +292,45 @@ const FilterOption = ({ const filterOperatorMap = { date: ( ), number: ( ), @@ -295,27 +341,30 @@ const FilterOption = ({ ), 'albums.name': ( ), 'albums.playCount': ( handleChangeValue(e)} /> ), 'albums.ratings.value': ( ), @@ -371,19 +425,21 @@ const FilterOption = ({ ), - 'albums.year': ( + 'albums.releaseYear': ( ), @@ -391,60 +447,75 @@ const FilterOption = ({ {field ? ( @@ -453,14 +524,15 @@ const FilterOption = ({ .type as keyof typeof filterOperatorMap ] ) : ( - + )} {field ? ( filterInputValueMap[field as keyof typeof filterInputValueMap] ) : ( - + )}