UNPKG

@adonisjs/lucid

Version:

SQL ORM built on top of Active Record pattern

1,690 lines 53.9 kB
/* * @adonisjs/lucid * * (c) Harminder Virk <virk@adonisjs.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ import Macroable from '@poppinss/macroable'; import { Exception } from '@poppinss/utils'; import { isObject } from '../../utils/index.js'; import { RawQueryBuilder } from './raw.js'; import { RawBuilder } from '../static_builder/raw.js'; import { ReferenceBuilder } from '../static_builder/reference.js'; /** * The chainable query builder to construct SQL queries for selecting, updating and * deleting records. * * The API internally uses the knex query builder. However, many of methods may have * different API. */ export class Chainable extends Macroable { knexQuery; queryCallback; keysResolver; hasAggregates = false; hasGroupBy = false; hasUnion = false; /** * Collection where clauses in a 2nd array. Calling `wrapExisting` * adds a new stack item */ whereStack = [[]]; /** * Returns the recent most array from the where stack */ getRecentStackItem() { return this.whereStack[this.whereStack.length - 1]; } /** * Returns the wrapping method for a given where method */ getWrappingMethod(method) { if (method.startsWith('or')) { return { method: method, wrappingMethod: 'orWhere', }; } return { method: method, wrappingMethod: 'where', }; } /** * Applies the where clauses */ applyWhere() { this.knexQuery.clearWhere(); if (this.whereStack.length === 1) { this.whereStack[0].forEach(({ method, args }) => { ; this.knexQuery[method](...args); }); return; } this.whereStack.forEach((collection) => { if (collection.length) { const firstItem = collection.shift(); const wrapper = this.getWrappingMethod(firstItem.method); this.knexQuery[wrapper.wrappingMethod]((subquery) => { subquery[wrapper.method](...firstItem.args); collection.forEach(({ method, args }) => subquery[method](...args)); }); } }); } /** * An array of selected columns */ get columns() { return this.knexQuery['_statements'] .filter(({ grouping }) => grouping === 'columns') .reduce((result, { value }) => { result = result.concat(value); return result; }, []); } /** * Custom alias for the query results. Ignored if it not a * subquery */ subQueryAlias; constructor(knexQuery, queryCallback, keysResolver) { super(); this.knexQuery = knexQuery; this.queryCallback = queryCallback; this.keysResolver = keysResolver; } /** * Raises exception when only one argument is passed to a where * clause and it is a string. It means the value is undefined */ validateWhereSingleArgument(value, method) { if (typeof value === 'string') { throw new Exception(`".${method}" expects value to be defined, but undefined is passed`); } } /** * Returns the value pair for the `whereBetween` clause */ getBetweenPair(value) { const [lhs, rhs] = value; if (lhs === undefined || rhs === undefined) { throw new Error('Invalid array for whereBetween value'); } return [this.transformValue(lhs), this.transformValue(rhs)]; } /** * Normalizes the columns aggregates functions to something * knex can process. */ normalizeAggregateColumns(columns, alias) { if (columns.constructor === Object) { return Object.keys(columns).reduce((result, key) => { const value = columns[key]; result[key] = typeof value === 'string' ? this.resolveKey(value) : this.transformValue(value); return result; }, {}); } if (!alias) { return columns; } return { [alias]: typeof columns === 'string' ? this.resolveKey(columns) : this.transformValue(columns), }; } /** * Resolves the column name considering raw queries as well. */ resolveColumn(columns, checkForObject = false, returnValue) { if (columns instanceof RawQueryBuilder) { return columns['knexQuery']; } if (columns instanceof RawBuilder) { return columns.toKnex(this.knexQuery.client); } return this.resolveKey(columns, checkForObject, returnValue); } /** * Resolves column names */ resolveKey(columns, checkForObject = false, returnValue) { /** * If there is no keys resolver in place, then return the * optional return value or defined column(s) */ if (!this.keysResolver) { return returnValue || columns; } /** * If column is a string, then resolve it as a key */ if (typeof columns === 'string') { return columns === '*' ? columns : this.keysResolver(columns); } /** * If check for objects is enabled, then resolve object keys */ if (checkForObject && isObject(columns)) { return Object.keys(columns).reduce((result, column) => { result[this.keysResolver(column)] = columns[column]; return result; }, {}); } /** * Return the return value or columns as fallback */ return returnValue || columns; } /** * Apply existing query flags to a new query builder. This is * done during clone operation */ applyQueryFlags(query) { query.hasAggregates = this.hasAggregates; query.hasGroupBy = this.hasGroupBy; query.hasUnion = this.hasUnion; query.whereStack = this.whereStack.map((collection) => { return collection.map((node) => node); }); } /** * Transforms the value to something that knex can internally understand and * handle. It includes. * * 1. Returning the `knexBuilder` for sub queries. * 2. Returning the `knex.refBuilder` for reference builder. * 2. Returning the `knexBuilder` for raw queries. * 3. Wrapping callbacks, so that the end user receives an instance Lucid query * builder and not knex query builder. */ transformValue(value) { if (value instanceof Chainable) { return value.toKnex(); } if (value instanceof ReferenceBuilder) { return value.toKnex(this.knexQuery.client); } if (typeof value === 'function') { return this.transformCallback(value); } return this.transformRaw(value); } /** * Transforms the user callback to something that knex * can internally process */ transformCallback(value) { if (typeof value === 'function') { return this.queryCallback(value, this.keysResolver); } return value; } /** * Returns the underlying knex raw query builder for Lucid raw * query builder */ transformRaw(value) { if (value instanceof RawQueryBuilder) { return value['knexQuery']; } if (value instanceof RawBuilder) { return value.toKnex(this.knexQuery.client); } return value; } /** * Define columns for selection */ select(...args) { let columns = args; if (Array.isArray(args[0])) { columns = args[0]; } this.knexQuery.select(columns.map((column) => { if (typeof column === 'string') { return this.resolveKey(column, true); } return this.transformValue(column); })); return this; } /** * Select table for the query. Re-calling this method multiple times will * use the last selected table */ from(table) { this.knexQuery.from(this.transformValue(table)); return this; } /** * Wrap existing where clauses to its own group */ wrapExisting() { if (this.getRecentStackItem().length) { this.whereStack.push([]); } return this; } /** * Add a `where` clause */ where(key, operator, value) { const whereClauses = this.getRecentStackItem(); if (value !== undefined) { whereClauses.push({ method: 'where', args: [this.resolveColumn(key), operator, this.transformValue(value)], }); } else if (operator !== undefined) { whereClauses.push({ method: 'where', args: [this.resolveColumn(key), this.transformValue(operator)], }); } else { /** * Only callback is allowed as a standalone param. One must use `whereRaw` * for raw/sub queries. This is our limitation to have consistent API */ this.validateWhereSingleArgument(key, 'where'); whereClauses.push({ method: 'where', args: [this.resolveColumn(key, true, this.transformCallback(key))], }); } return this; } /** * Add a `or where` clause */ orWhere(key, operator, value) { const whereClauses = this.getRecentStackItem(); if (value !== undefined) { whereClauses.push({ method: 'orWhere', args: [this.resolveColumn(key), operator, this.transformValue(value)], }); } else if (operator !== undefined) { whereClauses.push({ method: 'orWhere', args: [this.resolveColumn(key), this.transformValue(operator)], }); } else { this.validateWhereSingleArgument(key, 'orWhere'); whereClauses.push({ method: 'orWhere', args: [this.resolveColumn(key, true, this.transformCallback(key))], }); } return this; } /** * Alias for `where` */ andWhere(key, operator, value) { return this.where(key, operator, value); } /** * Adding `where not` clause */ whereNot(key, operator, value) { const whereClauses = this.getRecentStackItem(); if (value !== undefined) { whereClauses.push({ method: 'whereNot', args: [this.resolveColumn(key), operator, this.transformValue(value)], }); } else if (operator !== undefined) { whereClauses.push({ method: 'whereNot', args: [this.resolveColumn(key), this.transformValue(operator)], }); } else { this.validateWhereSingleArgument(key, 'whereNot'); whereClauses.push({ method: 'whereNot', args: [this.resolveColumn(key, true, this.transformCallback(key))], }); } return this; } /** * Adding `or where not` clause */ orWhereNot(key, operator, value) { const whereClauses = this.getRecentStackItem(); if (value !== undefined) { whereClauses.push({ method: 'orWhereNot', args: [this.resolveColumn(key), operator, this.transformValue(value)], }); } else if (operator !== undefined) { whereClauses.push({ method: 'orWhereNot', args: [this.resolveColumn(key), this.transformValue(operator)], }); } else { this.validateWhereSingleArgument(key, 'orWhereNot'); whereClauses.push({ method: 'orWhereNot', args: [this.resolveColumn(key, true, this.transformCallback(key))], }); } return this; } /** * Alias for [[whereNot]] */ andWhereNot(key, operator, value) { return this.whereNot(key, operator, value); } /** * Add a where clause on a given column */ whereColumn(column, operator, comparisonColumn) { if (comparisonColumn !== undefined) { this.where(column, operator, new ReferenceBuilder(comparisonColumn, this.knexQuery.client)); } else { this.where(column, new ReferenceBuilder(operator, this.knexQuery.client)); } return this; } /** * Add a orWhere clause on a given column */ orWhereColumn(column, operator, comparisonColumn) { if (comparisonColumn !== undefined) { this.orWhere(column, operator, new ReferenceBuilder(comparisonColumn, this.knexQuery.client)); } else { this.orWhere(column, new ReferenceBuilder(operator, this.knexQuery.client)); } return this; } /** * Alias for whereColumn */ andWhereColumn(column, operator, comparisonColumn) { return this.whereColumn(column, operator, comparisonColumn); } /** * Add a whereNot clause on a given column */ whereNotColumn(column, operator, comparisonColumn) { if (comparisonColumn !== undefined) { this.whereNot(column, operator, new ReferenceBuilder(comparisonColumn, this.knexQuery.client)); } else { this.whereNot(column, new ReferenceBuilder(operator, this.knexQuery.client)); } return this; } /** * Add a orWhereNotColumn clause on a given column */ orWhereNotColumn(column, operator, comparisonColumn) { if (comparisonColumn !== undefined) { this.orWhereNot(column, operator, new ReferenceBuilder(comparisonColumn, this.knexQuery.client)); } else { this.orWhereNot(column, new ReferenceBuilder(operator, this.knexQuery.client)); } return this; } /** * Alias for whereNotColumn */ andWhereNotColumn(column, operator, comparisonColumn) { return this.whereNotColumn(column, operator, comparisonColumn); } /** * Adding a `where in` clause */ whereIn(columns, value) { value = Array.isArray(value) ? value.map((one) => this.transformValue(one)) : this.transformValue(value); columns = Array.isArray(columns) ? columns.map((column) => this.resolveColumn(column)) : this.resolveColumn(columns); const whereClauses = this.getRecentStackItem(); whereClauses.push({ method: 'whereIn', args: [columns, value], }); return this; } /** * Adding a `or where in` clause */ orWhereIn(columns, value) { value = Array.isArray(value) ? value.map((one) => this.transformValue(one)) : this.transformValue(value); columns = Array.isArray(columns) ? columns.map((column) => this.resolveColumn(column)) : this.resolveColumn(columns); const whereClauses = this.getRecentStackItem(); whereClauses.push({ method: 'orWhereIn', args: [columns, value], }); return this; } /** * Alias for [[whereIn]] */ andWhereIn(key, value) { return this.whereIn(key, value); } /** * Adding a `where not in` clause */ whereNotIn(columns, value) { value = Array.isArray(value) ? value.map((one) => this.transformValue(one)) : this.transformValue(value); columns = Array.isArray(columns) ? columns.map((column) => this.resolveColumn(column)) : this.resolveColumn(columns); const whereClauses = this.getRecentStackItem(); whereClauses.push({ method: 'whereNotIn', args: [columns, value], }); return this; } /** * Adding a `or where not in` clause */ orWhereNotIn(columns, value) { value = Array.isArray(value) ? value.map((one) => this.transformValue(one)) : this.transformValue(value); columns = Array.isArray(columns) ? columns.map((column) => this.resolveColumn(column)) : this.resolveColumn(columns); const whereClauses = this.getRecentStackItem(); whereClauses.push({ method: 'orWhereNotIn', args: [columns, value], }); return this; } /** * Alias for [[whereNotIn]] */ andWhereNotIn(key, value) { return this.whereNotIn(key, value); } /** * Adding `where not null` clause */ whereNull(key) { const whereClauses = this.getRecentStackItem(); whereClauses.push({ method: 'whereNull', args: [this.resolveColumn(key)], }); return this; } /** * Adding `or where not null` clause */ orWhereNull(key) { const whereClauses = this.getRecentStackItem(); whereClauses.push({ method: 'orWhereNull', args: [this.resolveColumn(key)], }); return this; } /** * Alias for [[whereNull]] */ andWhereNull(key) { return this.whereNull(key); } /** * Adding `where not null` clause */ whereNotNull(key) { const whereClauses = this.getRecentStackItem(); whereClauses.push({ method: 'whereNotNull', args: [this.resolveColumn(key)], }); return this; } /** * Adding `or where not null` clause */ orWhereNotNull(key) { const whereClauses = this.getRecentStackItem(); whereClauses.push({ method: 'orWhereNotNull', args: [this.resolveColumn(key)], }); return this; } /** * Alias for [[whereNotNull]] */ andWhereNotNull(key) { return this.whereNotNull(key); } /** * Add a `where exists` clause */ whereExists(value) { const whereClauses = this.getRecentStackItem(); whereClauses.push({ method: 'whereExists', args: [this.transformValue(value)], }); return this; } /** * Add a `or where exists` clause */ orWhereExists(value) { const whereClauses = this.getRecentStackItem(); whereClauses.push({ method: 'orWhereExists', args: [this.transformValue(value)], }); return this; } /** * Alias for [[whereExists]] */ andWhereExists(value) { return this.whereExists(value); } /** * Add a `where not exists` clause */ whereNotExists(value) { const whereClauses = this.getRecentStackItem(); whereClauses.push({ method: 'whereNotExists', args: [this.transformValue(value)], }); return this; } /** * Add a `or where not exists` clause */ orWhereNotExists(value) { const whereClauses = this.getRecentStackItem(); whereClauses.push({ method: 'orWhereNotExists', args: [this.transformValue(value)], }); return this; } /** * Alias for [[whereNotExists]] */ andWhereNotExists(value) { return this.whereNotExists(value); } /** * Add where between clause */ whereBetween(key, value) { const whereClauses = this.getRecentStackItem(); whereClauses.push({ method: 'whereBetween', args: [this.resolveColumn(key), this.getBetweenPair(value)], }); return this; } /** * Add where between clause */ orWhereBetween(key, value) { const whereClauses = this.getRecentStackItem(); whereClauses.push({ method: 'orWhereBetween', args: [this.resolveColumn(key), this.getBetweenPair(value)], }); return this; } /** * Alias for [[whereBetween]] */ andWhereBetween(key, value) { return this.whereBetween(key, value); } /** * Add where between clause */ whereNotBetween(key, value) { const whereClauses = this.getRecentStackItem(); whereClauses.push({ method: 'whereNotBetween', args: [this.resolveColumn(key), this.getBetweenPair(value)], }); return this; } /** * Add where between clause */ orWhereNotBetween(key, value) { const whereClauses = this.getRecentStackItem(); whereClauses.push({ method: 'orWhereNotBetween', args: [this.resolveColumn(key), this.getBetweenPair(value)], }); return this; } /** * Alias for [[whereNotBetween]] */ andWhereNotBetween(key, value) { return this.whereNotBetween(key, value); } /** * Adding a where clause using raw sql */ whereRaw(sql, bindings) { const whereClauses = this.getRecentStackItem(); if (bindings) { bindings = Array.isArray(bindings) ? bindings.map((binding) => this.transformValue(binding)) : bindings; whereClauses.push({ method: 'whereRaw', args: [sql, bindings], }); } else { whereClauses.push({ method: 'whereRaw', args: [this.transformRaw(sql)], }); } return this; } /** * Adding a or where clause using raw sql */ orWhereRaw(sql, bindings) { const whereClauses = this.getRecentStackItem(); if (bindings) { bindings = Array.isArray(bindings) ? bindings.map((binding) => this.transformValue(binding)) : bindings; whereClauses.push({ method: 'orWhereRaw', args: [sql, bindings], }); } else { whereClauses.push({ method: 'orWhereRaw', args: [this.transformRaw(sql)], }); } return this; } /** * Alias for [[whereRaw]] */ andWhereRaw(sql, bindings) { return this.whereRaw(sql, bindings); } /** * Add a `where like` clause */ whereLike(key, value) { const whereClauses = this.getRecentStackItem(); whereClauses.push({ method: 'whereLike', args: [this.resolveColumn(key), this.transformValue(value)], }); return this; } /** * Add a `where like` clause */ orWhereLike(key, value) { const whereClauses = this.getRecentStackItem(); whereClauses.push({ method: 'orWhereLike', args: [this.resolveColumn(key), this.transformValue(value)], }); return this; } /** * Add a `where like` clause */ andWhereLike(key, value) { return this.whereLike(key, value); } /** * Add a `where like` clause */ whereILike(key, value) { const whereClauses = this.getRecentStackItem(); whereClauses.push({ method: 'whereILike', args: [this.resolveColumn(key), this.transformValue(value)], }); return this; } /** * Add a `where like` clause */ orWhereILike(key, value) { const whereClauses = this.getRecentStackItem(); whereClauses.push({ method: 'orWhereILike', args: [this.resolveColumn(key), this.transformValue(value)], }); return this; } /** * Add a `where like` clause */ andWhereILike(key, value) { return this.whereILike(key, value); } /** * Define a where clause with value that matches for JSON */ whereJson(column, value) { const whereClauses = this.getRecentStackItem(); whereClauses.push({ method: 'whereJsonObject', args: [this.resolveColumn(column), this.transformValue(value)], }); return this; } /** * Define a or where clause with value that matches for JSON */ orWhereJson(column, value) { const whereClauses = this.getRecentStackItem(); whereClauses.push({ method: 'orWhereJsonObject', args: [this.resolveColumn(column), this.transformValue(value)], }); return this; } /** * Define a where clause with value that matches for JSON * * @alias whereJson */ andWhereJson(column, value) { return this.whereJson(column, value); } /** * Define a where clause with value that matches for JSON */ whereNotJson(column, value) { const whereClauses = this.getRecentStackItem(); whereClauses.push({ method: 'whereNotJsonObject', args: [this.resolveColumn(column), this.transformValue(value)], }); return this; } /** * Define a or where clause with value that matches for JSON */ orWhereNotJson(column, value) { const whereClauses = this.getRecentStackItem(); whereClauses.push({ method: 'orWhereNotJsonObject', args: [this.resolveColumn(column), this.transformValue(value)], }); return this; } /** * Define a where clause with value that matches for JSON * * @alias whereNotJson */ andWhereNotJson(column, value) { return this.whereNotJson(column, value); } /** * Define a where clause with value that matches for a superset of * JSON */ whereJsonSuperset(column, value) { const whereClauses = this.getRecentStackItem(); whereClauses.push({ method: 'whereJsonSupersetOf', args: [this.resolveColumn(column), this.transformValue(value)], }); return this; } /** * Define a or where clause with value that matches for a superset of * JSON */ orWhereJsonSuperset(column, value) { const whereClauses = this.getRecentStackItem(); whereClauses.push({ method: 'orWhereJsonSupersetOf', args: [this.resolveColumn(column), this.transformValue(value)], }); return this; } /** * Define or where clause with value that matches for a superset of * JSON * * @alias whereJsonSuperset */ andWhereJsonSuperset(column, value) { return this.whereJsonSuperset(column, value); } /** * Define a where clause with value that matches for a superset of * JSON */ whereNotJsonSuperset(column, value) { const whereClauses = this.getRecentStackItem(); whereClauses.push({ method: 'whereJsonNotSupersetOf', args: [this.resolveColumn(column), this.transformValue(value)], }); return this; } /** * Define a or where clause with value that matches for a superset of * JSON */ orWhereNotJsonSuperset(column, value) { const whereClauses = this.getRecentStackItem(); whereClauses.push({ method: 'orWhereJsonNotSupersetOf', args: [this.resolveColumn(column), this.transformValue(value)], }); return this; } /** * Define or where clause with value that matches for a superset of * JSON * * @alias whereNotJsonSuperset */ andWhereNotJsonSuperset(column, value) { return this.whereNotJsonSuperset(column, value); } /** * Define a where clause with value that matches for a subset of * JSON */ whereJsonSubset(column, value) { const whereClauses = this.getRecentStackItem(); whereClauses.push({ method: 'whereJsonSubsetOf', args: [this.resolveColumn(column), this.transformValue(value)], }); return this; } /** * Define a or where clause with value that matches for a subset of * JSON */ orWhereJsonSubset(column, value) { const whereClauses = this.getRecentStackItem(); whereClauses.push({ method: 'orWhereJsonSubsetOf', args: [this.resolveColumn(column), this.transformValue(value)], }); return this; } /** * Define or where clause with value that matches for a subset of * JSON * * @alias whereJsonSubset */ andWhereJsonSubset(column, value) { return this.whereJsonSubset(column, value); } /** * Define a where clause with value that matches for a subset of * JSON */ whereNotJsonSubset(column, value) { const whereClauses = this.getRecentStackItem(); whereClauses.push({ method: 'whereJsonNotSubsetOf', args: [this.resolveColumn(column), this.transformValue(value)], }); return this; } /** * Define a or where clause with value that matches for a subset of * JSON */ orWhereNotJsonSubset(column, value) { const whereClauses = this.getRecentStackItem(); whereClauses.push({ method: 'orWhereJsonNotSubsetOf', args: [this.resolveColumn(column), this.transformValue(value)], }); return this; } /** * Define or where clause with value that matches for a subset of * JSON * * @alias whereNotJsonSubset */ andWhereNotJsonSubset(column, value) { return this.whereNotJsonSubset(column, value); } /** * Adds a where clause with comparison of a value returned * by a JsonPath given an operator and a value. */ whereJsonPath(column, jsonPath, operator, value) { const whereClauses = this.getRecentStackItem(); if (!value) { value = operator; operator = '='; } whereClauses.push({ method: 'whereJsonPath', args: [this.resolveColumn(column), jsonPath, operator, this.transformValue(value)], }); return this; } /** * Adds a or where clause with comparison of a value returned * by a JsonPath given an operator and a value. */ orWhereJsonPath(column, jsonPath, operator, value) { const whereClauses = this.getRecentStackItem(); if (!value) { value = operator; operator = '='; } whereClauses.push({ method: 'orWhereJsonPath', args: [this.resolveColumn(column), jsonPath, operator, this.transformValue(value)], }); return this; } /** * Adds a where clause with comparison of a value returned * by a JsonPath given an operator and a value. * * @alias whereJsonPath */ andWhereJsonPath(column, jsonPath, operator, value) { return this.whereJsonPath(column, jsonPath, operator, value); } /** * Add a join clause */ join(table, first, operator, second) { if (second !== undefined) { this.knexQuery.join(table, first, operator, this.transformRaw(second)); } else if (operator !== undefined) { this.knexQuery.join(table, first, this.transformRaw(operator)); } else { this.knexQuery.join(table, this.transformRaw(first)); } return this; } /** * Add an inner join clause */ innerJoin(table, first, operator, second) { if (second !== undefined) { this.knexQuery.innerJoin(table, first, operator, this.transformRaw(second)); } else if (operator !== undefined) { this.knexQuery.innerJoin(table, first, this.transformRaw(operator)); } else { this.knexQuery.innerJoin(table, this.transformRaw(first)); } return this; } /** * Add a left join clause */ leftJoin(table, first, operator, second) { if (second !== undefined) { this.knexQuery.leftJoin(table, first, operator, this.transformRaw(second)); } else if (operator !== undefined) { this.knexQuery.leftJoin(table, first, this.transformRaw(operator)); } else { this.knexQuery.leftJoin(table, this.transformRaw(first)); } return this; } /** * Add a left outer join clause */ leftOuterJoin(table, first, operator, second) { if (second !== undefined) { this.knexQuery.leftOuterJoin(table, first, operator, this.transformRaw(second)); } else if (operator !== undefined) { this.knexQuery.leftOuterJoin(table, first, this.transformRaw(operator)); } else { this.knexQuery.leftOuterJoin(table, this.transformRaw(first)); } return this; } /** * Add a right join clause */ rightJoin(table, first, operator, second) { if (second !== undefined) { this.knexQuery.rightJoin(table, first, operator, this.transformRaw(second)); } else if (operator !== undefined) { this.knexQuery.rightJoin(table, first, this.transformRaw(operator)); } else { this.knexQuery.rightJoin(table, this.transformRaw(first)); } return this; } /** * Add a right outer join clause */ rightOuterJoin(table, first, operator, second) { if (second !== undefined) { this.knexQuery.rightOuterJoin(table, first, operator, this.transformRaw(second)); } else if (operator !== undefined) { this.knexQuery.rightOuterJoin(table, first, this.transformRaw(operator)); } else { this.knexQuery.rightOuterJoin(table, this.transformRaw(first)); } return this; } /** * Add a full outer join clause */ fullOuterJoin(table, first, operator, second) { if (second !== undefined) { this.knexQuery.fullOuterJoin(table, first, operator, this.transformRaw(second)); } else if (operator !== undefined) { this.knexQuery.fullOuterJoin(table, first, this.transformRaw(operator)); } else { this.knexQuery.fullOuterJoin(table, this.transformRaw(first)); } return this; } /** * Add a cross join clause */ crossJoin(table, first, operator, second) { if (second !== undefined) { this.knexQuery.crossJoin(table, first, operator, this.transformRaw(second)); } else if (operator !== undefined) { this.knexQuery.crossJoin(table, first, this.transformRaw(operator)); } else { this.knexQuery.crossJoin(table, this.transformRaw(first)); } return this; } /** * Add join clause as a raw query */ joinRaw(sql, bindings) { if (bindings) { this.knexQuery.joinRaw(sql, bindings); } else { this.knexQuery.joinRaw(this.transformRaw(sql)); } return this; } /** * Adds a having clause. The having clause breaks for `postgreSQL` when * referencing alias columns, since PG doesn't support alias columns * being referred within `having` clause. The end user has to * use raw queries in this case. */ having(key, operator, value) { if (value !== undefined) { this.knexQuery.having(this.resolveColumn(key), operator, this.transformValue(value)); return this; } if (operator !== undefined) { throw new Exception('Invalid arguments for "queryBuilder.having". Excepts a callback or key-value pair along with an operator'); } this.knexQuery.having(this.transformValue(key)); return this; } /** * Adds or having clause. The having clause breaks for `postgreSQL` when * referencing alias columns, since PG doesn't support alias columns * being referred within `having` clause. The end user has to * use raw queries in this case. */ orHaving(key, operator, value) { if (value !== undefined) { this.knexQuery.orHaving(this.resolveColumn(key), operator, this.transformValue(value)); return this; } if (operator !== undefined) { throw new Exception('Invalid arguments for "queryBuilder.orHaving". Excepts a callback or key-value pair along with an operator'); } this.knexQuery.orHaving(this.transformValue(key)); return this; } /** * Alias for [[having]] */ andHaving(key, operator, value) { return this.having(key, operator, value); } /** * Adding having in clause to the query */ havingIn(key, value) { value = Array.isArray(value) ? value.map((one) => this.transformValue(one)) : this.transformValue(value); this.knexQuery.havingIn(this.resolveColumn(key), value); return this; } /** * Adding or having in clause to the query */ orHavingIn(key, value) { value = Array.isArray(value) ? value.map((one) => this.transformValue(one)) : this.transformValue(value); this.knexQuery['orHavingIn'](this.resolveColumn(key), value); return this; } /** * Alias for [[havingIn]] */ andHavingIn(key, value) { return this.havingIn(key, value); } /** * Adding having not in clause to the query */ havingNotIn(key, value) { value = Array.isArray(value) ? value.map((one) => this.transformValue(one)) : this.transformValue(value); this.knexQuery['havingNotIn'](this.resolveColumn(key), value); return this; } /** * Adding or having not in clause to the query */ orHavingNotIn(key, value) { value = Array.isArray(value) ? value.map((one) => this.transformValue(one)) : this.transformValue(value); this.knexQuery['orHavingNotIn'](this.resolveColumn(key), value); return this; } /** * Alias for [[havingNotIn]] */ andHavingNotIn(key, value) { return this.havingNotIn(key, value); } /** * Adding having null clause */ havingNull(key) { this.knexQuery['havingNull'](this.resolveColumn(key)); return this; } /** * Adding or having null clause */ orHavingNull(key) { ; this.knexQuery['orHavingNull'](this.resolveColumn(key)); return this; } /** * Alias for [[havingNull]] clause */ andHavingNull(key) { return this.havingNull(key); } /** * Adding having not null clause */ havingNotNull(key) { this.knexQuery['havingNotNull'](this.resolveColumn(key)); return this; } /** * Adding or having not null clause */ orHavingNotNull(key) { ; this.knexQuery['orHavingNotNull'](this.resolveColumn(key)); return this; } /** * Alias for [[havingNotNull]] clause */ andHavingNotNull(key) { return this.havingNotNull(key); } /** * Adding `having exists` clause */ havingExists(value) { ; this.knexQuery['havingExists'](this.transformValue(value)); return this; } /** * Adding `or having exists` clause */ orHavingExists(value) { ; this.knexQuery['orHavingExists'](this.transformValue(value)); return this; } /** * Alias for [[havingExists]] */ andHavingExists(value) { return this.havingExists(value); } /** * Adding `having not exists` clause */ havingNotExists(value) { ; this.knexQuery['havingNotExists'](this.transformValue(value)); return this; } /** * Adding `or having not exists` clause */ orHavingNotExists(value) { ; this.knexQuery['orHavingNotExists'](this.transformValue(value)); return this; } /** * Alias for [[havingNotExists]] */ andHavingNotExists(value) { return this.havingNotExists(value); } /** * Adding `having between` clause */ havingBetween(key, value) { this.knexQuery.havingBetween(this.resolveColumn(key), this.getBetweenPair(value)); return this; } /** * Adding `or having between` clause */ orHavingBetween(key, value) { this.knexQuery.orHavingBetween(this.resolveColumn(key), this.getBetweenPair(value)); return this; } /** * Alias for [[havingBetween]] */ andHavingBetween(key, value) { return this.havingBetween(this.resolveColumn(key), value); } /** * Adding `having not between` clause */ havingNotBetween(key, value) { this.knexQuery.havingNotBetween(this.resolveColumn(key), this.getBetweenPair(value)); return this; } /** * Adding `or having not between` clause */ orHavingNotBetween(key, value) { this.knexQuery.orHavingNotBetween(this.resolveColumn(key), this.getBetweenPair(value)); return this; } /** * Alias for [[havingNotBetween]] */ andHavingNotBetween(key, value) { return this.havingNotBetween(key, value); } /** * Adding a where clause using raw sql */ havingRaw(sql, bindings) { if (bindings) { this.knexQuery.havingRaw(sql, bindings); } else { this.knexQuery.havingRaw(this.transformRaw(sql)); } return this; } /** * Adding a where clause using raw sql */ orHavingRaw(sql, bindings) { if (bindings) { this.knexQuery.orHavingRaw(sql, bindings); } else { this.knexQuery.orHavingRaw(this.transformRaw(sql)); } return this; } /** * Alias for [[havingRaw]] */ andHavingRaw(sql, bindings) { return this.havingRaw(sql, bindings); } /** * Add distinct clause */ distinct(...columns) { this.knexQuery.distinct(...columns.map((column) => this.resolveKey(column))); return this; } /** * Add distinctOn clause */ distinctOn(...columns) { this.knexQuery.distinctOn(...columns.map((column) => this.resolveKey(column))); return this; } /** * Add group by clause */ groupBy(...columns) { this.hasGroupBy = true; this.knexQuery.groupBy(...columns.map((column) => this.resolveKey(column))); return this; } /** * Add group by clause as a raw query */ groupByRaw(sql, bindings) { this.hasGroupBy = true; if (bindings) { this.knexQuery.groupByRaw(sql, bindings); } else { this.knexQuery.groupByRaw(this.transformRaw(sql)); } return this; } /** * Add order by clause */ orderBy(column, direction) { if (typeof column === 'string') { this.knexQuery.orderBy(this.resolveKey(column), direction); return this; } /** * Here value can be one of the following * ['age', 'name'] * [{ column: 'age', direction: 'desc' }] * * [{ column: Database.query().from('user_logins'), direction: 'desc' }] */ if (Array.isArray(column)) { const transformedColumns = column.map((col) => { if (typeof col === 'string') { return { column: this.resolveKey(col) }; } if (col.column) { col.column = typeof col.column === 'string' ? this.resolveKey(col.column) : this.transformValue(col.column); return col; } return col; }); this.knexQuery.orderBy(transformedColumns); return this; } this.knexQuery.orderBy(this.transformValue(column), direction); return this; } /** * Add order by clause as a raw query */ orderByRaw(sql, bindings) { if (bindings) { this.knexQuery.orderByRaw(sql, bindings); } else { this.knexQuery.orderByRaw(this.transformRaw(sql)); } return this; } /** * Define select offset */ offset(value) { this.knexQuery.offset(value); return this; } /** * Define results limit */ limit(value) { this.knexQuery.limit(value); return this; } /** * Define union queries */ union(queries, wrap) { this.hasUnion = true; queries = Array.isArray(queries) ? queries.map((one) => this.transformValue(one)) : this.transformValue(queries); wrap !== undefined ? this.knexQuery.union(queries, wrap) : this.knexQuery.union(queries); return this; } /** * Define union all queries */ unionAll(queries, wrap) { this.hasUnion = true; queries = Array.isArray(queries) ? queries.map((one) => this.transformValue(one)) : this.transformValue(queries); wrap !== undefined ? this.knexQuery.unionAll(queries, wrap) : this.knexQuery.unionAll(queries); return this; } /** * Define intersect queries */ intersect(queries, wrap) { queries = Array.isArray(queries) ? queries.map((one) => this.transformValue(one)) : this.transformValue(queries); wrap !== undefined ? this.knexQuery.intersect(queries, wrap) : this.knexQuery.intersect(queries); return this; } /** * Clear select columns */ clearSelect() { this.knexQuery.clearSelect(); return this; } /** * Clear where clauses */ clearWhere() { this.whereStack = [[]]; this.knexQuery.clearWhere(); return this; } /** * Clear order by */ clearOrder() { this.knexQuery.clearOrder(); return this; } /** * Clear having */ clearHaving() { this.knexQuery.clearHaving(); return this; } /** * Clear limit */ clearLimit() { ; this.knexQuery['_single'].limit = null; return this; } /** * Clear offset */ clearOffset() { ; this.knexQuery['_single'].offset = null; return this; } /** * Specify `FOR UPDATE` lock mode for a given * query */ forUpdate(...tableNames) { this.knexQuery.forUpdate(...tableNames); return this; } /** * Specify `FOR SHARE` lock mode for a given * query */ forShare(...tableNames) { this.knexQuery.forShare(...tableNames); return this; } /** * Skip locked rows */ skipLocked() { this.knexQuery.skipLocked(); return this; } /** * Fail when query wants a locked row */ noWait() { this.knexQuery.noWait(); return this; } /** * Define `with` CTE */ with(alias, query, columns = []) { if (columns.length > 0) { this.knexQuery.with(alias, columns, this.transformValue(query)); } else { this.knexQuery.with(alias, this.transformValue(query)); } return this; } /** * Define `with` CTE with recursive keyword */ withRecursive(alias, query, columns = []) { if (columns.length > 0) { this.knexQuery.withRecursive(alias, columns, this.transformValue(query)); } else { this.knexQuery.withRecursive(alias, this.transformValue(query)); } return this; } /** * Define `with materialized` CTE */ withMaterialized(alias, query, columns = []) { if (columns.length > 0) { this.knexQuery.withMaterialized(alias, columns, this.transformValue(query)); } else { this.knexQuery.withMaterialized(alias, this.transformValue(query)); } return this; } /** * Define not `with materialized` CTE */ withNotMaterialized(alias, query, columns = []) { if (columns.length > 0) { this.knexQuery.withNotMaterialized(alias, columns, this.transformValue(query)); } else { this.knexQuery.withNotMaterialized(alias, this.transformValue(query)); } return this; } /** * Define schema for the table */ withSchema(schema) { this.knexQuery.withSchema(schema); return this; } /** * Define table alias */ as(alias) { this.subQueryAlias = alias; this.knexQuery.as(alias); return this; } /** * Count rows for the current query */ count(columns, alias) { this.hasAggregates = true; this.knexQuery.count(this.normalizeAggregateColumns(c