Add left join to query builder
This commit is contained in:
parent
e965303777
commit
e63b5d21fe
@ -5,6 +5,7 @@ import AuthProof from "../AuthProof";
|
||||
import Validator from "../../db/Validator";
|
||||
import User from "./User";
|
||||
import argon2 from "argon2";
|
||||
import {WhereTest} from "../../db/Query";
|
||||
|
||||
export default class MagicLink extends Model implements AuthProof {
|
||||
public static async bySessionID(sessionID: string, actionType?: string | string[]): Promise<MagicLink | null> {
|
||||
@ -13,7 +14,7 @@ export default class MagicLink extends Model implements AuthProof {
|
||||
if (typeof actionType === 'string') {
|
||||
query = query.where('action_type', actionType);
|
||||
} else {
|
||||
query = query.whereIn('action_type', actionType);
|
||||
query = query.where('action_type', actionType, WhereTest.IN);
|
||||
}
|
||||
}
|
||||
const links = await this.models<MagicLink>(query.first());
|
||||
|
@ -8,23 +8,25 @@ export default class Query {
|
||||
|
||||
public static update(table: string, data: {
|
||||
[key: string]: any
|
||||
}) {
|
||||
}): Query {
|
||||
const fields = [];
|
||||
for (let key in data) {
|
||||
if (data.hasOwnProperty(key)) {
|
||||
fields.push(new UpdateFieldValue(key, data[key]));
|
||||
fields.push(new UpdateFieldValue(key, data[key], false));
|
||||
}
|
||||
}
|
||||
return new Query(QueryType.UPDATE, table, fields);
|
||||
}
|
||||
|
||||
public static delete(table: string) {
|
||||
public static delete(table: string): Query {
|
||||
return new Query(QueryType.DELETE, table);
|
||||
}
|
||||
|
||||
private readonly type: QueryType;
|
||||
private readonly table: string;
|
||||
private readonly fields: (string | SelectFieldValue | UpdateFieldValue)[];
|
||||
private _leftJoin?: string;
|
||||
private _leftJoinOn: WhereFieldValue[] = [];
|
||||
private _where: WhereFieldValue[] = [];
|
||||
private _limit?: number;
|
||||
private _offset?: number;
|
||||
@ -38,40 +40,38 @@ export default class Query {
|
||||
this.fields = fields || [];
|
||||
}
|
||||
|
||||
public where(field: string, value: string | Date | Query | any, operator: WhereOperator = WhereOperator.AND, test: WhereTest = WhereTest.EQUALS): Query {
|
||||
this._where.push(new WhereFieldValue(field, value, operator, test));
|
||||
public leftJoin(table: string): this {
|
||||
this._leftJoin = table;
|
||||
return this;
|
||||
}
|
||||
|
||||
public whereNot(field: string, value: string | Date | Query | any, operator: WhereOperator = WhereOperator.AND): Query {
|
||||
return this.where(field, value, operator, WhereTest.DIFFERENT);
|
||||
public on(field1: string, field2: string, test: WhereTest = WhereTest.EQ, operator: WhereOperator = WhereOperator.AND): this {
|
||||
this._leftJoinOn.push(new WhereFieldValue(field1, field2, true, test, operator));
|
||||
return this;
|
||||
}
|
||||
|
||||
public orWhere(field: string, value: string | Date | Query | any): Query {
|
||||
return this.where(field, value, WhereOperator.OR);
|
||||
public where(field: string, value: string | Date | Query | any, test: WhereTest = WhereTest.EQ, operator: WhereOperator = WhereOperator.AND): this {
|
||||
this._where.push(new WhereFieldValue(field, value, false, test, operator));
|
||||
return this;
|
||||
}
|
||||
|
||||
public whereIn(field: string, value: any[]): Query {
|
||||
return this.where(field, value, WhereOperator.AND, WhereTest.IN);
|
||||
}
|
||||
|
||||
public limit(limit: number, offset: number = 0): Query {
|
||||
public limit(limit: number, offset: number = 0): this {
|
||||
this._limit = limit;
|
||||
this._offset = offset;
|
||||
return this;
|
||||
}
|
||||
|
||||
public first(): Query {
|
||||
public first(): this {
|
||||
return this.limit(1);
|
||||
}
|
||||
|
||||
public sortBy(field: string, direction: 'ASC' | 'DESC' = 'ASC'): Query {
|
||||
public sortBy(field: string, direction: 'ASC' | 'DESC' = 'ASC'): this {
|
||||
this._sortBy = field;
|
||||
this._sortDirection = direction;
|
||||
return this;
|
||||
}
|
||||
|
||||
public withTotalRowCount(): Query {
|
||||
public withTotalRowCount(): this {
|
||||
this._foundRows = true;
|
||||
return this;
|
||||
}
|
||||
@ -81,6 +81,14 @@ export default class Query {
|
||||
|
||||
let fields = this.fields?.join(',');
|
||||
|
||||
let join = '';
|
||||
if (this._leftJoin) {
|
||||
join = `LEFT JOIN ${this._leftJoin} ON ${this._leftJoinOn[0]}`;
|
||||
for (let i = 1; i < this._leftJoinOn.length; i++) {
|
||||
join += this._leftJoinOn[i].toString(false);
|
||||
}
|
||||
}
|
||||
|
||||
let where = '';
|
||||
if (this._where.length > 0) {
|
||||
where = `WHERE ${this._where[0]}`;
|
||||
@ -104,7 +112,7 @@ export default class Query {
|
||||
|
||||
switch (this.type) {
|
||||
case QueryType.SELECT:
|
||||
query = `SELECT ${this._foundRows ? 'SQL_CALC_FOUND_ROWS' : ''} ${fields} FROM ${this.table} ${where} ${orderBy} ${limit}`;
|
||||
query = `SELECT ${this._foundRows ? 'SQL_CALC_FOUND_ROWS' : ''} ${fields} FROM ${this.table} ${join} ${where} ${orderBy} ${limit}`;
|
||||
break;
|
||||
case QueryType.UPDATE:
|
||||
query = `UPDATE ${this.table} SET ${fields} ${where} ${orderBy} ${limit}`;
|
||||
@ -152,28 +160,34 @@ export enum QueryType {
|
||||
DELETE,
|
||||
}
|
||||
|
||||
enum WhereOperator {
|
||||
export enum WhereOperator {
|
||||
AND = 'AND',
|
||||
OR = 'OR',
|
||||
}
|
||||
|
||||
enum WhereTest {
|
||||
EQUALS = '=',
|
||||
DIFFERENT = '!=',
|
||||
export enum WhereTest {
|
||||
EQ = '=',
|
||||
NE = '!=',
|
||||
GT = '>',
|
||||
GE = '>=',
|
||||
LT = '<',
|
||||
LE = '<=',
|
||||
IN = ' IN ',
|
||||
}
|
||||
|
||||
class FieldValue {
|
||||
protected readonly field: string;
|
||||
protected value: any;
|
||||
protected raw: boolean;
|
||||
|
||||
constructor(field: string, value: any) {
|
||||
constructor(field: string, value: any, raw: boolean) {
|
||||
this.field = field;
|
||||
this.value = value;
|
||||
this.raw = raw;
|
||||
}
|
||||
|
||||
public toString(first: boolean = true): string {
|
||||
return `${!first ? ',' : ''}${this.field}${this.test}${this.value instanceof Query ? this.value : (Array.isArray(this.value) ? '(?)' : '?')}`;
|
||||
return `${!first ? ',' : ''}${this.field}${this.test}${this.raw || this.value instanceof Query ? this.value : (Array.isArray(this.value) ? '(?)' : '?')}`;
|
||||
}
|
||||
|
||||
protected get test(): string {
|
||||
@ -195,13 +209,13 @@ class UpdateFieldValue extends FieldValue {
|
||||
}
|
||||
|
||||
class WhereFieldValue extends FieldValue {
|
||||
private readonly operator: WhereOperator;
|
||||
private readonly _test: WhereTest;
|
||||
private readonly operator: WhereOperator;
|
||||
|
||||
constructor(field: string, value: any, operator: WhereOperator, test: WhereTest) {
|
||||
super(field, value);
|
||||
this.operator = operator;
|
||||
constructor(field: string, value: any, raw: boolean, test: WhereTest, operator: WhereOperator) {
|
||||
super(field, value, raw);
|
||||
this._test = test;
|
||||
this.operator = operator;
|
||||
}
|
||||
|
||||
public toString(first: boolean = true): string {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import Model from "./Model";
|
||||
import Query from "./Query";
|
||||
import Query, {WhereTest} from "./Query";
|
||||
import {Connection} from "mysql";
|
||||
import {Type} from "../Utils";
|
||||
|
||||
@ -184,7 +184,7 @@ export default class Validator<T> {
|
||||
} else {
|
||||
query = (model instanceof Model ? <any>model.constructor : model).select('1').where(foreignKey, val);
|
||||
}
|
||||
if (model instanceof Model && typeof model.id === 'number') query = query.whereNot('id', model.id);
|
||||
if (model instanceof Model && typeof model.id === 'number') query = query.where('id', model.id, WhereTest.NE);
|
||||
return (await query.execute(c)).results.length === 0;
|
||||
},
|
||||
throw: () => new AlreadyExistsValidationError((<any>model).table),
|
||||
|
Loading…
Reference in New Issue
Block a user