diff --git a/.env b/.env index c2504c93..31ad2276 100644 --- a/.env +++ b/.env @@ -1,12 +1,12 @@ DB=pg DB_NAME=lucid -MYSQL_HOST=mysql +MYSQL_HOST=0.0.0.0 MYSQL_PORT=3306 MYSQL_USER=virk MYSQL_PASSWORD=password -PG_HOST=pg +PG_HOST=0.0.0.0 PG_PORT=5432 PG_USER=virk PG_PASSWORD=password diff --git a/adonis-typings/querybuilder.ts b/adonis-typings/querybuilder.ts index a69d1c12..a506b887 100644 --- a/adonis-typings/querybuilder.ts +++ b/adonis-typings/querybuilder.ts @@ -11,6 +11,12 @@ declare module '@ioc:Adonis/Addons/DatabaseQueryBuilder' { import { Dictionary } from 'ts-essentials' import { JoinCallback, Sql, Raw } from 'knex' + export interface Registery { + Count: number, + } + + type OneOrMany = T | T[] + /** * A known set of values allowed when defining values for different * clauses @@ -359,6 +365,56 @@ declare module '@ioc:Adonis/Addons/DatabaseQueryBuilder' { interface GroupByRaw extends RawQueryBuilderContract { } + /** + * Possible signatures for aggregate functions. It will append + * to the pre existing result + */ + interface Aggregate < + Record extends Dictionary, + Result extends any = Record, + > { + /** + * Accepting a typed column with the alias for the count. Unlike knex + * we enforce the alias, otherwise the output highly varies based + * upon the driver in use + */ + ( + column: OneOrMany, + alias: Alias, + ): DatabaseQueryBuilderContract)> + + /** + * Accepting an object for multiple counts in a single query. Again + * aliases are enforced for consistency. + */ + < + K extends keyof Record, + Alias extends string, + Columns extends Dictionary, Alias>, + >( + columns: Columns, + ): DatabaseQueryBuilderContract + + /** + * Accepting an un typed column with the alias for the count. + */ + ( + column: OneOrMany>, + alias: Alias, + ): DatabaseQueryBuilderContract)> + + /** + * Accepting an object for multiple counts in a single query. Again + * aliases are enforced for consistency + */ + < + Alias extends string, + Columns extends Dictionary>, Alias>, + >( + columns: Columns, + ): DatabaseQueryBuilderContract + } + /** * Possible signatures for orderBy method. */ @@ -720,9 +776,17 @@ declare module '@ioc:Adonis/Addons/DatabaseQueryBuilder' { export interface DatabaseQueryBuilderContract < Record extends Dictionary = Dictionary, Result extends any = Record, - > extends ChainableContract { + > extends ChainableContract, Promise { select: DatabaseQueryBuilderSelect, + count: Aggregate, + countDistinct: Aggregate, + min: Aggregate, + max: Aggregate, + sum: Aggregate, + avg: Aggregate, + avgDistinct: Aggregate, + /** * Returns the first row */ diff --git a/src/BaseQueryBuilder/index.ts b/src/BaseQueryBuilder/index.ts index f29592a1..f5aa69b0 100644 --- a/src/BaseQueryBuilder/index.ts +++ b/src/BaseQueryBuilder/index.ts @@ -37,7 +37,7 @@ export class BaseQueryBuilder implements ChainableContract { * 3. Wrapping callbacks, so that the end user receives an instance Lucid query * builder and not knex query builder. */ - private _transformValue (value: any) { + protected $transformValue (value: any) { if (value instanceof BaseQueryBuilder) { return value.$knexBuilder } @@ -46,14 +46,14 @@ export class BaseQueryBuilder implements ChainableContract { return this._queryCallback(value) } - return this._transformRaw(value) + return this.$transformRaw(value) } /** * Returns the underlying knex raw query builder for Lucid raw * query builder */ - private _transformRaw (value: any) { + protected $transformRaw (value: any) { if (value instanceof RawQueryBuilder) { return value['$knexBuilder'] } @@ -75,11 +75,11 @@ export class BaseQueryBuilder implements ChainableContract { */ public where (key: any, operator?: any, value?: any): this { if (value) { - this.$knexBuilder.where(key, operator, this._transformValue(value)) + this.$knexBuilder.where(key, operator, this.$transformValue(value)) } else if (operator) { - this.$knexBuilder.where(key, this._transformValue(operator)) + this.$knexBuilder.where(key, this.$transformValue(operator)) } else { - this.$knexBuilder.where(this._transformValue(key)) + this.$knexBuilder.where(this.$transformValue(key)) } return this @@ -90,11 +90,11 @@ export class BaseQueryBuilder implements ChainableContract { */ public orWhere (key: any, operator?: any, value?: any): this { if (value) { - this.$knexBuilder.orWhere(key, operator, this._transformValue(value)) + this.$knexBuilder.orWhere(key, operator, this.$transformValue(value)) } else if (operator) { - this.$knexBuilder.orWhere(key, this._transformValue(operator)) + this.$knexBuilder.orWhere(key, this.$transformValue(operator)) } else { - this.$knexBuilder.orWhere(this._transformValue(key)) + this.$knexBuilder.orWhere(this.$transformValue(key)) } return this @@ -112,11 +112,11 @@ export class BaseQueryBuilder implements ChainableContract { */ public whereNot (key: any, operator?: any, value?: any): this { if (value) { - this.$knexBuilder.whereNot(key, operator, this._transformValue(value)) + this.$knexBuilder.whereNot(key, operator, this.$transformValue(value)) } else if (operator) { - this.$knexBuilder.whereNot(key, this._transformValue(operator)) + this.$knexBuilder.whereNot(key, this.$transformValue(operator)) } else { - this.$knexBuilder.whereNot(this._transformValue(key)) + this.$knexBuilder.whereNot(this.$transformValue(key)) } return this @@ -127,11 +127,11 @@ export class BaseQueryBuilder implements ChainableContract { */ public orWhereNot (key: any, operator?: any, value?: any): this { if (value) { - this.$knexBuilder.orWhereNot(key, operator, this._transformValue(value)) + this.$knexBuilder.orWhereNot(key, operator, this.$transformValue(value)) } else if (operator) { - this.$knexBuilder.orWhereNot(key, this._transformValue(operator)) + this.$knexBuilder.orWhereNot(key, this.$transformValue(operator)) } else { - this.$knexBuilder.orWhereNot(this._transformValue(key)) + this.$knexBuilder.orWhereNot(this.$transformValue(key)) } return this @@ -149,8 +149,8 @@ export class BaseQueryBuilder implements ChainableContract { */ public whereIn (key: any, value: any): this { value = Array.isArray(value) - ? value.map((one) => this._transformValue(one)) - : this._transformValue(value) + ? value.map((one) => this.$transformValue(one)) + : this.$transformValue(value) this.$knexBuilder.whereIn(key, value) return this @@ -161,8 +161,8 @@ export class BaseQueryBuilder implements ChainableContract { */ public orWhereIn (key: any, value: any): this { value = Array.isArray(value) - ? value.map((one) => this._transformValue(one)) - : this._transformValue(value) + ? value.map((one) => this.$transformValue(one)) + : this.$transformValue(value) this.$knexBuilder.orWhereIn(key, value) return this @@ -180,8 +180,8 @@ export class BaseQueryBuilder implements ChainableContract { */ public whereNotIn (key: any, value: any): this { value = Array.isArray(value) - ? value.map((one) => this._transformValue(one)) - : this._transformValue(value) + ? value.map((one) => this.$transformValue(one)) + : this.$transformValue(value) this.$knexBuilder.whereNotIn(key, value) return this @@ -192,8 +192,8 @@ export class BaseQueryBuilder implements ChainableContract { */ public orWhereNotIn (key: any, value: any): this { value = Array.isArray(value) - ? value.map((one) => this._transformValue(one)) - : this._transformValue(value) + ? value.map((one) => this.$transformValue(one)) + : this.$transformValue(value) this.$knexBuilder.orWhereNotIn(key, value) return this @@ -256,7 +256,7 @@ export class BaseQueryBuilder implements ChainableContract { * Add a `where exists` clause */ public whereExists (value: any) { - this.$knexBuilder.whereExists(this._transformValue(value)) + this.$knexBuilder.whereExists(this.$transformValue(value)) return this } @@ -264,7 +264,7 @@ export class BaseQueryBuilder implements ChainableContract { * Add a `or where exists` clause */ public orWhereExists (value: any) { - this.$knexBuilder.orWhereExists(this._transformValue(value)) + this.$knexBuilder.orWhereExists(this.$transformValue(value)) return this } @@ -279,7 +279,7 @@ export class BaseQueryBuilder implements ChainableContract { * Add a `where not exists` clause */ public whereNotExists (value: any) { - this.$knexBuilder.whereNotExists(this._transformValue(value)) + this.$knexBuilder.whereNotExists(this.$transformValue(value)) return this } @@ -287,7 +287,7 @@ export class BaseQueryBuilder implements ChainableContract { * Add a `or where not exists` clause */ public orWhereNotExists (value: any) { - this.$knexBuilder.orWhereNotExists(this._transformValue(value)) + this.$knexBuilder.orWhereNotExists(this.$transformValue(value)) return this } @@ -302,7 +302,7 @@ export class BaseQueryBuilder implements ChainableContract { * Add where between clause */ public whereBetween (key: any, value: any): this { - this.$knexBuilder.whereBetween(key, value.map((one) => this._transformValue(one))) + this.$knexBuilder.whereBetween(key, value.map((one) => this.$transformValue(one))) return this } @@ -310,7 +310,7 @@ export class BaseQueryBuilder implements ChainableContract { * Add where between clause */ public orWhereBetween (key: any, value: any): this { - this.$knexBuilder.orWhereBetween(key, value.map((one) => this._transformValue(one))) + this.$knexBuilder.orWhereBetween(key, value.map((one) => this.$transformValue(one))) return this } @@ -325,7 +325,7 @@ export class BaseQueryBuilder implements ChainableContract { * Add where between clause */ public whereNotBetween (key: any, value: any): this { - this.$knexBuilder.whereNotBetween(key, value.map((one) => this._transformValue(one))) + this.$knexBuilder.whereNotBetween(key, value.map((one) => this.$transformValue(one))) return this } @@ -333,7 +333,7 @@ export class BaseQueryBuilder implements ChainableContract { * Add where between clause */ public orWhereNotBetween (key: any, value: any): this { - this.$knexBuilder.orWhereNotBetween(key, value.map((one) => this._transformValue(one))) + this.$knexBuilder.orWhereNotBetween(key, value.map((one) => this.$transformValue(one))) return this } @@ -351,7 +351,7 @@ export class BaseQueryBuilder implements ChainableContract { if (bindings) { this.$knexBuilder.whereRaw(sql, bindings) } else { - this.$knexBuilder.whereRaw(this._transformRaw(sql)) + this.$knexBuilder.whereRaw(this.$transformRaw(sql)) } return this @@ -364,7 +364,7 @@ export class BaseQueryBuilder implements ChainableContract { if (bindings) { this.$knexBuilder.orWhereRaw(sql, bindings) } else { - this.$knexBuilder.orWhereRaw(this._transformRaw(sql)) + this.$knexBuilder.orWhereRaw(this.$transformRaw(sql)) } return this } @@ -381,11 +381,11 @@ export class BaseQueryBuilder implements ChainableContract { */ public join (table: any, first: any, operator?: any, second?: any): this { if (second) { - this.$knexBuilder.join(table, first, operator, this._transformRaw(second)) + this.$knexBuilder.join(table, first, operator, this.$transformRaw(second)) } else if (operator) { - this.$knexBuilder.join(table, first, this._transformRaw(operator)) + this.$knexBuilder.join(table, first, this.$transformRaw(operator)) } else { - this.$knexBuilder.join(table, this._transformRaw(first)) + this.$knexBuilder.join(table, this.$transformRaw(first)) } return this @@ -396,11 +396,11 @@ export class BaseQueryBuilder implements ChainableContract { */ public innerJoin (table: any, first: any, operator?: any, second?: any): this { if (second) { - this.$knexBuilder.innerJoin(table, first, operator, this._transformRaw(second)) + this.$knexBuilder.innerJoin(table, first, operator, this.$transformRaw(second)) } else if (operator) { - this.$knexBuilder.innerJoin(table, first, this._transformRaw(operator)) + this.$knexBuilder.innerJoin(table, first, this.$transformRaw(operator)) } else { - this.$knexBuilder.innerJoin(table, this._transformRaw(first)) + this.$knexBuilder.innerJoin(table, this.$transformRaw(first)) } return this @@ -411,11 +411,11 @@ export class BaseQueryBuilder implements ChainableContract { */ public leftJoin (table: any, first: any, operator?: any, second?: any): this { if (second) { - this.$knexBuilder.leftJoin(table, first, operator, this._transformRaw(second)) + this.$knexBuilder.leftJoin(table, first, operator, this.$transformRaw(second)) } else if (operator) { - this.$knexBuilder.leftJoin(table, first, this._transformRaw(operator)) + this.$knexBuilder.leftJoin(table, first, this.$transformRaw(operator)) } else { - this.$knexBuilder.leftJoin(table, this._transformRaw(first)) + this.$knexBuilder.leftJoin(table, this.$transformRaw(first)) } return this @@ -426,11 +426,11 @@ export class BaseQueryBuilder implements ChainableContract { */ public leftOuterJoin (table: any, first: any, operator?: any, second?: any): this { if (second) { - this.$knexBuilder.leftOuterJoin(table, first, operator, this._transformRaw(second)) + this.$knexBuilder.leftOuterJoin(table, first, operator, this.$transformRaw(second)) } else if (operator) { - this.$knexBuilder.leftOuterJoin(table, first, this._transformRaw(operator)) + this.$knexBuilder.leftOuterJoin(table, first, this.$transformRaw(operator)) } else { - this.$knexBuilder.leftOuterJoin(table, this._transformRaw(first)) + this.$knexBuilder.leftOuterJoin(table, this.$transformRaw(first)) } return this @@ -441,11 +441,11 @@ export class BaseQueryBuilder implements ChainableContract { */ public rightJoin (table: any, first: any, operator?: any, second?: any): this { if (second) { - this.$knexBuilder.rightJoin(table, first, operator, this._transformRaw(second)) + this.$knexBuilder.rightJoin(table, first, operator, this.$transformRaw(second)) } else if (operator) { - this.$knexBuilder.rightJoin(table, first, this._transformRaw(operator)) + this.$knexBuilder.rightJoin(table, first, this.$transformRaw(operator)) } else { - this.$knexBuilder.rightJoin(table, this._transformRaw(first)) + this.$knexBuilder.rightJoin(table, this.$transformRaw(first)) } return this @@ -456,11 +456,11 @@ export class BaseQueryBuilder implements ChainableContract { */ public rightOuterJoin (table: any, first: any, operator?: any, second?: any): this { if (second) { - this.$knexBuilder.rightOuterJoin(table, first, operator, this._transformRaw(second)) + this.$knexBuilder.rightOuterJoin(table, first, operator, this.$transformRaw(second)) } else if (operator) { - this.$knexBuilder.rightOuterJoin(table, first, this._transformRaw(operator)) + this.$knexBuilder.rightOuterJoin(table, first, this.$transformRaw(operator)) } else { - this.$knexBuilder.rightOuterJoin(table, this._transformRaw(first)) + this.$knexBuilder.rightOuterJoin(table, this.$transformRaw(first)) } return this @@ -471,11 +471,11 @@ export class BaseQueryBuilder implements ChainableContract { */ public fullOuterJoin (table: any, first: any, operator?: any, second?: any): this { if (second) { - this.$knexBuilder.fullOuterJoin(table, first, operator, this._transformRaw(second)) + this.$knexBuilder.fullOuterJoin(table, first, operator, this.$transformRaw(second)) } else if (operator) { - this.$knexBuilder.fullOuterJoin(table, first, this._transformRaw(operator)) + this.$knexBuilder.fullOuterJoin(table, first, this.$transformRaw(operator)) } else { - this.$knexBuilder.fullOuterJoin(table, this._transformRaw(first)) + this.$knexBuilder.fullOuterJoin(table, this.$transformRaw(first)) } return this @@ -486,11 +486,11 @@ export class BaseQueryBuilder implements ChainableContract { */ public crossJoin (table: any, first: any, operator?: any, second?: any): this { if (second) { - this.$knexBuilder.crossJoin(table, first, operator, this._transformRaw(second)) + this.$knexBuilder.crossJoin(table, first, operator, this.$transformRaw(second)) } else if (operator) { - this.$knexBuilder.crossJoin(table, first, this._transformRaw(operator)) + this.$knexBuilder.crossJoin(table, first, this.$transformRaw(operator)) } else { - this.$knexBuilder.crossJoin(table, this._transformRaw(first)) + this.$knexBuilder.crossJoin(table, this.$transformRaw(first)) } return this @@ -503,7 +503,7 @@ export class BaseQueryBuilder implements ChainableContract { if (bindings) { this.$knexBuilder.joinRaw(sql, bindings) } else { - this.$knexBuilder.joinRaw(this._transformRaw(sql)) + this.$knexBuilder.joinRaw(this.$transformRaw(sql)) } return this @@ -517,7 +517,7 @@ export class BaseQueryBuilder implements ChainableContract { */ public having (key: any, operator?: any, value?: any): this { if (value) { - this.$knexBuilder.having(key, operator, this._transformValue(value)) + this.$knexBuilder.having(key, operator, this.$transformValue(value)) } else if (operator) { /** * @todo: The having method in Knex is badly implemented. They only accept @@ -526,9 +526,9 @@ export class BaseQueryBuilder implements ChainableContract { * * So we need to transform the `sql` and `bindings` to a raw query instance. */ - this.$knexBuilder.having(key, this._transformValue(operator)) + this.$knexBuilder.having(key, this.$transformValue(operator)) } else { - this.$knexBuilder.having(this._transformValue(key)) + this.$knexBuilder.having(this.$transformValue(key)) } return this @@ -542,7 +542,7 @@ export class BaseQueryBuilder implements ChainableContract { */ public orHaving (key: any, operator?: any, value?: any): this { if (value) { - this.$knexBuilder.orHaving(key, operator, this._transformValue(value)) + this.$knexBuilder.orHaving(key, operator, this.$transformValue(value)) } else if (operator) { /** * @todo: The having method in Knex is badly implemented. They only accept @@ -551,9 +551,9 @@ export class BaseQueryBuilder implements ChainableContract { * * So we need to transform the `sql` and `bindings` to a raw query instance. */ - this.$knexBuilder.orHaving(key, this._transformValue(operator)) + this.$knexBuilder.orHaving(key, this.$transformValue(operator)) } else { - this.$knexBuilder.orHaving(this._transformValue(key)) + this.$knexBuilder.orHaving(this.$transformValue(key)) } return this @@ -571,8 +571,8 @@ export class BaseQueryBuilder implements ChainableContract { */ public havingIn (key: any, value: any): this { value = Array.isArray(value) - ? value.map((one) => this._transformValue(one)) - : this._transformValue(value) + ? value.map((one) => this.$transformValue(one)) + : this.$transformValue(value) this.$knexBuilder.havingIn(key, value) return this @@ -583,8 +583,8 @@ export class BaseQueryBuilder implements ChainableContract { */ public orHavingIn (key: any, value: any): this { value = Array.isArray(value) - ? value.map((one) => this._transformValue(one)) - : this._transformValue(value) + ? value.map((one) => this.$transformValue(one)) + : this.$transformValue(value) this.$knexBuilder['orHavingIn'](key, value) return this @@ -602,8 +602,8 @@ export class BaseQueryBuilder implements ChainableContract { */ public havingNotIn (key: any, value: any): this { value = Array.isArray(value) - ? value.map((one) => this._transformValue(one)) - : this._transformValue(value) + ? value.map((one) => this.$transformValue(one)) + : this.$transformValue(value) this.$knexBuilder['havingNotIn'](key, value) return this @@ -614,8 +614,8 @@ export class BaseQueryBuilder implements ChainableContract { */ public orHavingNotIn (key: any, value: any): this { value = Array.isArray(value) - ? value.map((one) => this._transformValue(one)) - : this._transformValue(value) + ? value.map((one) => this.$transformValue(one)) + : this.$transformValue(value) this.$knexBuilder['orHavingNotIn'](key, value) return this @@ -678,7 +678,7 @@ export class BaseQueryBuilder implements ChainableContract { * Adding `having exists` clause */ public havingExists (value: any): this { - this.$knexBuilder['havingExists'](this._transformValue(value)) + this.$knexBuilder['havingExists'](this.$transformValue(value)) return this } @@ -686,7 +686,7 @@ export class BaseQueryBuilder implements ChainableContract { * Adding `or having exists` clause */ public orHavingExists (value: any): this { - this.$knexBuilder['orHavingExists'](this._transformValue(value)) + this.$knexBuilder['orHavingExists'](this.$transformValue(value)) return this } @@ -701,7 +701,7 @@ export class BaseQueryBuilder implements ChainableContract { * Adding `having not exists` clause */ public havingNotExists (value: any): this { - this.$knexBuilder['havingNotExists'](this._transformValue(value)) + this.$knexBuilder['havingNotExists'](this.$transformValue(value)) return this } @@ -709,7 +709,7 @@ export class BaseQueryBuilder implements ChainableContract { * Adding `or having not exists` clause */ public orHavingNotExists (value: any): this { - this.$knexBuilder['orHavingNotExists'](this._transformValue(value)) + this.$knexBuilder['orHavingNotExists'](this.$transformValue(value)) return this } @@ -724,7 +724,7 @@ export class BaseQueryBuilder implements ChainableContract { * Adding `having between` clause */ public havingBetween (key: any, value: any): this { - this.$knexBuilder.havingBetween(key, value.map((one) => this._transformValue(one))) + this.$knexBuilder.havingBetween(key, value.map((one) => this.$transformValue(one))) return this } @@ -732,7 +732,7 @@ export class BaseQueryBuilder implements ChainableContract { * Adding `or having between` clause */ public orHavingBetween (key: any, value: any): this { - this.$knexBuilder.orHavingBetween(key, value.map((one) => this._transformValue(one))) + this.$knexBuilder.orHavingBetween(key, value.map((one) => this.$transformValue(one))) return this } @@ -747,7 +747,7 @@ export class BaseQueryBuilder implements ChainableContract { * Adding `having not between` clause */ public havingNotBetween (key: any, value: any): this { - this.$knexBuilder.havingNotBetween(key, value.map((one) => this._transformValue(one))) + this.$knexBuilder.havingNotBetween(key, value.map((one) => this.$transformValue(one))) return this } @@ -755,7 +755,7 @@ export class BaseQueryBuilder implements ChainableContract { * Adding `or having not between` clause */ public orHavingNotBetween (key: any, value: any): this { - this.$knexBuilder.orHavingNotBetween(key, value.map((one) => this._transformValue(one))) + this.$knexBuilder.orHavingNotBetween(key, value.map((one) => this.$transformValue(one))) return this } @@ -773,7 +773,7 @@ export class BaseQueryBuilder implements ChainableContract { if (bindings) { this.$knexBuilder.havingRaw(sql, bindings) } else { - this.$knexBuilder.havingRaw(this._transformRaw(sql)) + this.$knexBuilder.havingRaw(this.$transformRaw(sql)) } return this @@ -786,7 +786,7 @@ export class BaseQueryBuilder implements ChainableContract { if (bindings) { this.$knexBuilder.orHavingRaw(sql, bindings) } else { - this.$knexBuilder.orHavingRaw(this._transformRaw(sql)) + this.$knexBuilder.orHavingRaw(this.$transformRaw(sql)) } return this @@ -822,7 +822,7 @@ export class BaseQueryBuilder implements ChainableContract { if (bindings) { this.$knexBuilder.groupByRaw(sql, bindings) } else { - this.$knexBuilder.groupByRaw(this._transformRaw(sql)) + this.$knexBuilder.groupByRaw(this.$transformRaw(sql)) } return this @@ -843,7 +843,7 @@ export class BaseQueryBuilder implements ChainableContract { if (bindings) { this.$knexBuilder.orderByRaw(sql, bindings) } else { - this.$knexBuilder.orderByRaw(this._transformRaw(sql)) + this.$knexBuilder.orderByRaw(this.$transformRaw(sql)) } return this @@ -870,8 +870,8 @@ export class BaseQueryBuilder implements ChainableContract { */ public union (queries: any, wrap?: boolean): this { queries = Array.isArray(queries) - ? queries.map((one) => this._transformValue(one)) - : this._transformValue(queries) + ? queries.map((one) => this.$transformValue(one)) + : this.$transformValue(queries) wrap ? this.$knexBuilder.union(queries, wrap) : this.$knexBuilder.union(queries) return this @@ -882,8 +882,8 @@ export class BaseQueryBuilder implements ChainableContract { */ public unionAll (queries: any, wrap?: boolean): this { queries = Array.isArray(queries) - ? queries.map((one) => this._transformValue(one)) - : this._transformValue(queries) + ? queries.map((one) => this.$transformValue(one)) + : this.$transformValue(queries) wrap ? this.$knexBuilder.unionAll(queries, wrap) : this.$knexBuilder.unionAll(queries) return this @@ -894,8 +894,8 @@ export class BaseQueryBuilder implements ChainableContract { */ public intersect (queries: any, wrap?: boolean): this { queries = Array.isArray(queries) - ? queries.map((one) => this._transformValue(one)) - : this._transformValue(queries) + ? queries.map((one) => this.$transformValue(one)) + : this.$transformValue(queries) wrap ? this.$knexBuilder.intersect(queries, wrap) : this.$knexBuilder.intersect(queries) return this diff --git a/src/Database/index.ts b/src/Database/index.ts index 8f1eff59..b4692b8f 100644 --- a/src/Database/index.ts +++ b/src/Database/index.ts @@ -25,15 +25,64 @@ function queryCallback (userFn: QueryCallback) { } } -// @ts-ignore export class DatabaseQueryBuilder extends BaseQueryBuilder implements DatabaseQueryBuilderContract { constructor (client: knex.QueryBuilder) { super(client, queryCallback) } + private _normalizeAggregateColumns (columns: any, alias?: any): any { + if (columns.constructor === Object) { + return Object.keys(columns).reduce((result, key) => { + result[key] = this.$transformValue(columns[key]) + return result + }, {}) + } + + if (!alias) { + throw new Error('Aggregate function needs an alias as 2nd argument') + } + + return { [alias]: this.$transformValue(columns) } + } + // @ts-ignore public select (): this { this.$knexBuilder.select(...arguments) return this } + + public count (columns: any, alias?: any): any { + this.$knexBuilder.count(this._normalizeAggregateColumns(columns, alias)) + return this + } + + public countDistinct (columns: any, alias?: any): any { + this.$knexBuilder.countDistinct(this._normalizeAggregateColumns(columns, alias)) + return this + } + + public min (columns: any, alias?: any): any { + this.$knexBuilder.min(this._normalizeAggregateColumns(columns, alias)) + return this + } + + public max (columns: any, alias?: any): any { + this.$knexBuilder.max(this._normalizeAggregateColumns(columns, alias)) + return this + } + + public avg (columns: any, alias?: any): any { + this.$knexBuilder.avg(this._normalizeAggregateColumns(columns, alias)) + return this + } + + public avgDistinct (columns: any, alias?: any): any { + this.$knexBuilder.avgDistinct(this._normalizeAggregateColumns(columns, alias)) + return this + } + + public sum (columns: any, alias?: any): any { + this.$knexBuilder.sum(this._normalizeAggregateColumns(columns, alias)) + return this + } } diff --git a/test/query-builder.spec.ts b/test/query-builder.spec.ts index 1961da13..9287f50d 100644 --- a/test/query-builder.spec.ts +++ b/test/query-builder.spec.ts @@ -3765,3 +3765,1001 @@ test.group('Query Builder | clearHaving', (group) => { assert.deepEqual(bindings, knexBindings) }) }) + +test.group('Query Builder | count', (group) => { + group.before(async () => { + await setup() + }) + + group.after(async () => { + await cleanup() + }) + + test('count all rows', (assert) => { + const connection = new Connection('primary', getConfig()) + connection.connect() + + const db = getQueryBuilder(connection) + const { sql, bindings } = db + .from('users') + .count('*', 'total') + .toSQL() + + const { sql: knexSql, bindings: knexBindings } = connection.client! + .from('users') + .count('*', { as: 'total' }) + .toSQL() + + assert.equal(sql, knexSql) + assert.deepEqual(bindings, knexBindings) + }) + + test('count multiple rows', (assert) => { + const connection = new Connection('primary', getConfig()) + connection.connect() + + const db = getQueryBuilder(connection) + const { sql, bindings } = db + .from('users') + .count({ u: 'username', e: 'email' }) + .toSQL() + + const { sql: knexSql, bindings: knexBindings } = connection.client! + .from('users') + .count({ u: 'username', e: 'email' }) + .toSQL() + + assert.equal(sql, knexSql) + assert.deepEqual(bindings, knexBindings) + }) + + test('count by raw query', (assert) => { + const connection = new Connection('primary', getConfig()) + connection.connect() + + const db = getQueryBuilder(connection) + const { sql, bindings } = db + .from('users') + .count(getRawQueryBuilder(connection, 'select * from profiles where is_verified = ?', [true]), 'u') + .toSQL() + + const { sql: knexSql, bindings: knexBindings } = connection.client! + .from('users') + .count({ + u: connection.client!.raw('select * from profiles where is_verified = ?', [true]), + }) + .toSQL() + + assert.equal(sql, knexSql) + assert.deepEqual(bindings, knexBindings) + }) + + test('count by subquery', (assert) => { + const connection = new Connection('primary', getConfig()) + connection.connect() + + const db = getQueryBuilder(connection) + const { sql, bindings } = db + .from('users') + .count(getQueryBuilder(connection).where('is_verified', true).from('profiles'), 'u') + .toSQL() + + const { sql: knexSql, bindings: knexBindings } = connection.client! + .from('users') + .count({ + u: connection.client!.where('is_verified', true).from('profiles'), + }) + .toSQL() + + assert.equal(sql, knexSql) + assert.deepEqual(bindings, knexBindings) + }) + + test('count by raw query on multiple columns', (assert) => { + const connection = new Connection('primary', getConfig()) + connection.connect() + + const db = getQueryBuilder(connection) + const { sql, bindings } = db + .from('users') + .count({ + u: getRawQueryBuilder(connection, 'select * from profiles where is_verified = ?', [true]), + e: 'email', + }) + .toSQL() + + const { sql: knexSql, bindings: knexBindings } = connection.client! + .from('users') + .count({ + u: connection.client!.raw('select * from profiles where is_verified = ?', [true]), + e: 'email', + }) + .toSQL() + + assert.equal(sql, knexSql) + assert.deepEqual(bindings, knexBindings) + }) + + test('count by subquery on multiple columns', (assert) => { + const connection = new Connection('primary', getConfig()) + connection.connect() + + const db = getQueryBuilder(connection) + const { sql, bindings } = db + .from('users') + .count({ + u: getQueryBuilder(connection).where('is_verified', true).from('profiles'), + e: 'email', + }) + .toSQL() + + const { sql: knexSql, bindings: knexBindings } = connection.client! + .from('users') + .count({ + u: connection.client!.where('is_verified', true).from('profiles'), + e: 'email', + }) + .toSQL() + + assert.equal(sql, knexSql) + assert.deepEqual(bindings, knexBindings) + }) +}) + +test.group('Query Builder | countDistinct', (group) => { + group.before(async () => { + await setup() + }) + + group.after(async () => { + await cleanup() + }) + + test('count all rows', (assert) => { + const connection = new Connection('primary', getConfig()) + connection.connect() + + const db = getQueryBuilder(connection) + const { sql, bindings } = db + .from('users') + .countDistinct('*', 'total') + .toSQL() + + const { sql: knexSql, bindings: knexBindings } = connection.client! + .from('users') + .countDistinct('*', { as: 'total' }) + .toSQL() + + assert.equal(sql, knexSql) + assert.deepEqual(bindings, knexBindings) + }) + + test('count multiple rows', (assert) => { + const connection = new Connection('primary', getConfig()) + connection.connect() + + const db = getQueryBuilder(connection) + const { sql, bindings } = db + .from('users') + .countDistinct({ u: 'username', e: 'email' }) + .toSQL() + + const { sql: knexSql, bindings: knexBindings } = connection.client! + .from('users') + .countDistinct({ u: 'username', e: 'email' }) + .toSQL() + + assert.equal(sql, knexSql) + assert.deepEqual(bindings, knexBindings) + }) + + test('count by raw query', (assert) => { + const connection = new Connection('primary', getConfig()) + connection.connect() + + const db = getQueryBuilder(connection) + const { sql, bindings } = db + .from('users') + .countDistinct( + getRawQueryBuilder(connection, 'select * from profiles where is_verified = ?', [true]), + 'u', + ) + .toSQL() + + const { sql: knexSql, bindings: knexBindings } = connection.client! + .from('users') + .countDistinct({ + u: connection.client!.raw('select * from profiles where is_verified = ?', [true]), + }) + .toSQL() + + assert.equal(sql, knexSql) + assert.deepEqual(bindings, knexBindings) + }) + + test('count by subquery', (assert) => { + const connection = new Connection('primary', getConfig()) + connection.connect() + + const db = getQueryBuilder(connection) + const { sql, bindings } = db + .from('users') + .countDistinct(getQueryBuilder(connection).where('is_verified', true).from('profiles'), 'u') + .toSQL() + + const { sql: knexSql, bindings: knexBindings } = connection.client! + .from('users') + .countDistinct({ + u: connection.client!.where('is_verified', true).from('profiles'), + }) + .toSQL() + + assert.equal(sql, knexSql) + assert.deepEqual(bindings, knexBindings) + }) + + test('count by raw query on multiple columns', (assert) => { + const connection = new Connection('primary', getConfig()) + connection.connect() + + const db = getQueryBuilder(connection) + const { sql, bindings } = db + .from('users') + .countDistinct({ + u: getRawQueryBuilder(connection, 'select * from profiles where is_verified = ?', [true]), + e: 'email', + }) + .toSQL() + + const { sql: knexSql, bindings: knexBindings } = connection.client! + .from('users') + .countDistinct({ + u: connection.client!.raw('select * from profiles where is_verified = ?', [true]), + e: 'email', + }) + .toSQL() + + assert.equal(sql, knexSql) + assert.deepEqual(bindings, knexBindings) + }) + + test('count by subquery on multiple columns', (assert) => { + const connection = new Connection('primary', getConfig()) + connection.connect() + + const db = getQueryBuilder(connection) + const { sql, bindings } = db + .from('users') + .countDistinct({ + u: getQueryBuilder(connection).where('is_verified', true).from('profiles'), + e: 'email', + }) + .toSQL() + + const { sql: knexSql, bindings: knexBindings } = connection.client! + .from('users') + .countDistinct({ + u: connection.client!.where('is_verified', true).from('profiles'), + e: 'email', + }) + .toSQL() + + assert.equal(sql, knexSql) + assert.deepEqual(bindings, knexBindings) + }) +}) + +test.group('Query Builder | min', (group) => { + group.before(async () => { + await setup() + }) + + group.after(async () => { + await cleanup() + }) + + test('use min function', (assert) => { + const connection = new Connection('primary', getConfig()) + connection.connect() + + const db = getQueryBuilder(connection) + const { sql, bindings } = db + .from('users') + .min('*', 'smallest') + .toSQL() + + const { sql: knexSql, bindings: knexBindings } = connection.client! + .from('users') + .min('*', { as: 'smallest' }) + .toSQL() + + assert.equal(sql, knexSql) + assert.deepEqual(bindings, knexBindings) + }) + + test('use min function for multiple times', (assert) => { + const connection = new Connection('primary', getConfig()) + connection.connect() + + const db = getQueryBuilder(connection) + const { sql, bindings } = db + .from('users') + .min({ u: 'username', e: 'email' }) + .toSQL() + + const { sql: knexSql, bindings: knexBindings } = connection.client! + .from('users') + .min({ u: 'username', e: 'email' }) + .toSQL() + + assert.equal(sql, knexSql) + assert.deepEqual(bindings, knexBindings) + }) + + test('use raw queries to compute min', (assert) => { + const connection = new Connection('primary', getConfig()) + connection.connect() + + const db = getQueryBuilder(connection) + const { sql, bindings } = db + .from('users') + .min( + getRawQueryBuilder(connection, 'select * from profiles where is_verified = ?', [true]), + 'u', + ) + .toSQL() + + const { sql: knexSql, bindings: knexBindings } = connection.client! + .from('users') + .min({ + u: connection.client!.raw('select * from profiles where is_verified = ?', [true]), + }) + .toSQL() + + assert.equal(sql, knexSql) + assert.deepEqual(bindings, knexBindings) + }) + + test('use subqueries to compute min', (assert) => { + const connection = new Connection('primary', getConfig()) + connection.connect() + + const db = getQueryBuilder(connection) + const { sql, bindings } = db + .from('users') + .min(getQueryBuilder(connection).where('is_verified', true).from('profiles'), 'u') + .toSQL() + + const { sql: knexSql, bindings: knexBindings } = connection.client! + .from('users') + .min({ + u: connection.client!.where('is_verified', true).from('profiles'), + }) + .toSQL() + + assert.equal(sql, knexSql) + assert.deepEqual(bindings, knexBindings) + }) + + test('use raw query to compute min with multiple columns', (assert) => { + const connection = new Connection('primary', getConfig()) + connection.connect() + + const db = getQueryBuilder(connection) + const { sql, bindings } = db + .from('users') + .min({ + u: getRawQueryBuilder(connection, 'select * from profiles where is_verified = ?', [true]), + e: 'email', + }) + .toSQL() + + const { sql: knexSql, bindings: knexBindings } = connection.client! + .from('users') + .min({ + u: connection.client!.raw('select * from profiles where is_verified = ?', [true]), + e: 'email', + }) + .toSQL() + + assert.equal(sql, knexSql) + assert.deepEqual(bindings, knexBindings) + }) + + test('use subquery to compute min with multiple columns', (assert) => { + const connection = new Connection('primary', getConfig()) + connection.connect() + + const db = getQueryBuilder(connection) + const { sql, bindings } = db + .from('users') + .min({ + u: getQueryBuilder(connection).where('is_verified', true).from('profiles'), + e: 'email', + }) + .toSQL() + + const { sql: knexSql, bindings: knexBindings } = connection.client! + .from('users') + .min({ + u: connection.client!.where('is_verified', true).from('profiles'), + e: 'email', + }) + .toSQL() + + assert.equal(sql, knexSql) + assert.deepEqual(bindings, knexBindings) + }) +}) + +test.group('Query Builder | max', (group) => { + group.before(async () => { + await setup() + }) + + group.after(async () => { + await cleanup() + }) + + test('use max function', (assert) => { + const connection = new Connection('primary', getConfig()) + connection.connect() + + const db = getQueryBuilder(connection) + const { sql, bindings } = db + .from('users') + .max('*', 'biggest') + .toSQL() + + const { sql: knexSql, bindings: knexBindings } = connection.client! + .from('users') + .max('*', { as: 'biggest' }) + .toSQL() + + assert.equal(sql, knexSql) + assert.deepEqual(bindings, knexBindings) + }) + + test('use max function for multiple times', (assert) => { + const connection = new Connection('primary', getConfig()) + connection.connect() + + const db = getQueryBuilder(connection) + const { sql, bindings } = db + .from('users') + .max({ u: 'username', e: 'email' }) + .toSQL() + + const { sql: knexSql, bindings: knexBindings } = connection.client! + .from('users') + .max({ u: 'username', e: 'email' }) + .toSQL() + + assert.equal(sql, knexSql) + assert.deepEqual(bindings, knexBindings) + }) + + test('use raw queries to compute max', (assert) => { + const connection = new Connection('primary', getConfig()) + connection.connect() + + const db = getQueryBuilder(connection) + const { sql, bindings } = db + .from('users') + .max( + getRawQueryBuilder(connection, 'select * from profiles where is_verified = ?', [true]), + 'u', + ) + .toSQL() + + const { sql: knexSql, bindings: knexBindings } = connection.client! + .from('users') + .max({ + u: connection.client!.raw('select * from profiles where is_verified = ?', [true]), + }) + .toSQL() + + assert.equal(sql, knexSql) + assert.deepEqual(bindings, knexBindings) + }) + + test('use subqueries to compute max', (assert) => { + const connection = new Connection('primary', getConfig()) + connection.connect() + + const db = getQueryBuilder(connection) + const { sql, bindings } = db + .from('users') + .max(getQueryBuilder(connection).where('is_verified', true).from('profiles'), 'u') + .toSQL() + + const { sql: knexSql, bindings: knexBindings } = connection.client! + .from('users') + .max({ + u: connection.client!.where('is_verified', true).from('profiles'), + }) + .toSQL() + + assert.equal(sql, knexSql) + assert.deepEqual(bindings, knexBindings) + }) + + test('use raw query to compute max with multiple columns', (assert) => { + const connection = new Connection('primary', getConfig()) + connection.connect() + + const db = getQueryBuilder(connection) + const { sql, bindings } = db + .from('users') + .max({ + u: getRawQueryBuilder(connection, 'select * from profiles where is_verified = ?', [true]), + e: 'email', + }) + .toSQL() + + const { sql: knexSql, bindings: knexBindings } = connection.client! + .from('users') + .max({ + u: connection.client!.raw('select * from profiles where is_verified = ?', [true]), + e: 'email', + }) + .toSQL() + + assert.equal(sql, knexSql) + assert.deepEqual(bindings, knexBindings) + }) + + test('use subquery to compute max with multiple columns', (assert) => { + const connection = new Connection('primary', getConfig()) + connection.connect() + + const db = getQueryBuilder(connection) + const { sql, bindings } = db + .from('users') + .max({ + u: getQueryBuilder(connection).where('is_verified', true).from('profiles'), + e: 'email', + }) + .toSQL() + + const { sql: knexSql, bindings: knexBindings } = connection.client! + .from('users') + .max({ + u: connection.client!.where('is_verified', true).from('profiles'), + e: 'email', + }) + .toSQL() + + assert.equal(sql, knexSql) + assert.deepEqual(bindings, knexBindings) + }) +}) + +test.group('Query Builder | sum', (group) => { + group.before(async () => { + await setup() + }) + + group.after(async () => { + await cleanup() + }) + + test('use sum function', (assert) => { + const connection = new Connection('primary', getConfig()) + connection.connect() + + const db = getQueryBuilder(connection) + const { sql, bindings } = db + .from('users') + .sum('*', 'total') + .toSQL() + + const { sql: knexSql, bindings: knexBindings } = connection.client! + .from('users') + .sum('*', { as: 'total' }) + .toSQL() + + assert.equal(sql, knexSql) + assert.deepEqual(bindings, knexBindings) + }) + + test('use sum function for multiple times', (assert) => { + const connection = new Connection('primary', getConfig()) + connection.connect() + + const db = getQueryBuilder(connection) + const { sql, bindings } = db + .from('users') + .sum({ u: 'username', e: 'email' }) + .toSQL() + + const { sql: knexSql, bindings: knexBindings } = connection.client! + .from('users') + .sum({ u: 'username', e: 'email' }) + .toSQL() + + assert.equal(sql, knexSql) + assert.deepEqual(bindings, knexBindings) + }) + + test('use raw queries to compute sum', (assert) => { + const connection = new Connection('primary', getConfig()) + connection.connect() + + const db = getQueryBuilder(connection) + const { sql, bindings } = db + .from('users') + .sum( + getRawQueryBuilder(connection, 'select * from profiles where is_verified = ?', [true]), + 'u', + ) + .toSQL() + + const { sql: knexSql, bindings: knexBindings } = connection.client! + .from('users') + .sum({ + u: connection.client!.raw('select * from profiles where is_verified = ?', [true]), + }) + .toSQL() + + assert.equal(sql, knexSql) + assert.deepEqual(bindings, knexBindings) + }) + + test('use subqueries to compute sum', (assert) => { + const connection = new Connection('primary', getConfig()) + connection.connect() + + const db = getQueryBuilder(connection) + const { sql, bindings } = db + .from('users') + .sum(getQueryBuilder(connection).where('is_verified', true).from('profiles'), 'u') + .toSQL() + + const { sql: knexSql, bindings: knexBindings } = connection.client! + .from('users') + .sum({ + u: connection.client!.where('is_verified', true).from('profiles'), + }) + .toSQL() + + assert.equal(sql, knexSql) + assert.deepEqual(bindings, knexBindings) + }) + + test('use raw query to compute sum with multiple columns', (assert) => { + const connection = new Connection('primary', getConfig()) + connection.connect() + + const db = getQueryBuilder(connection) + const { sql, bindings } = db + .from('users') + .sum({ + u: getRawQueryBuilder(connection, 'select * from profiles where is_verified = ?', [true]), + e: 'email', + }) + .toSQL() + + const { sql: knexSql, bindings: knexBindings } = connection.client! + .from('users') + .sum({ + u: connection.client!.raw('select * from profiles where is_verified = ?', [true]), + e: 'email', + }) + .toSQL() + + assert.equal(sql, knexSql) + assert.deepEqual(bindings, knexBindings) + }) + + test('use subquery to compute sum with multiple columns', (assert) => { + const connection = new Connection('primary', getConfig()) + connection.connect() + + const db = getQueryBuilder(connection) + const { sql, bindings } = db + .from('users') + .sum({ + u: getQueryBuilder(connection).where('is_verified', true).from('profiles'), + e: 'email', + }) + .toSQL() + + const { sql: knexSql, bindings: knexBindings } = connection.client! + .from('users') + .sum({ + u: connection.client!.where('is_verified', true).from('profiles'), + e: 'email', + }) + .toSQL() + + assert.equal(sql, knexSql) + assert.deepEqual(bindings, knexBindings) + }) +}) + +test.group('Query Builder | avg', (group) => { + group.before(async () => { + await setup() + }) + + group.after(async () => { + await cleanup() + }) + + test('use avg function', (assert) => { + const connection = new Connection('primary', getConfig()) + connection.connect() + + const db = getQueryBuilder(connection) + const { sql, bindings } = db + .from('users') + .avg('*', 'avg') + .toSQL() + + const { sql: knexSql, bindings: knexBindings } = connection.client! + .from('users') + .avg('*', { as: 'avg' }) + .toSQL() + + assert.equal(sql, knexSql) + assert.deepEqual(bindings, knexBindings) + }) + + test('use avg function for multiple times', (assert) => { + const connection = new Connection('primary', getConfig()) + connection.connect() + + const db = getQueryBuilder(connection) + const { sql, bindings } = db + .from('users') + .avg({ u: 'username', e: 'email' }) + .toSQL() + + const { sql: knexSql, bindings: knexBindings } = connection.client! + .from('users') + .avg({ u: 'username', e: 'email' }) + .toSQL() + + assert.equal(sql, knexSql) + assert.deepEqual(bindings, knexBindings) + }) + + test('use raw queries to compute avg', (assert) => { + const connection = new Connection('primary', getConfig()) + connection.connect() + + const db = getQueryBuilder(connection) + const { sql, bindings } = db + .from('users') + .avg( + getRawQueryBuilder(connection, 'select * from profiles where is_verified = ?', [true]), + 'u', + ) + .toSQL() + + const { sql: knexSql, bindings: knexBindings } = connection.client! + .from('users') + .avg({ + u: connection.client!.raw('select * from profiles where is_verified = ?', [true]), + }) + .toSQL() + + assert.equal(sql, knexSql) + assert.deepEqual(bindings, knexBindings) + }) + + test('use subqueries to compute avg', (assert) => { + const connection = new Connection('primary', getConfig()) + connection.connect() + + const db = getQueryBuilder(connection) + const { sql, bindings } = db + .from('users') + .avg(getQueryBuilder(connection).where('is_verified', true).from('profiles'), 'u') + .toSQL() + + const { sql: knexSql, bindings: knexBindings } = connection.client! + .from('users') + .avg({ + u: connection.client!.where('is_verified', true).from('profiles'), + }) + .toSQL() + + assert.equal(sql, knexSql) + assert.deepEqual(bindings, knexBindings) + }) + + test('use raw query to compute avg with multiple columns', (assert) => { + const connection = new Connection('primary', getConfig()) + connection.connect() + + const db = getQueryBuilder(connection) + const { sql, bindings } = db + .from('users') + .avg({ + u: getRawQueryBuilder(connection, 'select * from profiles where is_verified = ?', [true]), + e: 'email', + }) + .toSQL() + + const { sql: knexSql, bindings: knexBindings } = connection.client! + .from('users') + .avg({ + u: connection.client!.raw('select * from profiles where is_verified = ?', [true]), + e: 'email', + }) + .toSQL() + + assert.equal(sql, knexSql) + assert.deepEqual(bindings, knexBindings) + }) + + test('use subquery to compute avg with multiple columns', (assert) => { + const connection = new Connection('primary', getConfig()) + connection.connect() + + const db = getQueryBuilder(connection) + const { sql, bindings } = db + .from('users') + .avg({ + u: getQueryBuilder(connection).where('is_verified', true).from('profiles'), + e: 'email', + }) + .toSQL() + + const { sql: knexSql, bindings: knexBindings } = connection.client! + .from('users') + .avg({ + u: connection.client!.where('is_verified', true).from('profiles'), + e: 'email', + }) + .toSQL() + + assert.equal(sql, knexSql) + assert.deepEqual(bindings, knexBindings) + }) +}) + +test.group('Query Builder | avgDistinct', (group) => { + group.before(async () => { + await setup() + }) + + group.after(async () => { + await cleanup() + }) + + test('use avgDistinct function', (assert) => { + const connection = new Connection('primary', getConfig()) + connection.connect() + + const db = getQueryBuilder(connection) + const { sql, bindings } = db + .from('users') + .avgDistinct('*', 'avgDistinct') + .toSQL() + + const { sql: knexSql, bindings: knexBindings } = connection.client! + .from('users') + .avgDistinct('*', { as: 'avgDistinct' }) + .toSQL() + + assert.equal(sql, knexSql) + assert.deepEqual(bindings, knexBindings) + }) + + test('use avgDistinct function for multiple times', (assert) => { + const connection = new Connection('primary', getConfig()) + connection.connect() + + const db = getQueryBuilder(connection) + const { sql, bindings } = db + .from('users') + .avgDistinct({ u: 'username', e: 'email' }) + .toSQL() + + const { sql: knexSql, bindings: knexBindings } = connection.client! + .from('users') + .avgDistinct({ u: 'username', e: 'email' }) + .toSQL() + + assert.equal(sql, knexSql) + assert.deepEqual(bindings, knexBindings) + }) + + test('use raw queries to compute avgDistinct', (assert) => { + const connection = new Connection('primary', getConfig()) + connection.connect() + + const db = getQueryBuilder(connection) + const { sql, bindings } = db + .from('users') + .avgDistinct( + getRawQueryBuilder(connection, 'select * from profiles where is_verified = ?', [true]), + 'u', + ) + .toSQL() + + const { sql: knexSql, bindings: knexBindings } = connection.client! + .from('users') + .avgDistinct({ + u: connection.client!.raw('select * from profiles where is_verified = ?', [true]), + }) + .toSQL() + + assert.equal(sql, knexSql) + assert.deepEqual(bindings, knexBindings) + }) + + test('use subqueries to compute avgDistinct', (assert) => { + const connection = new Connection('primary', getConfig()) + connection.connect() + + const db = getQueryBuilder(connection) + const { sql, bindings } = db + .from('users') + .avgDistinct(getQueryBuilder(connection).where('is_verified', true).from('profiles'), 'u') + .toSQL() + + const { sql: knexSql, bindings: knexBindings } = connection.client! + .from('users') + .avgDistinct({ + u: connection.client!.where('is_verified', true).from('profiles'), + }) + .toSQL() + + assert.equal(sql, knexSql) + assert.deepEqual(bindings, knexBindings) + }) + + test('use raw query to compute avgDistinct with multiple columns', (assert) => { + const connection = new Connection('primary', getConfig()) + connection.connect() + + const db = getQueryBuilder(connection) + const { sql, bindings } = db + .from('users') + .avgDistinct({ + u: getRawQueryBuilder(connection, 'select * from profiles where is_verified = ?', [true]), + e: 'email', + }) + .toSQL() + + const { sql: knexSql, bindings: knexBindings } = connection.client! + .from('users') + .avgDistinct({ + u: connection.client!.raw('select * from profiles where is_verified = ?', [true]), + e: 'email', + }) + .toSQL() + + assert.equal(sql, knexSql) + assert.deepEqual(bindings, knexBindings) + }) + + test('use subquery to compute avgDistinct with multiple columns', (assert) => { + const connection = new Connection('primary', getConfig()) + connection.connect() + + const db = getQueryBuilder(connection) + const { sql, bindings } = db + .from('users') + .avgDistinct({ + u: getQueryBuilder(connection).where('is_verified', true).from('profiles'), + e: 'email', + }) + .toSQL() + + const { sql: knexSql, bindings: knexBindings } = connection.client! + .from('users') + .avgDistinct({ + u: connection.client!.where('is_verified', true).from('profiles'), + e: 'email', + }) + .toSQL() + + assert.equal(sql, knexSql) + assert.deepEqual(bindings, knexBindings) + }) +})