Skip to content

Commit 8787e49

Browse files
committed
Added cascade_filter function to allow for the exact output needed
1 parent 33120e9 commit 8787e49

File tree

2 files changed

+147
-17
lines changed

2 files changed

+147
-17
lines changed

server/src/generic-controller.ts

Lines changed: 145 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -353,27 +353,157 @@ export class GenericController {
353353
return Promise.all(
354354
records.map((r) => this.getById(modelName, r.getDataValue("id"))),
355355
);
356-
/*
356+
}
357357

358-
We need it to return like this
358+
/** Filter records dynamically based on provided query parameters */
359+
static async cascade_filter<T extends ModelKey>(modelName: T, filters: any) {
360+
console.log(`Filtering ${modelName} with filters:`, filters);
359361

360-
return records.map((paper) => {
361-
const paperData = paper.get({ plain: true });
362+
const formattedName = this.formatModelName(modelName);
363+
const model = models[formattedName];
364+
365+
if (!model) {
366+
return { error: `Invalid model name: ${modelName}`, status: 400 };
367+
}
368+
369+
// Prepare conditions for each model
370+
const whereConditions: Record<string, any> = {};
371+
const includeRelations: any[] = [];
372+
373+
// Extract filters for different models
374+
const modelFilters: Record<string, any> = {
375+
paper: {},
376+
parts: {},
377+
tids: {},
378+
sees: {},
379+
dds: {},
380+
};
381+
382+
for (const key in filters) {
383+
if (!filters[key]) continue;
384+
385+
if (key.includes(".")) {
386+
const [relation, field] = key.split(".");
387+
if (modelFilters[relation] !== undefined) {
388+
modelFilters[relation][field] = filters[key];
389+
} else {
390+
console.warn(`Invalid related model in filter: ${relation}`);
391+
return { error: `Invalid relation '${relation}'`, status: 400 };
392+
}
393+
} else {
394+
modelFilters.paper[key] = filters[key]; // Direct paper filters
395+
}
396+
}
362397

398+
console.log("Parsed Filters:", modelFilters);
399+
400+
// Build Include Array Dynamically
401+
const buildInclude = (modelKey: string, modelRef: any, alias: string) => {
402+
if (Object.keys(modelFilters[modelKey]).length > 0) {
403+
return {
404+
model: modelRef,
405+
as: alias,
406+
required: true, // Enforce filtering at SQL level
407+
where: modelFilters[modelKey],
408+
attributes: {
409+
exclude: ["createdAt", "updatedAt", "paperId", "partId"],
410+
}, // Exclude unwanted fields
411+
};
412+
}
363413
return {
364-
...paperData,
365-
authors: Array.isArray(paperData.authors) ? paperData.authors : [],
366-
parts: Array.isArray(paperData.parts)
367-
? paperData.parts.map((part: any) => ({
368-
...part,
369-
tids: Array.isArray(part.tids) ? part.tids : [],
370-
sees: Array.isArray(part.sees) ? part.sees : [],
371-
dds: Array.isArray(part.dds) ? part.dds : [],
372-
}))
373-
: [],
414+
model: modelRef,
415+
as: alias,
416+
attributes: {
417+
exclude: ["createdAt", "updatedAt", "paperId", "partId"],
418+
}, // Exclude unwanted fields
374419
};
420+
};
421+
422+
// Fetch filtered records
423+
const records = await models.Paper.findAll({
424+
where: modelFilters.paper,
425+
include: [
426+
{
427+
model: models.Author,
428+
as: "authors",
429+
attributes: { exclude: ["createdAt", "updatedAt", "paper_author"] },
430+
through: {
431+
attributes: [], // This will exclude the 'paper_author' relationship table fields
432+
},
433+
}, // Exclude fields for authors
434+
buildInclude("parts", models.Part, "parts"),
435+
{
436+
model: models.Part,
437+
as: "parts",
438+
required: Object.keys(modelFilters.parts).length > 0,
439+
where: modelFilters.parts,
440+
include: [
441+
buildInclude("tids", models.Tid, "tids"),
442+
buildInclude("sees", models.See, "sees"),
443+
buildInclude("dds", models.Dd, "dds"),
444+
],
445+
attributes: { exclude: ["createdAt", "updatedAt", "paper_part"] }, // Exclude fields for parts
446+
through: {
447+
attributes: [], // This will exclude the 'paper_author' relationship table fields
448+
},
449+
},
450+
],
451+
attributes: { exclude: ["createdAt", "updatedAt"] }, // Exclude fields for parts
452+
});
453+
454+
console.log("Found records:", records.length);
455+
456+
// Flatten Results & Remove Unfiltered Tests
457+
return records.flatMap((paper) => {
458+
const paperData = paper.get({ plain: true });
459+
const { parts, ...paperWithoutParts } = paperData;
460+
461+
return parts.flatMap((part: any) => {
462+
const { tids, sees, dds, ...partWithoutTests } = part;
463+
464+
return [
465+
...tids
466+
.filter(
467+
(t: { [x: string]: any }) =>
468+
Object.keys(modelFilters.tids).length === 0 ||
469+
modelFilters.tids.some(
470+
(f: string | number) => t[f] === filters[`tids.${f}`],
471+
),
472+
)
473+
.map((tid: any) => ({
474+
paper: paperWithoutParts,
475+
part: partWithoutTests,
476+
tid,
477+
})),
478+
...sees
479+
.filter(
480+
(s: { [x: string]: any }) =>
481+
Object.keys(modelFilters.sees).length === 0 ||
482+
modelFilters.sees.some(
483+
(f: string | number) => s[f] === filters[`sees.${f}`],
484+
),
485+
)
486+
.map((see: any) => ({
487+
paper: paperWithoutParts,
488+
part: partWithoutTests,
489+
see,
490+
})),
491+
...dds
492+
.filter(
493+
(d: { [x: string]: any }) =>
494+
Object.keys(modelFilters.dds).length === 0 ||
495+
modelFilters.dds.some(
496+
(f: string | number) => d[f] === filters[`dds.${f}`],
497+
),
498+
)
499+
.map((dd: any) => ({
500+
paper: paperWithoutParts,
501+
part: partWithoutTests,
502+
dd,
503+
})),
504+
];
505+
});
375506
});
376-
*/
377507
}
378508

379509
/** Create full paper along with related entities */

server/src/routes/cascade-router.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@ const router = express.Router();
99
export default function cascadeRouter(dbController: GenericController): Router {
1010
const router = Router();
1111

12-
router.get("/:model/filter", async (req: Request, res: Response) => {
12+
router.get("/papers/filter", async (req: Request, res: Response) => {
1313
try {
1414
const modelName = req.params.model;
1515
const filters = req.query; // Extract query parameters
1616

1717
console.log(`Filtering ${modelName} with`, filters);
1818

19-
const records = await GenericController.filter(modelName, filters);
19+
const records = await GenericController.cascade_filter("papers", filters);
2020

2121
if (!Array.isArray(records) && "error" in records) {
2222
res.status(records.status || 500).json({ error: records.error });

0 commit comments

Comments
 (0)