From 6a4898cbdab0818108d59b4d1f01cacb1c2c4795 Mon Sep 17 00:00:00 2001 From: Alice Gaudon Date: Fri, 4 Sep 2020 18:01:29 +0200 Subject: [PATCH] ModelRelation: add pagination --- package.json | 2 +- src/db/ModelRelation.ts | 70 +++++++++++++++++++++++++---------------- 2 files changed, 44 insertions(+), 28 deletions(-) diff --git a/package.json b/package.json index f205016..210ee4b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "wms-core", - "version": "0.21.9", + "version": "0.21.10-rc.1", "description": "Node web application framework and toolbelt.", "repository": "https://gitlab.com/ArisuOngaku/wms-core", "author": "Alice Gaudon ", diff --git a/src/db/ModelRelation.ts b/src/db/ModelRelation.ts index 1704ecb..ff42085 100644 --- a/src/db/ModelRelation.ts +++ b/src/db/ModelRelation.ts @@ -5,7 +5,6 @@ import ModelFactory from "./ModelFactory"; export default abstract class ModelRelation { protected readonly model: S; protected readonly foreignFactory: ModelFactory; - private readonly query: ModelQuery; protected readonly queryModifiers: QueryModifier[] = []; protected readonly filters: ModelFilter[] = []; protected cachedModels?: R; @@ -13,7 +12,6 @@ export default abstract class ModelRelation) { this.model = model; this.foreignFactory = foreignFactory; - this.query = this.foreignFactory.select(); } public abstract clone(): ModelRelation; @@ -28,19 +26,23 @@ export default abstract class ModelRelation { - for (const modifier of this.queryModifiers) modifier(this.query); - this.queryModifiers.splice(0, this.queryModifiers.length); // Empty modifier now that they were applied - return this.query; + protected makeQuery(): ModelQuery { + const query = this.foreignFactory.select(); + for (const modifier of this.queryModifiers) modifier(query); + return query; } public abstract getModelID(): any; protected abstract async compute(query: ModelQuery): Promise; + protected abstract applyRegularConstraints(query: ModelQuery): void; + public async get(): Promise { if (this.cachedModels === undefined) { - this.cachedModels = await this.compute(this.getFinalQuery()); + const query = this.makeQuery(); + this.applyRegularConstraints(query); + this.cachedModels = await this.compute(query); } return this.cachedModels; } @@ -84,10 +86,13 @@ export class OneModelRelation extends ModelRel } protected async compute(query: ModelQuery): Promise { - this.getFinalQuery().where(this.dbProperties.foreignKey, this.getModelID()); return await query.first(); } + protected applyRegularConstraints(query: ModelQuery): void { + query.where(this.dbProperties.foreignKey, this.getModelID()); + } + public async get(): Promise { const model = await super.get(); if (model) { @@ -104,7 +109,7 @@ export class OneModelRelation extends ModelRel const ids = relations.map(r => r.getModelID()).filter(id => id !== null && id !== undefined); if (ids.length === 0) return []; - const query = this.getFinalQuery(); + const query = this.makeQuery(); query.where(this.dbProperties.foreignKey, ids, WhereTest.IN); return await query.get(); } @@ -124,6 +129,7 @@ export class OneModelRelation extends ModelRel export class ManyModelRelation extends ModelRelation { protected readonly dbProperties: RelationDatabaseProperties; + protected readonly paginatedCache: { [perPage: number]: { [pageNumber: number]: ModelQueryResult } } = {}; constructor(model: S, foreignFactory: ModelFactory, dbProperties: RelationDatabaseProperties) { super(model, foreignFactory); @@ -143,10 +149,13 @@ export class ManyModelRelation extends ModelRe } protected async compute(query: ModelQuery): Promise { - this.getFinalQuery().where(this.dbProperties.foreignKey, this.getModelID()); return await query.get(); } + protected applyRegularConstraints(query: ModelQuery): void { + query.where(this.dbProperties.foreignKey, this.getModelID()); + } + public async get(): Promise { let models = await super.get(); for (const filter of this.filters) { @@ -161,11 +170,25 @@ export class ManyModelRelation extends ModelRe return models; } + public async paginate(page: number, perPage: number): Promise> { + if (!this.paginatedCache[perPage]) this.paginatedCache[perPage] = {}; + + const cache = this.paginatedCache[perPage]; + + if (!cache[page]) { + const query = this.makeQuery(); + this.applyRegularConstraints(query); + cache[page] = await query.paginate(page, perPage); + } + + return cache[page]; + } + public async eagerLoad(relations: ModelRelation[]): Promise> { const ids = relations.map(r => r.getModelID()).filter(id => id !== null && id !== undefined); if (ids.length === 0) return []; - const query = this.getFinalQuery(); + const query = this.makeQuery(); query.where(this.dbProperties.foreignKey, ids, WhereTest.IN); return await query.get(); } @@ -175,11 +198,11 @@ export class ManyModelRelation extends ModelRe } } -export class ManyThroughModelRelation extends ModelRelation { +export class ManyThroughModelRelation extends ManyModelRelation { protected readonly dbProperties: PivotRelationDatabaseProperties; constructor(model: S, foreignFactory: ModelFactory, dbProperties: PivotRelationDatabaseProperties) { - super(model, foreignFactory); + super(model, foreignFactory, dbProperties); this.dbProperties = dbProperties; this.constraint(query => query .leftJoin(`${this.dbProperties.pivotTable} as pivot`) @@ -191,34 +214,27 @@ export class ManyThroughModelRelation extends return new ManyThroughModelRelation(this.model, this.foreignFactory, this.dbProperties); } + public cloneReduceToOne(): OneModelRelation { + throw new Error('Cannot reduce many through relation to one model.'); + } + public getModelID(): any { return this.model[this.dbProperties.localKey]; } protected async compute(query: ModelQuery): Promise { - this.getFinalQuery().where(`pivot.${this.dbProperties.localPivotKey}`, this.getModelID()); return await query.get(); } - public async get(): Promise { - let models = await super.get(); - for (const filter of this.filters) { - const newModels = []; - for (const model of models) { - if (await filter(model)) { - newModels.push(model); - } - } - models = newModels; - } - return models; + protected applyRegularConstraints(query: ModelQuery): void { + query.where(`pivot.${this.dbProperties.localPivotKey}`, this.getModelID()); } public async eagerLoad(relations: ModelRelation[]): Promise> { const ids = relations.map(r => r.getModelID()); if (ids.length === 0) return []; - const query = this.getFinalQuery(); + const query = this.makeQuery(); query.where(`pivot.${this.dbProperties.localPivotKey}`, ids, WhereTest.IN); query.pivot(`pivot.${this.dbProperties.localPivotKey}`, `pivot.${this.dbProperties.foreignPivotKey}`); return await query.get();