2020-04-22 15:52:17 +02:00
|
|
|
import {query, QueryResult} from "./MysqlConnectionManager";
|
|
|
|
import {Connection} from "mysql";
|
2020-07-24 12:13:28 +02:00
|
|
|
import Model from "./Model";
|
2020-06-27 14:36:50 +02:00
|
|
|
import Pagination from "../Pagination";
|
2020-09-05 14:55:19 +02:00
|
|
|
import ModelRelation, {RelationDatabaseProperties} from "./ModelRelation";
|
2020-07-24 12:13:28 +02:00
|
|
|
import ModelFactory from "./ModelFactory";
|
2020-04-22 15:52:17 +02:00
|
|
|
|
2020-09-04 22:15:55 +02:00
|
|
|
|
|
|
|
export default class ModelQuery<M extends Model> implements WhereFieldConsumer<M> {
|
2020-07-24 12:13:28 +02:00
|
|
|
public static select<M extends Model>(factory: ModelFactory<M>, ...fields: string[]): ModelQuery<M> {
|
|
|
|
return new ModelQuery(QueryType.SELECT, factory, fields.length > 0 ? fields : ['*']);
|
2020-04-22 15:52:17 +02:00
|
|
|
}
|
|
|
|
|
2020-07-24 12:13:28 +02:00
|
|
|
public static update<M extends Model>(factory: ModelFactory<M>, data: {
|
2020-04-22 15:52:17 +02:00
|
|
|
[key: string]: any
|
2020-06-27 14:36:50 +02:00
|
|
|
}): ModelQuery<M> {
|
2020-04-22 15:52:17 +02:00
|
|
|
const fields = [];
|
|
|
|
for (let key in data) {
|
|
|
|
if (data.hasOwnProperty(key)) {
|
2020-06-14 11:43:31 +02:00
|
|
|
fields.push(new UpdateFieldValue(key, data[key], false));
|
2020-04-22 15:52:17 +02:00
|
|
|
}
|
|
|
|
}
|
2020-07-24 12:13:28 +02:00
|
|
|
return new ModelQuery(QueryType.UPDATE, factory, fields);
|
2020-04-22 15:52:17 +02:00
|
|
|
}
|
|
|
|
|
2020-07-24 12:13:28 +02:00
|
|
|
public static delete<M extends Model>(factory: ModelFactory<M>): ModelQuery<M> {
|
|
|
|
return new ModelQuery(QueryType.DELETE, factory);
|
2020-04-22 15:52:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private readonly type: QueryType;
|
2020-07-24 12:13:28 +02:00
|
|
|
private readonly factory: ModelFactory<M>;
|
2020-04-22 15:52:17 +02:00
|
|
|
private readonly table: string;
|
2020-09-02 11:55:38 +02:00
|
|
|
private fields: (string | SelectFieldValue | UpdateFieldValue)[];
|
2020-06-14 11:43:31 +02:00
|
|
|
private _leftJoin?: string;
|
|
|
|
private _leftJoinOn: WhereFieldValue[] = [];
|
2020-09-04 22:15:55 +02:00
|
|
|
private _where: (WhereFieldValue | WhereFieldValueGroup)[] = [];
|
2020-04-22 15:52:17 +02:00
|
|
|
private _limit?: number;
|
|
|
|
private _offset?: number;
|
|
|
|
private _sortBy?: string;
|
|
|
|
private _sortDirection?: 'ASC' | 'DESC';
|
2020-06-27 14:36:50 +02:00
|
|
|
private readonly relations: string[] = [];
|
|
|
|
private _pivot?: string[];
|
2020-09-05 14:55:19 +02:00
|
|
|
private _recursiveRelation?: RelationDatabaseProperties;
|
2020-04-22 15:52:17 +02:00
|
|
|
|
2020-07-24 12:13:28 +02:00
|
|
|
private constructor(type: QueryType, factory: ModelFactory<M>, fields?: (string | SelectFieldValue | UpdateFieldValue)[]) {
|
2020-04-22 15:52:17 +02:00
|
|
|
this.type = type;
|
2020-07-24 12:13:28 +02:00
|
|
|
this.factory = factory;
|
|
|
|
this.table = factory.table;
|
2020-04-22 15:52:17 +02:00
|
|
|
this.fields = fields || [];
|
|
|
|
}
|
|
|
|
|
2020-06-14 11:43:31 +02:00
|
|
|
public leftJoin(table: string): this {
|
|
|
|
this._leftJoin = table;
|
2020-04-22 15:52:17 +02:00
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2020-06-14 11:43:31 +02:00
|
|
|
public on(field1: string, field2: string, test: WhereTest = WhereTest.EQ, operator: WhereOperator = WhereOperator.AND): this {
|
2020-09-05 14:55:19 +02:00
|
|
|
this._leftJoinOn.push(new WhereFieldValue(field1, field2.split('.').map(v => `\`${v}\``).join('.'), true, test, operator));
|
2020-06-14 11:43:31 +02:00
|
|
|
return this;
|
2020-04-22 15:52:17 +02:00
|
|
|
}
|
|
|
|
|
2020-06-27 14:36:50 +02:00
|
|
|
public where(field: string, value: string | Date | ModelQuery<any> | any, test: WhereTest = WhereTest.EQ, operator: WhereOperator = WhereOperator.AND): this {
|
2020-06-14 11:43:31 +02:00
|
|
|
this._where.push(new WhereFieldValue(field, value, false, test, operator));
|
|
|
|
return this;
|
2020-04-22 15:52:17 +02:00
|
|
|
}
|
|
|
|
|
2020-09-04 22:15:55 +02:00
|
|
|
public groupWhere(setter: (query: WhereFieldConsumer<M>) => void, operator: WhereOperator = WhereOperator.AND): this {
|
|
|
|
this._where.push(new WhereFieldValueGroup(this.collectWheres(setter), operator));
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
private collectWheres(setter: (query: WhereFieldConsumer<M>) => void): (WhereFieldValue | WhereFieldValueGroup)[] {
|
|
|
|
const query = this;
|
|
|
|
const wheres: (WhereFieldValue | WhereFieldValueGroup)[] = [];
|
|
|
|
setter({
|
|
|
|
where(field: string, value: string | Date | ModelQuery<any> | any, test: WhereTest = WhereTest.EQ, operator: WhereOperator = WhereOperator.AND) {
|
|
|
|
wheres.push(new WhereFieldValue(field, value, false, test, operator));
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
groupWhere(setter: (query: WhereFieldConsumer<M>) => void, operator: WhereOperator = WhereOperator.AND) {
|
|
|
|
wheres.push(new WhereFieldValueGroup(query.collectWheres(setter), operator))
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return wheres;
|
|
|
|
}
|
|
|
|
|
2020-06-14 11:43:31 +02:00
|
|
|
public limit(limit: number, offset: number = 0): this {
|
2020-04-22 15:52:17 +02:00
|
|
|
this._limit = limit;
|
|
|
|
this._offset = offset;
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2020-06-14 11:43:31 +02:00
|
|
|
public sortBy(field: string, direction: 'ASC' | 'DESC' = 'ASC'): this {
|
2020-04-22 15:52:17 +02:00
|
|
|
this._sortBy = field;
|
|
|
|
this._sortDirection = direction;
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2020-06-27 14:36:50 +02:00
|
|
|
/**
|
|
|
|
* @param relation The relation field name to eagerload
|
|
|
|
*/
|
|
|
|
public with(relation: string): this {
|
|
|
|
this.relations.push(relation);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public pivot(...fields: string[]): this {
|
|
|
|
this._pivot = fields;
|
2020-04-22 15:52:17 +02:00
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2020-09-05 14:55:19 +02:00
|
|
|
public recursive(relation: RelationDatabaseProperties): this {
|
|
|
|
if (this.type !== QueryType.SELECT) throw new Error('Recursive queries are only implemented with SELECT.');
|
|
|
|
this._recursiveRelation = relation;
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2020-04-22 15:52:17 +02:00
|
|
|
public toString(final: boolean = false): string {
|
|
|
|
let query = '';
|
|
|
|
|
2020-09-02 16:29:02 +02:00
|
|
|
// Prevent wildcard and fields from conflicting
|
2020-09-04 15:07:31 +02:00
|
|
|
if (this._leftJoin) {
|
2020-09-04 18:51:41 +02:00
|
|
|
this.fields = this.fields
|
|
|
|
.map(f => !f.toString().startsWith('(') && f.toString().split('.').length === 1 ? `\`${this.table}\`.${f}` : f);
|
2020-09-04 15:07:31 +02:00
|
|
|
}
|
2020-09-02 16:29:02 +02:00
|
|
|
|
|
|
|
if (this._pivot) this.fields.push(...this._pivot);
|
2020-06-27 14:36:50 +02:00
|
|
|
|
|
|
|
let fields = this.fields.join(',');
|
2020-04-22 15:52:17 +02:00
|
|
|
|
2020-06-14 11:43:31 +02:00
|
|
|
let join = '';
|
|
|
|
if (this._leftJoin) {
|
2020-09-05 14:55:19 +02:00
|
|
|
join = `LEFT JOIN \`${this._leftJoin}\` ON ${this._leftJoinOn[0]}`;
|
2020-06-14 11:43:31 +02:00
|
|
|
for (let i = 1; i < this._leftJoinOn.length; i++) {
|
|
|
|
join += this._leftJoinOn[i].toString(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-22 15:52:17 +02:00
|
|
|
let where = '';
|
|
|
|
if (this._where.length > 0) {
|
|
|
|
where = `WHERE ${this._where[0]}`;
|
|
|
|
for (let i = 1; i < this._where.length; i++) {
|
|
|
|
where += this._where[i].toString(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let limit = '';
|
|
|
|
if (typeof this._limit === 'number') {
|
|
|
|
limit = `LIMIT ${this._limit}`;
|
|
|
|
if (typeof this._offset === 'number' && this._offset !== 0) {
|
|
|
|
limit += ` OFFSET ${this._offset}`;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let orderBy = '';
|
|
|
|
if (typeof this._sortBy === 'string') {
|
2020-09-05 14:55:19 +02:00
|
|
|
orderBy = `ORDER BY \`${this._sortBy}\` ${this._sortDirection}`;
|
2020-04-22 15:52:17 +02:00
|
|
|
}
|
|
|
|
|
2020-09-05 14:55:19 +02:00
|
|
|
const table = `\`${this.table}\``;
|
|
|
|
|
2020-04-22 15:52:17 +02:00
|
|
|
switch (this.type) {
|
|
|
|
case QueryType.SELECT:
|
2020-09-05 14:55:19 +02:00
|
|
|
if (this._recursiveRelation) {
|
|
|
|
const cteFields = fields.replace(RegExp(`${table}`, 'g'), 'o');
|
|
|
|
query = `WITH RECURSIVE cte AS (`
|
|
|
|
+ `SELECT ${fields} FROM ${table} ${where}`
|
|
|
|
+ ` UNION `
|
|
|
|
+ `SELECT ${cteFields} FROM ${table} AS o, cte AS c WHERE o.\`${this._recursiveRelation.foreignKey}\`=c.\`${this._recursiveRelation.localKey}\``
|
|
|
|
+ `) SELECT * FROM cte ${join} ${orderBy} ${limit}`;
|
|
|
|
} else {
|
|
|
|
query = `SELECT ${fields} FROM ${table} ${join} ${where} ${orderBy} ${limit}`;
|
|
|
|
}
|
2020-04-22 15:52:17 +02:00
|
|
|
break;
|
|
|
|
case QueryType.UPDATE:
|
2020-09-05 14:55:19 +02:00
|
|
|
query = `UPDATE ${table} SET ${fields} ${where} ${orderBy} ${limit}`;
|
2020-04-22 15:52:17 +02:00
|
|
|
break;
|
|
|
|
case QueryType.DELETE:
|
2020-09-05 14:55:19 +02:00
|
|
|
query = `DELETE FROM ${table} ${where} ${orderBy} ${limit}`;
|
2020-04-22 15:52:17 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return final ? query : `(${query})`;
|
|
|
|
}
|
|
|
|
|
|
|
|
public build(): string {
|
|
|
|
return this.toString(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
public get variables(): any[] {
|
|
|
|
const variables: any[] = [];
|
|
|
|
this.fields?.filter(v => v instanceof FieldValue)
|
|
|
|
.flatMap(v => (<FieldValue>v).variables)
|
|
|
|
.forEach(v => variables.push(v));
|
2020-09-04 22:15:55 +02:00
|
|
|
this._where.flatMap(v => this.getVariables(v))
|
2020-04-22 15:52:17 +02:00
|
|
|
.forEach(v => variables.push(v));
|
|
|
|
return variables;
|
|
|
|
}
|
|
|
|
|
2020-09-04 22:15:55 +02:00
|
|
|
private getVariables(where: WhereFieldValue | WhereFieldValueGroup): any[] {
|
|
|
|
return where instanceof WhereFieldValueGroup ?
|
|
|
|
where.fields.flatMap(v => this.getVariables(v)) :
|
|
|
|
where.variables;
|
|
|
|
}
|
|
|
|
|
2020-06-27 14:36:50 +02:00
|
|
|
public async execute(connection?: Connection): Promise<QueryResult> {
|
|
|
|
return await query(this.build(), this.variables, connection);
|
2020-04-22 15:52:17 +02:00
|
|
|
}
|
|
|
|
|
2020-06-27 14:36:50 +02:00
|
|
|
public async get(connection?: Connection): Promise<ModelQueryResult<M>> {
|
2020-09-04 15:10:11 +02:00
|
|
|
const queryResult = await this.execute(connection);
|
2020-06-27 14:36:50 +02:00
|
|
|
const models: ModelQueryResult<M> = [];
|
2020-09-04 22:44:30 +02:00
|
|
|
models.originalData = [];
|
2020-06-27 14:36:50 +02:00
|
|
|
|
|
|
|
if (this._pivot) models.pivot = [];
|
|
|
|
|
|
|
|
// Eager loading init
|
|
|
|
const relationMap: { [p: string]: ModelRelation<any, any, any>[] } = {};
|
|
|
|
for (const relation of this.relations) {
|
|
|
|
relationMap[relation] = [];
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const result of queryResult.results) {
|
2020-09-02 11:55:38 +02:00
|
|
|
const modelData: any = {};
|
|
|
|
for (const field of Object.keys(result)) {
|
|
|
|
modelData[field.split('.')[1] || field] = result[field];
|
|
|
|
}
|
|
|
|
|
|
|
|
const model = this.factory.create(modelData);
|
2020-06-27 14:36:50 +02:00
|
|
|
models.push(model);
|
2020-09-04 22:44:30 +02:00
|
|
|
models.originalData.push(modelData);
|
2020-06-27 14:36:50 +02:00
|
|
|
|
|
|
|
if (this._pivot) {
|
2020-09-02 11:55:38 +02:00
|
|
|
const pivotData: any = {};
|
2020-06-27 14:36:50 +02:00
|
|
|
for (const field of this._pivot) {
|
2020-09-02 11:55:38 +02:00
|
|
|
pivotData[field] = result[field.split('.')[1]];
|
2020-06-27 14:36:50 +02:00
|
|
|
}
|
2020-09-02 11:55:38 +02:00
|
|
|
models.pivot!.push(pivotData);
|
2020-06-27 14:36:50 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Eager loading init map
|
|
|
|
for (const relation of this.relations) {
|
2020-07-28 10:33:17 +02:00
|
|
|
if (model[relation] === undefined) throw new Error(`Relation ${relation} doesn't exist on ${model.constructor.name}.`);
|
2020-06-27 14:36:50 +02:00
|
|
|
relationMap[relation].push(model[relation]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Eager loading execute
|
|
|
|
for (const relationName of this.relations) {
|
|
|
|
const relations = relationMap[relationName];
|
2020-06-27 17:11:31 +02:00
|
|
|
if (relations.length > 0) {
|
|
|
|
const allModels = await relations[0].eagerLoad(relations);
|
|
|
|
await Promise.all(relations.map(r => r.populate(allModels)));
|
|
|
|
}
|
2020-06-27 14:36:50 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return models;
|
|
|
|
}
|
|
|
|
|
|
|
|
public async paginate(page: number, perPage: number, connection?: Connection): Promise<ModelQueryResult<M>> {
|
|
|
|
this.limit(perPage, (page - 1) * perPage);
|
|
|
|
const result = await this.get(connection);
|
|
|
|
result.pagination = new Pagination<M>(result, page, perPage, await this.count(true, connection));
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
public async first(): Promise<M | null> {
|
|
|
|
const models = await this.limit(1).get();
|
|
|
|
return models.length > 0 ? models[0] : null;
|
|
|
|
}
|
|
|
|
|
|
|
|
public async count(removeLimit: boolean = false, connection?: Connection): Promise<number> {
|
|
|
|
if (removeLimit) {
|
|
|
|
this._limit = undefined;
|
|
|
|
this._offset = undefined;
|
2020-04-22 15:52:17 +02:00
|
|
|
}
|
2020-06-27 14:36:50 +02:00
|
|
|
this._sortBy = undefined;
|
|
|
|
this._sortDirection = undefined;
|
|
|
|
|
2020-08-26 14:04:23 +02:00
|
|
|
this.fields.splice(0, this.fields.length);
|
2020-09-04 18:51:41 +02:00
|
|
|
this.fields.push(new SelectFieldValue('_count', 'COUNT(*)', true));
|
2020-06-27 14:36:50 +02:00
|
|
|
let queryResult = await this.execute(connection);
|
2020-09-04 18:51:41 +02:00
|
|
|
return queryResult.results[0]['_count'];
|
2020-04-22 15:52:17 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-27 14:36:50 +02:00
|
|
|
export interface ModelQueryResult<M extends Model> extends Array<M> {
|
2020-09-04 22:44:30 +02:00
|
|
|
originalData?: any[];
|
2020-06-27 14:36:50 +02:00
|
|
|
pagination?: Pagination<M>;
|
|
|
|
pivot?: { [p: string]: any }[];
|
|
|
|
}
|
|
|
|
|
2020-04-22 15:52:17 +02:00
|
|
|
export enum QueryType {
|
|
|
|
SELECT,
|
|
|
|
UPDATE,
|
|
|
|
DELETE,
|
|
|
|
}
|
|
|
|
|
2020-06-14 11:43:31 +02:00
|
|
|
export enum WhereOperator {
|
2020-04-22 15:52:17 +02:00
|
|
|
AND = 'AND',
|
|
|
|
OR = 'OR',
|
|
|
|
}
|
|
|
|
|
2020-06-14 11:43:31 +02:00
|
|
|
export enum WhereTest {
|
|
|
|
EQ = '=',
|
|
|
|
NE = '!=',
|
|
|
|
GT = '>',
|
|
|
|
GE = '>=',
|
|
|
|
LT = '<',
|
|
|
|
LE = '<=',
|
2020-04-22 15:52:17 +02:00
|
|
|
IN = ' IN ',
|
|
|
|
}
|
|
|
|
|
|
|
|
class FieldValue {
|
|
|
|
protected readonly field: string;
|
|
|
|
protected value: any;
|
2020-06-14 11:43:31 +02:00
|
|
|
protected raw: boolean;
|
2020-04-22 15:52:17 +02:00
|
|
|
|
2020-06-14 11:43:31 +02:00
|
|
|
constructor(field: string, value: any, raw: boolean) {
|
2020-04-22 15:52:17 +02:00
|
|
|
this.field = field;
|
|
|
|
this.value = value;
|
2020-06-14 11:43:31 +02:00
|
|
|
this.raw = raw;
|
2020-04-22 15:52:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public toString(first: boolean = true): string {
|
2020-09-04 18:51:41 +02:00
|
|
|
const valueStr = (this.raw || this.value === null || this.value instanceof ModelQuery) ? this.value :
|
2020-08-28 15:40:58 +02:00
|
|
|
(Array.isArray(this.value) ? `(${'?'.repeat(this.value.length).split('').join(',')})` : '?');
|
2020-09-04 15:07:31 +02:00
|
|
|
let field = this.field.split('.').map(p => `\`${p}\``).join('.');
|
|
|
|
return `${first ? '' : ','}${field}${this.test}${valueStr}`;
|
2020-04-22 15:52:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
protected get test(): string {
|
|
|
|
return '=';
|
|
|
|
}
|
|
|
|
|
|
|
|
public get variables(): any[] {
|
2020-07-28 15:01:59 +02:00
|
|
|
if (this.value instanceof ModelQuery) return this.value.variables;
|
|
|
|
if (this.raw || this.value === null) return [];
|
2020-08-28 15:40:58 +02:00
|
|
|
if (Array.isArray(this.value)) return this.value;
|
2020-07-28 15:01:59 +02:00
|
|
|
return [this.value];
|
2020-04-22 15:52:17 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class SelectFieldValue extends FieldValue {
|
|
|
|
public toString(first: boolean = true): string {
|
2020-09-04 18:51:41 +02:00
|
|
|
return `(${this.value instanceof ModelQuery ? this.value : (this.raw ? this.value : '?')}) AS \`${this.field}\``;
|
2020-04-22 15:52:17 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class UpdateFieldValue extends FieldValue {
|
|
|
|
}
|
|
|
|
|
|
|
|
class WhereFieldValue extends FieldValue {
|
|
|
|
private readonly _test: WhereTest;
|
2020-06-14 11:43:31 +02:00
|
|
|
private readonly operator: WhereOperator;
|
2020-04-22 15:52:17 +02:00
|
|
|
|
2020-06-14 11:43:31 +02:00
|
|
|
constructor(field: string, value: any, raw: boolean, test: WhereTest, operator: WhereOperator) {
|
|
|
|
super(field, value, raw);
|
2020-04-22 15:52:17 +02:00
|
|
|
this._test = test;
|
2020-06-14 11:43:31 +02:00
|
|
|
this.operator = operator;
|
2020-04-22 15:52:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public toString(first: boolean = true): string {
|
|
|
|
return (!first ? ` ${this.operator} ` : '') + super.toString(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected get test(): string {
|
2020-07-27 10:56:43 +02:00
|
|
|
if (this.value === null) {
|
|
|
|
if (this._test === WhereTest.EQ) {
|
|
|
|
return ' IS ';
|
|
|
|
} else if (this._test === WhereTest.NE) {
|
|
|
|
return ' IS NOT ';
|
|
|
|
}
|
|
|
|
}
|
2020-04-22 15:52:17 +02:00
|
|
|
return this._test;
|
|
|
|
}
|
2020-09-04 22:15:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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<M extends Model> {
|
|
|
|
where(field: string, value: string | Date | ModelQuery<any> | any, test?: WhereTest, operator?: WhereOperator): this;
|
|
|
|
|
|
|
|
groupWhere(setter: (query: WhereFieldConsumer<M>) => void, operator?: WhereOperator): this;
|
|
|
|
}
|