ModelRelation: sort recursive relations by tree

This commit is contained in:
Alice Gaudon 2020-09-08 18:12:39 +02:00
parent 892b830dc4
commit e08f4fb875
4 changed files with 17 additions and 10 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "wms-core", "name": "wms-core",
"version": "0.22.0-rc.8", "version": "0.22.0-rc.9",
"description": "Node web application framework and toolbelt.", "description": "Node web application framework and toolbelt.",
"repository": "https://gitlab.com/ArisuOngaku/wms-core", "repository": "https://gitlab.com/ArisuOngaku/wms-core",
"author": "Alice Gaudon <alice@gaudon.pro>", "author": "Alice Gaudon <alice@gaudon.pro>",

View File

@ -2,7 +2,7 @@ import {query, QueryResult} from "./MysqlConnectionManager";
import {Connection} from "mysql"; import {Connection} from "mysql";
import Model from "./Model"; import Model from "./Model";
import Pagination from "../Pagination"; import Pagination from "../Pagination";
import ModelRelation, {RelationDatabaseProperties} from "./ModelRelation"; import ModelRelation, {RecursiveRelationDatabaseProperties} from "./ModelRelation";
import ModelFactory from "./ModelFactory"; import ModelFactory from "./ModelFactory";
@ -44,7 +44,7 @@ export default class ModelQuery<M extends Model> implements WhereFieldConsumer<M
private readonly subRelations: { [relation: string]: string[] } = {}; private readonly subRelations: { [relation: string]: string[] } = {};
private _pivot?: string[]; private _pivot?: string[];
private _union?: ModelQueryUnion; private _union?: ModelQueryUnion;
private _recursiveRelation?: RelationDatabaseProperties; private _recursiveRelation?: RecursiveRelationDatabaseProperties;
private constructor(type: QueryType, factory: ModelFactory<M>, fields?: SelectFields) { private constructor(type: QueryType, factory: ModelFactory<M>, fields?: SelectFields) {
this.type = type; this.type = type;
@ -135,7 +135,7 @@ export default class ModelQuery<M extends Model> implements WhereFieldConsumer<M
return this; return this;
} }
public recursive(relation: RelationDatabaseProperties): this { public recursive(relation: RecursiveRelationDatabaseProperties): this {
if (this.type !== QueryType.SELECT) throw new Error('Recursive queries are only implemented with SELECT.'); if (this.type !== QueryType.SELECT) throw new Error('Recursive queries are only implemented with SELECT.');
this._recursiveRelation = relation; this._recursiveRelation = relation;
return this; return this;
@ -191,10 +191,10 @@ export default class ModelQuery<M extends Model> implements WhereFieldConsumer<M
if (this._recursiveRelation) { if (this._recursiveRelation) {
const cteFields = fields.replace(RegExp(`${table}`, 'g'), 'o'); const cteFields = fields.replace(RegExp(`${table}`, 'g'), 'o');
query = `WITH RECURSIVE cte AS (` query = `WITH RECURSIVE cte AS (`
+ `SELECT ${fields} FROM ${table}${where}` + `SELECT ${fields},1 AS __depth, ARRAY[\`${this._recursiveRelation.idKey}\`] AS __path FROM ${table}${where}`
+ ` UNION ` + ` UNION `
+ `SELECT ${cteFields} FROM ${table} AS o, cte AS c WHERE o.\`${this._recursiveRelation.foreignKey}\`=c.\`${this._recursiveRelation.localKey}\`` + `SELECT ${cteFields}, c.__depth + 1,c.__path || o.\`${this._recursiveRelation.idKey}\` FROM ${table} AS o, cte AS c WHERE o.\`${this._recursiveRelation.foreignKey}\`=c.\`${this._recursiveRelation.localKey}\``
+ `) SELECT * FROM cte${join}${orderBy}${limit}`; + `) SELECT * FROM cte${join}${orderBy || ` ORDER BY __path`}${limit}`;
} else { } else {
query = `SELECT ${fields} FROM ${table}${join}${where}${orderBy}${limit}`; query = `SELECT ${fields} FROM ${table}${join}${where}${orderBy}${limit}`;
} }

View File

@ -201,8 +201,11 @@ export class ManyThroughModelRelation<S extends Model, O extends Model> extends
} }
export class RecursiveModelRelation<M extends Model> extends ManyModelRelation<M, M> { export class RecursiveModelRelation<M extends Model> extends ManyModelRelation<M, M> {
public constructor(model: M, foreignModelType: ModelType<M>, dbProperties: RelationDatabaseProperties) { protected readonly dbProperties: RecursiveRelationDatabaseProperties;
public constructor(model: M, foreignModelType: ModelType<M>, dbProperties: RecursiveRelationDatabaseProperties) {
super(model, foreignModelType, dbProperties); super(model, foreignModelType, dbProperties);
this.dbProperties = dbProperties;
this.constraint(query => query.recursive(this.dbProperties)); this.constraint(query => query.recursive(this.dbProperties));
} }
@ -240,3 +243,7 @@ export type PivotRelationDatabaseProperties = RelationDatabaseProperties & {
localPivotKey: string; localPivotKey: string;
foreignPivotKey: string; foreignPivotKey: string;
}; };
export type RecursiveRelationDatabaseProperties = RelationDatabaseProperties & {
idKey: string;
};

View File

@ -59,10 +59,10 @@ describe('Test ModelQuery', () => {
const query = ModelQuery.select({table: 'model'} as unknown as ModelFactory<Model>, '*'); const query = ModelQuery.select({table: 'model'} as unknown as ModelFactory<Model>, '*');
query.where('f1', 'v1'); query.where('f1', 'v1');
query.leftJoin('test').on('model.j1', 'test.j2'); query.leftJoin('test').on('model.j1', 'test.j2');
query.recursive({localKey: 'local', foreignKey: 'foreign'}); query.recursive({localKey: 'local', foreignKey: 'foreign', idKey: 'foreign'});
query.sortBy('f2', 'ASC').limit(8); query.sortBy('f2', 'ASC').limit(8);
expect(query.toString(true)).toBe("WITH RECURSIVE cte AS (SELECT `model`.* FROM `model` WHERE `f1`=? UNION SELECT o.* FROM `model` AS o, cte AS c WHERE o.`foreign`=c.`local`) SELECT * FROM cte LEFT JOIN `test` ON `model`.`j1`=`test`.`j2` ORDER BY `f2` ASC LIMIT 8"); expect(query.toString(true)).toBe("WITH RECURSIVE cte AS (SELECT `model`.*,1 AS __depth, ARRAY[`foreign`] AS __path FROM `model` WHERE `f1`=? UNION SELECT o.*, c.__depth + 1,c.__path || o.`foreign` FROM `model` AS o, cte AS c WHERE o.`foreign`=c.`local`) SELECT * FROM cte LEFT JOIN `test` ON `model`.`j1`=`test`.`j2` ORDER BY `f2` ASC LIMIT 8");
expect(query.variables).toStrictEqual(['v1']); expect(query.variables).toStrictEqual(['v1']);
}); });