@adonisjs/lucid
Version:
SQL ORM built on top of Active Record pattern
714 lines (713 loc) • 23.6 kB
TypeScript
import { Knex } from 'knex';
import { DialectContract, QueryClientContract, TransactionClientContract } from './database.js';
/**
* Extracted from ts-essentials
*/
export type Dictionary<T, K extends string | number = string> = {
[key in K]: T;
};
/**
* Get one or many of a generic
*/
export type OneOrMany<T> = T | T[];
/**
* Allowing a generic value along with raw query instance or a subquery
* instance
*/
export type ValueWithSubQueries<T> = T | ChainableContract | RawQuery;
/**
* Acceptable raw queries
*/
export type RawQuery = RawBuilderContract | RawQueryBuilderContract | Knex.Raw | Knex.RawQueryBuilder;
/**
* A known set of values allowed when defining values for different
* clauses
*/
export type StrictValues = string | number | boolean | Date | Array<string> | Array<number> | Array<Date> | Array<boolean> | Buffer | RawQuery | ReferenceBuilderContract;
/**
* Strict set of allowed values except the raw queries
*/
export type StrictValuesWithoutRaw = Exclude<StrictValues, RawQuery>;
/**
* Shape of raw query bindings
*/
export type RawQueryBindings = {
[key: string]: StrictValues;
} | StrictValues[];
/**
* A builder method to allow raw queries. However, the return type is the
* instance of current query builder. This is used for `.{verb}Raw` methods.
*/
export interface RawQueryFn<Builder extends ChainableContract> {
(sql: string | RawQuery): Builder;
(sql: string, bindings: RawQueryBindings): Builder;
}
/**
* Query callback is used to write wrapped queries. We get rid of `this` from
* knex, since it makes everything confusing.
*/
export type QueryCallback<Builder extends ChainableContract> = (builder: Builder) => void;
/**
* Shape of the function accepted by the chainable query builder to
* pass lucid query builder to wrapped callbacks like
* `.where(function () {})`.
*
* - This method will accept the wrapped callback
* - Return a new method, that is accepted by Knex.
* - When knex calls that method, this method will invoke the user wrapped
* callback, but instead of passing the knex query builder, it will
* pass the appropriate lucid query builder.
*/
export type DBQueryCallback = (userFn: QueryCallback<ChainableContract>, keysResolver?: (columnName: string) => string) => (builder: Knex.QueryBuilder) => void;
/**
* Possible signatures for a select method on database query builder.
*/
export interface DatabaseQueryBuilderSelect<Builder extends ChainableContract> {
/**
* Selecting columns as a dictionary with key as the alias and value is
* the original column.
*/
(columns: Dictionary<string, string>): Builder;
/**
* An array of values with subqueries
*/
(columns: ValueWithSubQueries<string | number>[]): Builder;
/**
* A spread of array arguments
*/
(...columns: ValueWithSubQueries<string | number>[]): Builder;
/**
* Wildcard selector.
*/
(column: '*'): Builder;
}
/**
* Possible signatures for adding a where clause
*/
export interface Where<Builder extends ChainableContract> {
/**
* Callback for wrapped clauses
*/
(callback: QueryCallback<Builder>): Builder;
/**
* Passing an object of named key-value pair
*/
(clause: Dictionary<any, string>): Builder;
/**
* Key-value pair. The value can also be a subquery
*/
(key: string | RawQuery, value: StrictValues | ChainableContract): Builder;
(key: string | RawQuery, operator: string, value: StrictValues | ChainableContract): Builder;
}
/**
* Possible signatures for adding a whereLike clause
*/
export interface WhereLike<Builder extends ChainableContract> {
/**
* Key-value pair. The value can also be a subquery
*/
(key: string, value: StrictValues | ChainableContract): Builder;
}
/**
* Possible signatures for adding a whereLike clause
*/
export interface WhereJson<Builder extends ChainableContract> {
/**
* Key-value pair. The value can also be a subquery
*/
(column: string, value: Record<string, any> | ChainableContract | QueryCallback<Builder>): Builder;
}
export interface WhereJsonPath<Builder extends ChainableContract> {
(column: string, jsonPath: string, value: string | number | boolean | string[] | number[] | boolean[] | Record<string, any> | ChainableContract | QueryCallback<Builder>): Builder;
(column: string, jsonPath: string, operator: string, value: string | number | boolean | string[] | number[] | boolean[] | Record<string, any> | ChainableContract | QueryCallback<Builder>): Builder;
}
/**
* Possible signatures for adding a where column clause
*/
export interface WhereColumn<Builder extends ChainableContract> {
/**
* Key-value pair.
*/
(column: string | RawQuery, comparisonColumn: string): Builder;
(column: string | RawQuery, operator: string, comparisonColumn: string): Builder;
}
/**
* Possible signatures for adding where in clause.
*/
export interface WhereIn<Builder extends ChainableContract> {
/**
* Column name and array of values
*/
(K: string | RawQuery, value: StrictValues[]): Builder;
/**
* Column names and array of values as an 2d array
*/
(K: string[], value: StrictValues[][]): Builder;
(k: string | RawQuery, callback: QueryCallback<Builder>): Builder;
/**
* Column name with a subquery for a callback that yields an array of
* results
*/
(k: string | RawQuery, subquery: ChainableContract | RawBuilderContract | RawQuery): Builder;
/**
* Column names along with a subquery that yields an array
*/
(k: string[], subquery: ChainableContract | RawBuilderContract | RawQuery): Builder;
}
/**
* Possible signatures for adding whereNull clause.
*/
export interface WhereNull<Builder extends ChainableContract> {
(key: string | RawQuery): Builder;
}
/**
* Possibles signatures for adding a where exists clause
*/
export interface WhereExists<Builder extends ChainableContract> {
(callback: QueryCallback<Builder>): Builder;
(callback: ChainableContract | RawBuilderContract | RawQuery): Builder;
}
/**
* Possibles signatures for adding a where between clause
*/
export interface WhereBetween<Builder extends ChainableContract> {
/**
* Accept any string as a key for supporting prefix columns
*/
(key: string | RawQuery, value: [StrictValues | ChainableContract, StrictValues | ChainableContract]): Builder;
}
/**
* Possible signatures for join query
*/
export interface Join<Builder extends ChainableContract> {
/**
* Defining the join table with primary and secondary columns
* to match
*/
(table: string, primaryColumn: string, secondaryColumn: string): Builder;
/**
* Defining the join table with primary and secondary columns
* to match, where secondary column is output of a raw query
*/
(table: string, primaryColumn: string, raw: RawQuery): Builder;
/**
* Defining the join table with primary and secondary columns
* to match with a custom operator
*/
(table: string, primaryColumn: string, operator: string, secondaryColumn: string): Builder;
/**
* Join with a callback. The callback receives an array of join class from
* knex directly.
*/
(table: string, callback: Knex.JoinCallback): Builder;
}
/**
* Possible signatures for a distinct clause
*/
export interface Distinct<Builder extends ChainableContract> {
(columns: string[]): Builder;
(...columns: string[]): Builder;
(column: '*'): Builder;
}
/**
* The signatures are same as the `distinct` method. For subqueries and
* raw queries, one must use `groupByRaw`.
*/
export interface GroupBy<Builder extends ChainableContract> extends Distinct<Builder> {
}
/**
* Possible signatures for aggregate functions. Aggregates will push to the
* result set. Unlike knex, we force defining aliases for each aggregate.
*/
export interface Aggregate<Builder extends ChainableContract> {
/**
* Accepting column with the alias for the count.
*/
(column: OneOrMany<ValueWithSubQueries<string>>, alias?: string): Builder;
/**
* Accepting an object for multiple counts in a single query.
*/
(columns: Dictionary<OneOrMany<ValueWithSubQueries<string>>, string>): Builder;
}
/**
* Possible signatures for orderBy method.
*/
export interface OrderBy<Builder extends ChainableContract> {
/**
* Order by a column and optional direction
*/
(column: string | ChainableContract | RawBuilderContract | RawQuery, direction?: 'asc' | 'desc'): Builder;
/**
* Order by multiple columns in default direction
*/
(columns: string[]): Builder;
/**
* Order by multiple columns and custom direction for each of them
*/
(columns: {
column: string | ChainableContract | RawBuilderContract | RawQuery;
order?: 'asc' | 'desc';
nulls?: 'first' | 'last';
}[]): Builder;
}
/**
* Possible signatures for a union clause
*/
export interface Union<Builder extends ChainableContract> {
(callback: OneOrMany<QueryCallback<Builder>>, wrap?: boolean): Builder;
(subquery: OneOrMany<ChainableContract | RawQuery>, wrap?: boolean): Builder;
}
/**
* Same signature as union
*/
interface UnionAll<Builder extends ChainableContract> extends Union<Builder> {
}
/**
* Same signature as union
*/
export interface Intersect<Builder extends ChainableContract> extends Union<Builder> {
}
/**
* Possible signatures for having clause
*/
export interface Having<Builder extends ChainableContract> {
/**
* A subquery callback
*/
(callback: QueryCallback<Builder>): Builder;
(callback: RawBuilderContract | RawQuery): Builder;
/**
* Key operator and value. Value can be a subquery as well
*/
(key: string | RawQuery, operator: string, value: StrictValues | ChainableContract | RawBuilderContract | RawQuery): Builder;
}
/**
* Possible signatures for `having in` clause.
*/
export interface HavingIn<Builder extends ChainableContract> {
/**
* Key and an array of literal values, raw queries or
* subqueries.
*/
(key: string | RawQuery, value: (StrictValues | ChainableContract | RawBuilderContract | RawQuery)[]): Builder;
/**
* Key, along with a query callback
*/
(key: string | RawQuery, callback: QueryCallback<Builder>): Builder;
}
/**
* Possible signatures for `having null` clause
*/
export interface HavingNull<Builder extends ChainableContract> extends WhereNull<Builder> {
}
/**
* Possible signatures for `having exists` clause
*/
export interface HavingExists<Builder extends ChainableContract> {
/**
* A query callback or a sub query
*/
(callback: QueryCallback<Builder> | ChainableContract): Builder;
}
/**
* Possible signatures for having between
*/
export interface HavingBetween<Builder extends ChainableContract> extends WhereBetween<Builder> {
}
/**
* Possible signatures of `with` CTE
*/
export interface With<Builder extends ChainableContract> {
(alias: string, callback: QueryCallback<Builder>, columns?: string[]): Builder;
(alias: string, query: RawQuery | ChainableContract, columns?: string[]): Builder;
}
/**
* Possible signatures for defining table for a select query.
*/
export interface FromTable<Builder extends ChainableContract> {
(table: string | Dictionary<string, string> | QueryCallback<Builder> | ChainableContract): Builder;
}
/**
* Possible signatures for the `returning` method.
*/
export interface Returning<Builder> {
(column: OneOrMany<string>): Builder;
}
/**
* Possible signatures for performing an update
*/
export interface Update<Builder extends ChainableContract> {
/**
* Accepts an array of object of named key/value pair and returns an array
* of Generic return columns.
*/
(values: Dictionary<any, string>, returning?: OneOrMany<string>): Builder;
/**
* Accepts a key-value pair to update.
*/
(column: string, value: any, returning?: OneOrMany<string>): Builder;
}
/**
* Possible signatures for incrementing/decrementing
* values
*/
export interface Counter<Builder extends ChainableContract> {
(column: string, counter?: number): Builder;
(values: Dictionary<number, string>): Builder;
}
/**
* Possible signatures for an insert query
*/
export interface Insert<Builder extends InsertQueryBuilderContract> {
(values: Dictionary<any, string>): Builder;
}
/**
* Possible signatures for doing multiple inserts in a single query
*/
export interface MultiInsert<Builder extends InsertQueryBuilderContract> {
(values: Dictionary<any, string>[]): Builder;
}
/**
* The chainable contract has all the methods that can be chained
* to build a query. This interface will never have any
* methods to execute a query.
*/
export interface ChainableContract {
knexQuery: Knex.QueryBuilder;
columns: (string | Knex.QueryBuilder | Knex.RawQueryBuilder)[];
subQueryAlias?: string;
hasAggregates: boolean;
hasGroupBy: boolean;
hasUnion: boolean;
keysResolver?: (columnName: string) => string;
from: FromTable<this>;
select: DatabaseQueryBuilderSelect<this>;
wrapExisting(): this;
where: Where<this>;
orWhere: Where<this>;
andWhere: Where<this>;
whereNot: Where<this>;
orWhereNot: Where<this>;
andWhereNot: Where<this>;
whereColumn: WhereColumn<this>;
orWhereColumn: WhereColumn<this>;
andWhereColumn: WhereColumn<this>;
whereNotColumn: WhereColumn<this>;
orWhereNotColumn: WhereColumn<this>;
andWhereNotColumn: WhereColumn<this>;
whereIn: WhereIn<this>;
orWhereIn: WhereIn<this>;
andWhereIn: WhereIn<this>;
whereNotIn: WhereIn<this>;
orWhereNotIn: WhereIn<this>;
andWhereNotIn: WhereIn<this>;
whereNull: WhereNull<this>;
orWhereNull: WhereNull<this>;
andWhereNull: WhereNull<this>;
whereNotNull: WhereNull<this>;
orWhereNotNull: WhereNull<this>;
andWhereNotNull: WhereNull<this>;
whereExists: WhereExists<this>;
orWhereExists: WhereExists<this>;
andWhereExists: WhereExists<this>;
whereNotExists: WhereExists<this>;
orWhereNotExists: WhereExists<this>;
andWhereNotExists: WhereExists<this>;
whereBetween: WhereBetween<this>;
orWhereBetween: WhereBetween<this>;
andWhereBetween: WhereBetween<this>;
whereNotBetween: WhereBetween<this>;
orWhereNotBetween: WhereBetween<this>;
andWhereNotBetween: WhereBetween<this>;
whereRaw: RawQueryFn<this>;
orWhereRaw: RawQueryFn<this>;
andWhereRaw: RawQueryFn<this>;
whereLike: WhereLike<this>;
orWhereLike: WhereLike<this>;
andWhereLike: WhereLike<this>;
whereILike: WhereLike<this>;
orWhereILike: WhereLike<this>;
andWhereILike: WhereLike<this>;
whereJson: WhereJson<this>;
orWhereJson: WhereJson<this>;
andWhereJson: WhereJson<this>;
whereNotJson: WhereJson<this>;
orWhereNotJson: WhereJson<this>;
andWhereNotJson: WhereJson<this>;
whereJsonSuperset: WhereJson<this>;
orWhereJsonSuperset: WhereJson<this>;
andWhereJsonSuperset: WhereJson<this>;
whereNotJsonSuperset: WhereJson<this>;
orWhereNotJsonSuperset: WhereJson<this>;
andWhereNotJsonSuperset: WhereJson<this>;
whereJsonSubset: WhereJson<this>;
orWhereJsonSubset: WhereJson<this>;
andWhereJsonSubset: WhereJson<this>;
whereNotJsonSubset: WhereJson<this>;
orWhereNotJsonSubset: WhereJson<this>;
andWhereNotJsonSubset: WhereJson<this>;
whereJsonPath: WhereJsonPath<this>;
orWhereJsonPath: WhereJsonPath<this>;
andWhereJsonPath: WhereJsonPath<this>;
join: Join<this>;
innerJoin: Join<this>;
leftJoin: Join<this>;
leftOuterJoin: Join<this>;
rightJoin: Join<this>;
rightOuterJoin: Join<this>;
fullOuterJoin: Join<this>;
crossJoin: Join<this>;
joinRaw: RawQueryFn<this>;
having: Having<this>;
orHaving: Having<this>;
andHaving: Having<this>;
havingIn: HavingIn<this>;
orHavingIn: HavingIn<this>;
andHavingIn: HavingIn<this>;
havingNotIn: HavingIn<this>;
orHavingNotIn: HavingIn<this>;
andHavingNotIn: HavingIn<this>;
havingNull: HavingNull<this>;
orHavingNull: HavingNull<this>;
andHavingNull: HavingNull<this>;
havingNotNull: HavingNull<this>;
orHavingNotNull: HavingNull<this>;
andHavingNotNull: HavingNull<this>;
havingExists: HavingExists<this>;
orHavingExists: HavingExists<this>;
andHavingExists: HavingExists<this>;
havingNotExists: HavingExists<this>;
orHavingNotExists: HavingExists<this>;
andHavingNotExists: HavingExists<this>;
havingBetween: HavingBetween<this>;
orHavingBetween: HavingBetween<this>;
andHavingBetween: HavingBetween<this>;
havingNotBetween: HavingBetween<this>;
orHavingNotBetween: HavingBetween<this>;
andHavingNotBetween: HavingBetween<this>;
havingRaw: RawQueryFn<this>;
orHavingRaw: RawQueryFn<this>;
andHavingRaw: RawQueryFn<this>;
distinct: Distinct<this>;
distinctOn: Distinct<this>;
groupBy: GroupBy<this>;
groupByRaw: RawQueryFn<this>;
orderBy: OrderBy<this>;
orderByRaw: RawQueryFn<this>;
union: Union<this>;
unionAll: UnionAll<this>;
intersect: Intersect<this>;
with: With<this>;
withRecursive: With<this>;
withMaterialized: With<this>;
withNotMaterialized: With<this>;
withSchema(schema: string): this;
as(name: string): this;
offset(offset: number): this;
limit(limit: number): this;
clearSelect(): this;
clearWhere(): this;
clearOrder(): this;
clearHaving(): this;
clearLimit(): this;
clearOffset(): this;
forUpdate(...tableNames: string[]): this;
forShare(...tableNames: string[]): this;
skipLocked(): this;
noWait(): this;
/**
* Executes the callback when condition is truthy
*/
if(condition: any, matchCallback: (query: this) => any, noMatchCallback?: (query: this) => any): this;
/**
* Executes the callback when condition is falsy
*/
unless(condition: any, matchCallback: (query: this) => any, noMatchCallback?: (query: this) => any): this;
/**
* Write blocks to match from
*/
match(...blocks: ([condition: any, callback: (query: this) => any] | ((query: this) => any))[]): this;
}
/**
* Shape of the raw query that can also be passed as a value to
* other queries
*/
export interface RawQueryBuilderContract<Result = any> extends ExcutableQueryBuilderContract<Result> {
knexQuery: Knex.Raw;
client: QueryClientContract;
wrap(before: string, after: string): this;
}
/**
* Reference builder
*/
export interface ReferenceBuilderContract {
withSchema(name: string): this;
as(name: string): this;
toKnex(client: Knex.Client): Knex.Ref<string, any>;
}
/**
* Static raw builder
*/
export interface RawBuilderContract {
wrap(before: string, after: string): this;
toKnex(client: Knex.Client): Knex.Raw;
}
/**
* The keys for the simple paginator meta
* data
*/
export type SimplePaginatorMetaKeys = {
total: string;
perPage: string;
currentPage: string;
lastPage: string;
firstPage: string;
firstPageUrl: string;
lastPageUrl: string;
nextPageUrl: string;
previousPageUrl: string;
};
/**
* Shape of the simple paginator that works with offset and limit
*/
export interface SimplePaginatorContract<Result> extends Array<Result> {
all(): Result[];
readonly firstPage: number;
readonly perPage: number;
readonly currentPage: number;
readonly lastPage: number;
readonly hasPages: boolean;
readonly hasMorePages: boolean;
readonly isEmpty: boolean;
readonly total: number;
readonly hasTotal: boolean;
namingStrategy: {
paginationMetaKeys(): SimplePaginatorMetaKeys;
};
baseUrl(url: string): this;
queryString(values: {
[key: string]: any;
}): this;
getUrl(page: number): string;
getMeta(): any;
getNextPageUrl(): string | null;
getPreviousPageUrl(): string | null;
getUrlsForRange(start: number, end: number): {
url: string;
page: number;
isActive: boolean;
}[];
toJSON(): {
meta: any;
data: Result[];
};
}
/**
* Database query builder exposes the API to construct SQL query using fluent
* chainable API
*/
export interface DatabaseQueryBuilderContract<Result = Dictionary<any, string>> extends ChainableContract, ExcutableQueryBuilderContract<Result[]> {
client: QueryClientContract;
returning: Returning<this>;
/**
* Clone current query
*/
clone<ClonedResult = Result>(): DatabaseQueryBuilderContract<ClonedResult>;
/**
* Execute and get first result
*/
first(): Promise<Result | null>;
/**
* Execute and get first result or fail
*/
firstOrFail(): Promise<Result>;
/**
* Perform delete operation
*/
del(returning?: OneOrMany<string>): this;
delete(returning?: OneOrMany<string>): this;
/**
* A shorthand to define limit and offset based upon the
* current page
*/
forPage(page: number, perPage?: number): this;
/**
* Execute query with pagination
*/
paginate(page: number, perPage?: number): Promise<SimplePaginatorContract<Result>>;
/**
* Mutations (update and increment can be one query aswell)
*/
update: Update<this>;
increment: Counter<this>;
decrement: Counter<this>;
/**
* Aggregates
*/
count: Aggregate<this>;
countDistinct: Aggregate<this>;
min: Aggregate<this>;
max: Aggregate<this>;
sum: Aggregate<this>;
sumDistinct: Aggregate<this>;
avg: Aggregate<this>;
avgDistinct: Aggregate<this>;
/**
* Executes the callback when dialect matches one of the mentioned
* dialects
*/
ifDialect(dialect: DialectContract['name'] | DialectContract['name'][], matchCallback: (query: this) => any, noMatchCallback?: (query: this) => any): this;
/**
* Executes the callback when dialect matches doesn't all the mentioned
* dialects
*/
unlessDialect(dialect: DialectContract['name'] | DialectContract['name'][], matchCallback: (query: this) => any, noMatchCallback?: (query: this) => any): this;
}
/**
* Insert query builder to perform database inserts.
*/
export interface InsertQueryBuilderContract<Result = any> extends ExcutableQueryBuilderContract<Result> {
knexQuery: Knex.QueryBuilder;
client: QueryClientContract;
/**
* Table for the insert query
*/
table(table: string): this;
withSchema(schema: string): this;
/**
* Define returning columns
*/
returning: Returning<this>;
/**
* Inserting a single record.
*/
insert: Insert<this>;
/**
* Inserting multiple columns at once
*/
multiInsert: MultiInsert<this>;
}
/**
* A executable query builder will always have these methods on it.
*/
export interface ExcutableQueryBuilderContract<Result> extends Promise<Result> {
debug(debug: boolean): this;
timeout(time: number, options?: {
cancel: boolean;
}): this;
/**
* @deprecated
* Do not use this method. Instead create a query with options.client
*
* ```ts
* Model.query({ client: trx })
* Database.query({ client: trx })
* ```
*/
useTransaction(trx: TransactionClientContract): this;
reporterData(data: any): this;
toQuery(): string;
exec(): Promise<Result>;
toSQL(): Knex.Sql;
}
export {};