diff --git a/package.json b/package.json index 458450f..e7c6efa 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "wms-core", - "version": "0.21.10", + "version": "0.21.11", "description": "Node web application framework and toolbelt.", "repository": "https://gitlab.com/ArisuOngaku/wms-core", "author": "Alice Gaudon ", diff --git a/src/db/ModelQuery.ts b/src/db/ModelQuery.ts index 1be60b2..dbdd4aa 100644 --- a/src/db/ModelQuery.ts +++ b/src/db/ModelQuery.ts @@ -5,7 +5,8 @@ import Pagination from "../Pagination"; import ModelRelation from "./ModelRelation"; import ModelFactory from "./ModelFactory"; -export default class ModelQuery { + +export default class ModelQuery implements WhereFieldConsumer { public static select(factory: ModelFactory, ...fields: string[]): ModelQuery { return new ModelQuery(QueryType.SELECT, factory, fields.length > 0 ? fields : ['*']); } @@ -32,7 +33,7 @@ export default class ModelQuery { private fields: (string | SelectFieldValue | UpdateFieldValue)[]; private _leftJoin?: string; private _leftJoinOn: WhereFieldValue[] = []; - private _where: WhereFieldValue[] = []; + private _where: (WhereFieldValue | WhereFieldValueGroup)[] = []; private _limit?: number; private _offset?: number; private _sortBy?: string; @@ -62,6 +63,27 @@ export default class ModelQuery { return this; } + public groupWhere(setter: (query: WhereFieldConsumer) => void, operator: WhereOperator = WhereOperator.AND): this { + this._where.push(new WhereFieldValueGroup(this.collectWheres(setter), operator)); + return this; + } + + private collectWheres(setter: (query: WhereFieldConsumer) => void): (WhereFieldValue | WhereFieldValueGroup)[] { + const query = this; + const wheres: (WhereFieldValue | WhereFieldValueGroup)[] = []; + setter({ + where(field: string, value: string | Date | ModelQuery | any, test: WhereTest = WhereTest.EQ, operator: WhereOperator = WhereOperator.AND) { + wheres.push(new WhereFieldValue(field, value, false, test, operator)); + return this; + }, + groupWhere(setter: (query: WhereFieldConsumer) => void, operator: WhereOperator = WhereOperator.AND) { + wheres.push(new WhereFieldValueGroup(query.collectWheres(setter), operator)) + return this; + } + }); + return wheres; + } + public limit(limit: number, offset: number = 0): this { this._limit = limit; this._offset = offset; @@ -154,11 +176,17 @@ export default class ModelQuery { this.fields?.filter(v => v instanceof FieldValue) .flatMap(v => (v).variables) .forEach(v => variables.push(v)); - this._where.flatMap(v => v.variables) + this._where.flatMap(v => this.getVariables(v)) .forEach(v => variables.push(v)); return variables; } + private getVariables(where: WhereFieldValue | WhereFieldValueGroup): any[] { + return where instanceof WhereFieldValueGroup ? + where.fields.flatMap(v => this.getVariables(v)) : + where.variables; + } + public async execute(connection?: Connection): Promise { return await query(this.build(), this.variables, connection); } @@ -327,4 +355,31 @@ class WhereFieldValue extends FieldValue { } return this._test; } -} \ No newline at end of file +} + +class WhereFieldValueGroup { + public readonly fields: (WhereFieldValue | WhereFieldValueGroup)[]; + public readonly operator: WhereOperator; + + public constructor(fields: (WhereFieldValue | WhereFieldValueGroup)[], operator: WhereOperator) { + this.fields = fields; + this.operator = operator; + } + + public toString(first: boolean = true): string { + let str = `${first ? '' : ` ${this.operator} `}(`; + let firstField = true; + for (const field of this.fields) { + str += field.toString(firstField); + firstField = false; + } + str += ')'; + return str; + } +} + +interface WhereFieldConsumer { + where(field: string, value: string | Date | ModelQuery | any, test?: WhereTest, operator?: WhereOperator): this; + + groupWhere(setter: (query: WhereFieldConsumer) => void, operator?: WhereOperator): this; +} diff --git a/test/ModelQuery.test.ts b/test/ModelQuery.test.ts new file mode 100644 index 0000000..8f0bab9 --- /dev/null +++ b/test/ModelQuery.test.ts @@ -0,0 +1,16 @@ +import ModelQuery, {WhereOperator} from "../src/db/ModelQuery"; +import ModelFactory from "../src/db/ModelFactory"; +import Model from "../src/db/Model"; + +describe('Test ModelQuery', () => { + test('groupWhere generates proper query', () => { + const query = ModelQuery.select({table: 'model'} as unknown as ModelFactory, '*'); + query.where('f1', 'v1'); + query.groupWhere(q => q.where('f2', 'v2').where('f3', 'v3') + .groupWhere(q => q.where('f4', 'v4'), WhereOperator.OR)) + .where('f5', 'v5'); + + expect(query.toString(true)).toBe('SELECT * FROM model WHERE `f1`=? AND (`f2`=? AND `f3`=? OR (`f4`=?)) AND `f5`=? '); + expect(query.variables.length).toBe(5); + }); +});