@athenna/database
Advanced tools
| /** | ||
| * @athenna/database | ||
| * | ||
| * (c) João Lenon <lenon@athenna.io> | ||
| * | ||
| * For the full copyright and license information, please view the LICENSE | ||
| * file that was distributed with this source code. | ||
| */ | ||
| import { type PaginatedResponse, type PaginationOptions } from '@athenna/common'; | ||
| import type { Knex } from 'knex'; | ||
| import { Driver } from '#src/database/drivers/Driver'; | ||
| import { Transaction } from '#src/database/transactions/Transaction'; | ||
| import type { ConnectionOptions, Direction, Operations } from '#src/types'; | ||
| export declare class BaseKnexDriver extends Driver<Knex, Knex.QueryBuilder> { | ||
| /** | ||
| * Connect to database. | ||
| */ | ||
| connect(options?: ConnectionOptions): void; | ||
| /** | ||
| * Close the connection with database in this instance. | ||
| */ | ||
| close(): Promise<void>; | ||
| /** | ||
| * Creates a new instance of query builder. | ||
| */ | ||
| query(): Knex.QueryBuilder; | ||
| /** | ||
| * Sync a model schema with database. | ||
| */ | ||
| sync(): Promise<void>; | ||
| /** | ||
| * Create a new transaction. | ||
| */ | ||
| startTransaction(): Promise<Transaction<Knex.Transaction, Knex.QueryBuilder>>; | ||
| /** | ||
| * Commit the transaction. | ||
| */ | ||
| commitTransaction(): Promise<void>; | ||
| /** | ||
| * Rollback the transaction. | ||
| */ | ||
| rollbackTransaction(): Promise<void>; | ||
| /** | ||
| * Run database migrations. | ||
| */ | ||
| runMigrations(): Promise<void>; | ||
| /** | ||
| * Revert database migrations. | ||
| */ | ||
| revertMigrations(): Promise<void>; | ||
| /** | ||
| * List all databases available. | ||
| */ | ||
| getDatabases(): Promise<string[]>; | ||
| /** | ||
| * Get the current database name. | ||
| */ | ||
| getCurrentDatabase(): Promise<string | undefined>; | ||
| /** | ||
| * Verify if database exists. | ||
| */ | ||
| hasDatabase(database: string): Promise<boolean>; | ||
| /** | ||
| * Create a new database. | ||
| */ | ||
| createDatabase(database: string): Promise<void>; | ||
| /** | ||
| * Drop some database. | ||
| */ | ||
| dropDatabase(database: string): Promise<void>; | ||
| /** | ||
| * List all tables available. | ||
| */ | ||
| getTables(): Promise<string[]>; | ||
| /** | ||
| * Verify if table exists. | ||
| */ | ||
| hasTable(table: string): Promise<boolean>; | ||
| /** | ||
| * Create a new table in database. | ||
| */ | ||
| createTable(table: string, closure: (builder: Knex.TableBuilder) => void | Promise<void>): Promise<void>; | ||
| /** | ||
| * Alter a table in database. | ||
| */ | ||
| alterTable(table: string, closure: (builder: Knex.TableBuilder) => void | Promise<void>): Promise<void>; | ||
| /** | ||
| * Drop a table in database. | ||
| */ | ||
| dropTable(table: string): Promise<void>; | ||
| /** | ||
| * Remove all data inside some database table | ||
| * and restart the identity of the table. | ||
| */ | ||
| truncate(table: string): Promise<void>; | ||
| /** | ||
| * Make a raw query in database. | ||
| */ | ||
| raw<T = any>(sql: string, bindings?: any): T; | ||
| /** | ||
| * Calculate the average of a given column. | ||
| */ | ||
| avg(column: string): Promise<string>; | ||
| /** | ||
| * Calculate the average of a given column using distinct. | ||
| */ | ||
| avgDistinct(column: string): Promise<string>; | ||
| /** | ||
| * Get the max number of a given column. | ||
| */ | ||
| max(column: string): Promise<string>; | ||
| /** | ||
| * Get the min number of a given column. | ||
| */ | ||
| min(column: string): Promise<string>; | ||
| /** | ||
| * Sum all numbers of a given column. | ||
| */ | ||
| sum(column: string): Promise<string>; | ||
| /** | ||
| * Sum all numbers of a given column in distinct mode. | ||
| */ | ||
| sumDistinct(column: string): Promise<string>; | ||
| /** | ||
| * Increment a value of a given column. | ||
| */ | ||
| increment(column: string): Promise<void>; | ||
| /** | ||
| * Decrement a value of a given column. | ||
| */ | ||
| decrement(column: string): Promise<void>; | ||
| /** | ||
| * Calculate the average of a given column using distinct. | ||
| */ | ||
| count(column?: string): Promise<number>; | ||
| /** | ||
| * Calculate the average of a given column using distinct. | ||
| */ | ||
| countDistinct(column: string): Promise<number>; | ||
| /** | ||
| * Find a value in database. | ||
| */ | ||
| find<T = any>(): Promise<T>; | ||
| /** | ||
| * Find many values in database. | ||
| */ | ||
| findMany<T = any>(): Promise<T[]>; | ||
| /** | ||
| * Find many values in database and return as paginated response. | ||
| */ | ||
| paginate<T = any>(page?: PaginationOptions | number, limit?: number, resourceUrl?: string): Promise<PaginatedResponse<T>>; | ||
| /** | ||
| * Create a value in database. | ||
| */ | ||
| create<T = any>(data?: Partial<T>): Promise<T>; | ||
| /** | ||
| * Create many values in database. | ||
| */ | ||
| createMany<T = any>(data?: Partial<T>[]): Promise<T[]>; | ||
| /** | ||
| * Create data or update if already exists. | ||
| */ | ||
| createOrUpdate<T = any>(data: Partial<T>): Promise<T>; | ||
| /** | ||
| * Update a value in database. | ||
| */ | ||
| update<T = any>(data: Partial<T>): Promise<T | T[]>; | ||
| /** | ||
| * Stringify object-like values before persisting with Knex. | ||
| */ | ||
| protected prepareInsert<T = any>(data: Partial<T>): Partial<T>; | ||
| /** | ||
| * Verify if a value should be serialized before persisting. | ||
| */ | ||
| private shouldStringifyJsonValue; | ||
| /** | ||
| * Delete one value in database. | ||
| */ | ||
| delete(): Promise<void>; | ||
| /** | ||
| * Set the table that this query will be executed. | ||
| */ | ||
| table(table: string): this; | ||
| /** | ||
| * Log in console the actual query built. | ||
| */ | ||
| dump(): this; | ||
| /** | ||
| * Set the columns that should be selected on query. | ||
| */ | ||
| select(...columns: string[]): this; | ||
| /** | ||
| * Set the columns that should be selected on query raw. | ||
| */ | ||
| selectRaw(sql: string, bindings?: any): this; | ||
| /** | ||
| * Set the table that should be used on query. | ||
| * Different from `table()` method, this method | ||
| * doesn't change the driver table. | ||
| */ | ||
| from(table: string): this; | ||
| /** | ||
| * Set the table that should be used on query raw. | ||
| * Different from `table()` method, this method | ||
| * doesn't change the driver table. | ||
| */ | ||
| fromRaw(sql: string, bindings?: any): this; | ||
| /** | ||
| * Set a join statement in your query. | ||
| */ | ||
| join(table: any, column1?: any, operation?: any | Operations, column2?: any): this; | ||
| /** | ||
| * Set a left join statement in your query. | ||
| */ | ||
| leftJoin(table: any, column1?: any, operation?: any | Operations, column2?: any): this; | ||
| /** | ||
| * Set a right join statement in your query. | ||
| */ | ||
| rightJoin(table: any, column1?: any, operation?: any | Operations, column2?: any): this; | ||
| /** | ||
| * Set a cross join statement in your query. | ||
| */ | ||
| crossJoin(table: any, column1?: any, operation?: any | Operations, column2?: any): this; | ||
| /** | ||
| * Set a full outer join statement in your query. | ||
| */ | ||
| fullOuterJoin(table: any, column1?: any, operation?: any | Operations, column2?: any): this; | ||
| /** | ||
| * Set a left outer join statement in your query. | ||
| */ | ||
| leftOuterJoin(table: any, column1?: any, operation?: any | Operations, column2?: any): this; | ||
| /** | ||
| * Set a right outer join statement in your query. | ||
| */ | ||
| rightOuterJoin(table: any, column1?: any, operation?: any | Operations, column2?: any): this; | ||
| /** | ||
| * Set a join raw statement in your query. | ||
| */ | ||
| joinRaw(sql: string, bindings?: any): this; | ||
| /** | ||
| * Set a group by statement in your query. | ||
| */ | ||
| groupBy(...columns: string[]): this; | ||
| /** | ||
| * Set a group by raw statement in your query. | ||
| */ | ||
| groupByRaw(sql: string, bindings?: any): this; | ||
| having(column: string): this; | ||
| having(column: string, value: any): this; | ||
| having(column: string, operation: Operations, value: any): this; | ||
| /** | ||
| * Set a having raw statement in your query. | ||
| */ | ||
| havingRaw(sql: string, bindings?: any): this; | ||
| /** | ||
| * Set a having in statement in your query. | ||
| */ | ||
| havingIn(column: string, values: any[]): this; | ||
| /** | ||
| * Set a having not in statement in your query. | ||
| */ | ||
| havingNotIn(column: string, values: any[]): this; | ||
| /** | ||
| * Set a having between statement in your query. | ||
| */ | ||
| havingBetween(column: string, values: [any, any]): this; | ||
| /** | ||
| * Set a having not between statement in your query. | ||
| */ | ||
| havingNotBetween(column: string, values: [any, any]): this; | ||
| /** | ||
| * Set a having null statement in your query. | ||
| */ | ||
| havingNull(column: string): this; | ||
| /** | ||
| * Set a having not null statement in your query. | ||
| */ | ||
| havingNotNull(column: string): this; | ||
| orHaving(column: string): this; | ||
| orHaving(column: string, value: any): this; | ||
| orHaving(column: string, operation: Operations, value: any): this; | ||
| /** | ||
| * Set an or having raw statement in your query. | ||
| */ | ||
| orHavingRaw(sql: string, bindings?: any): this; | ||
| /** | ||
| * Set an or having not in statement in your query. | ||
| */ | ||
| orHavingNotIn(column: string, values: any[]): this; | ||
| /** | ||
| * Set an or having between statement in your query. | ||
| */ | ||
| orHavingBetween(column: string, values: [any, any]): this; | ||
| /** | ||
| * Set an or having not between statement in your query. | ||
| */ | ||
| orHavingNotBetween(column: string, values: [any, any]): this; | ||
| /** | ||
| * Set an or having null statement in your query. | ||
| */ | ||
| orHavingNull(column: string): this; | ||
| /** | ||
| * Set an or having not null statement in your query. | ||
| */ | ||
| orHavingNotNull(column: string): this; | ||
| where(statement: Record<string, any>): this; | ||
| where(key: string, value: any): this; | ||
| where(key: string, operation: Operations, value: any): this; | ||
| whereNot(statement: Record<string, any>): this; | ||
| whereNot(key: string, value: any): this; | ||
| /** | ||
| * Set a where raw statement in your query. | ||
| */ | ||
| whereRaw(sql: string, bindings?: any): this; | ||
| /** | ||
| * Set a where exists statement in your query. | ||
| */ | ||
| whereExists(closure: (query: BaseKnexDriver) => void): this; | ||
| /** | ||
| * Set a where not exists statement in your query. | ||
| */ | ||
| whereNotExists(closure: (query: BaseKnexDriver) => void): this; | ||
| /** | ||
| * Set a where like statement in your query. | ||
| */ | ||
| whereLike(column: string, value: any): this; | ||
| /** | ||
| * Set a where ILike statement in your query. | ||
| */ | ||
| whereILike(column: string, value: any): this; | ||
| /** | ||
| * Set a where in statement in your query. | ||
| */ | ||
| whereIn(column: string, values: any[]): this; | ||
| /** | ||
| * Set a where not in statement in your query. | ||
| */ | ||
| whereNotIn(column: string, values: any[]): this; | ||
| /** | ||
| * Set a where between statement in your query. | ||
| */ | ||
| whereBetween(column: string, values: [any, any]): this; | ||
| /** | ||
| * Set a where not between statement in your query. | ||
| */ | ||
| whereNotBetween(column: string, values: [any, any]): this; | ||
| /** | ||
| * Set a where null statement in your query. | ||
| */ | ||
| whereNull(column: string): this; | ||
| /** | ||
| * Set a where not null statement in your query. | ||
| */ | ||
| whereNotNull(column: string): this; | ||
| whereJson(column: string, value: any): this; | ||
| whereJson(column: string, operation: Operations, value: any): this; | ||
| orWhere(statement: Record<string, any>): this; | ||
| orWhere(key: string, value: any): this; | ||
| orWhere(key: string, operation: Operations, value: any): this; | ||
| orWhereNot(statement: Record<string, any>): this; | ||
| orWhereNot(key: string, value: any): this; | ||
| /** | ||
| * Set a or where raw statement in your query. | ||
| */ | ||
| orWhereRaw(sql: string, bindings?: any): this; | ||
| /** | ||
| * Set an or where exists statement in your query. | ||
| */ | ||
| orWhereExists(closure: (query: BaseKnexDriver) => void): this; | ||
| /** | ||
| * Set an or where not exists statement in your query. | ||
| */ | ||
| orWhereNotExists(closure: (query: BaseKnexDriver) => void): this; | ||
| /** | ||
| * Set an or where like statement in your query. | ||
| */ | ||
| orWhereLike(column: string, value: any): this; | ||
| /** | ||
| * Set an or where ILike statement in your query. | ||
| */ | ||
| orWhereILike(column: string, value: any): this; | ||
| /** | ||
| * Set an or where in statement in your query. | ||
| */ | ||
| orWhereIn(column: string, values: any[]): this; | ||
| /** | ||
| * Set an or where not in statement in your query. | ||
| */ | ||
| orWhereNotIn(column: string, values: any[]): this; | ||
| /** | ||
| * Set an or where between statement in your query. | ||
| */ | ||
| orWhereBetween(column: string, values: [any, any]): this; | ||
| /** | ||
| * Set an or where not between statement in your query. | ||
| */ | ||
| orWhereNotBetween(column: string, values: [any, any]): this; | ||
| /** | ||
| * Set an or where null statement in your query. | ||
| */ | ||
| orWhereNull(column: string): this; | ||
| /** | ||
| * Set an or where not null statement in your query. | ||
| */ | ||
| orWhereNotNull(column: string): this; | ||
| orWhereJson(column: string, value: any): this; | ||
| orWhereJson(column: string, operation: Operations, value: any): this; | ||
| /** | ||
| * Convert a json selector path to a valid json path. | ||
| */ | ||
| private parseJsonSelectorToPath; | ||
| /** | ||
| * Set an order by statement in your query. | ||
| */ | ||
| orderBy(column: string, direction?: Direction): this; | ||
| /** | ||
| * Set an order by raw statement in your query. | ||
| */ | ||
| orderByRaw(sql: string, bindings?: any): this; | ||
| /** | ||
| * Order the results easily by the latest date. By default, the result will | ||
| * be ordered by the table's "createdAt" column. | ||
| */ | ||
| latest(column?: string): this; | ||
| /** | ||
| * Order the results easily by the oldest date. By default, the result will | ||
| * be ordered by the table's "createdAt" column. | ||
| */ | ||
| oldest(column?: string): this; | ||
| /** | ||
| * Set the skip number in your query. | ||
| */ | ||
| offset(number: number): this; | ||
| /** | ||
| * Set the limit number in your query. | ||
| */ | ||
| limit(number: number): this; | ||
| } |
| /* eslint-disable @typescript-eslint/ban-ts-comment */ | ||
| /** | ||
| * @athenna/database | ||
| * | ||
| * (c) João Lenon <lenon@athenna.io> | ||
| * | ||
| * For the full copyright and license information, please view the LICENSE | ||
| * file that was distributed with this source code. | ||
| */ | ||
| import { Exec, Is, Json, Options } from '@athenna/common'; | ||
| import { debug } from '#src/debug'; | ||
| import { Log } from '@athenna/logger'; | ||
| import { Driver } from '#src/database/drivers/Driver'; | ||
| import { ConnectionFactory } from '#src/factories/ConnectionFactory'; | ||
| import { Transaction } from '#src/database/transactions/Transaction'; | ||
| import { MigrationSource } from '#src/database/migrations/MigrationSource'; | ||
| import { EmptyValueException } from '#src/exceptions/EmptyValueException'; | ||
| import { EmptyColumnException } from '#src/exceptions/EmptyColumnException'; | ||
| import { WrongMethodException } from '#src/exceptions/WrongMethodException'; | ||
| import { PROTECTED_QUERY_METHODS } from '#src/constants/ProtectedQueryMethods'; | ||
| import { NotConnectedDatabaseException } from '#src/exceptions/NotConnectedDatabaseException'; | ||
| export class BaseKnexDriver extends Driver { | ||
| /** | ||
| * Connect to database. | ||
| */ | ||
| connect(options = {}) { | ||
| options = Options.create(options, { | ||
| force: false, | ||
| saveOnFactory: true, | ||
| connect: true | ||
| }); | ||
| if (!options.connect) { | ||
| return; | ||
| } | ||
| if (this.isConnected && !options.force) { | ||
| return; | ||
| } | ||
| const knex = this.getKnex(); | ||
| const configs = Config.get(`database.connections.${this.connection}`, {}); | ||
| const knexOpts = { | ||
| migrations: { | ||
| tableName: 'migrations' | ||
| }, | ||
| pool: { | ||
| min: 2, | ||
| max: 20, | ||
| acquireTimeoutMillis: 60 * 1000 | ||
| }, | ||
| debug: false, | ||
| useNullAsDefault: false, | ||
| ...Json.omit(configs, ['driver', 'validations']) | ||
| }; | ||
| debug('creating new connection using Knex. options defined: %o', knexOpts); | ||
| if (Config.is('rc.bootLogs', true)) { | ||
| Log.channelOrVanilla('application').success(`Successfully connected to ({yellow} ${this.connection}) database connection`); | ||
| } | ||
| this.client = knex.default(knexOpts); | ||
| this.isConnected = true; | ||
| this.isSavedOnFactory = options.saveOnFactory; | ||
| if (this.isSavedOnFactory) { | ||
| ConnectionFactory.setClient(this.connection, this.client); | ||
| } | ||
| this.qb = this.query(); | ||
| } | ||
| /** | ||
| * Close the connection with database in this instance. | ||
| */ | ||
| async close() { | ||
| if (!this.isConnected) { | ||
| return; | ||
| } | ||
| await this.client.destroy(); | ||
| this.qb = null; | ||
| this.tableName = null; | ||
| this.client = null; | ||
| this.isConnected = false; | ||
| ConnectionFactory.setClient(this.connection, null); | ||
| } | ||
| /** | ||
| * Creates a new instance of query builder. | ||
| */ | ||
| query() { | ||
| if (!this.isConnected) { | ||
| throw new NotConnectedDatabaseException(); | ||
| } | ||
| const query = this.useSetQB | ||
| ? this.qb.table(this.tableName) | ||
| : this.client.queryBuilder().table(this.tableName); | ||
| const handler = { | ||
| get: (target, propertyKey) => { | ||
| if (PROTECTED_QUERY_METHODS.includes(propertyKey)) { | ||
| this.qb = this.query(); | ||
| } | ||
| return target[propertyKey]; | ||
| } | ||
| }; | ||
| return new Proxy(query, handler); | ||
| } | ||
| /** | ||
| * Sync a model schema with database. | ||
| */ | ||
| async sync() { | ||
| debug(`database sync with ${this.constructor.name} is not available yet, use migration instead.`); | ||
| } | ||
| /** | ||
| * Create a new transaction. | ||
| */ | ||
| async startTransaction() { | ||
| const trx = await this.client.transaction(); | ||
| return new Transaction(this.clone().setClient(trx)); | ||
| } | ||
| /** | ||
| * Commit the transaction. | ||
| */ | ||
| async commitTransaction() { | ||
| const client = this.client; | ||
| await client.commit(); | ||
| this.tableName = null; | ||
| this.client = null; | ||
| this.isConnected = false; | ||
| } | ||
| /** | ||
| * Rollback the transaction. | ||
| */ | ||
| async rollbackTransaction() { | ||
| const client = this.client; | ||
| await client.rollback(); | ||
| this.tableName = null; | ||
| this.client = null; | ||
| this.isConnected = false; | ||
| } | ||
| /** | ||
| * Run database migrations. | ||
| */ | ||
| async runMigrations() { | ||
| await this.client.migrate.latest({ | ||
| migrationSource: new MigrationSource(this.connection) | ||
| }); | ||
| } | ||
| /** | ||
| * Revert database migrations. | ||
| */ | ||
| async revertMigrations() { | ||
| await this.client.migrate.rollback({ | ||
| migrationSource: new MigrationSource(this.connection) | ||
| }); | ||
| } | ||
| /** | ||
| * List all databases available. | ||
| */ | ||
| async getDatabases() { | ||
| const [databases] = await this.raw('SHOW DATABASES'); | ||
| return databases.map(database => database.Database); | ||
| } | ||
| /** | ||
| * Get the current database name. | ||
| */ | ||
| async getCurrentDatabase() { | ||
| return this.client.client.database(); | ||
| } | ||
| /** | ||
| * Verify if database exists. | ||
| */ | ||
| async hasDatabase(database) { | ||
| const databases = await this.getDatabases(); | ||
| return databases.includes(database); | ||
| } | ||
| /** | ||
| * Create a new database. | ||
| */ | ||
| async createDatabase(database) { | ||
| await this.raw('CREATE DATABASE IF NOT EXISTS ??', database); | ||
| } | ||
| /** | ||
| * Drop some database. | ||
| */ | ||
| async dropDatabase(database) { | ||
| await this.raw('DROP DATABASE IF EXISTS ??', database); | ||
| } | ||
| /** | ||
| * List all tables available. | ||
| */ | ||
| async getTables() { | ||
| const [tables] = await this.raw('SELECT table_name FROM information_schema.tables WHERE table_schema = ?', await this.getCurrentDatabase()); | ||
| return tables.map(table => table.TABLE_NAME); | ||
| } | ||
| /** | ||
| * Verify if table exists. | ||
| */ | ||
| async hasTable(table) { | ||
| return this.client.schema.hasTable(table); | ||
| } | ||
| /** | ||
| * Create a new table in database. | ||
| */ | ||
| async createTable(table, closure) { | ||
| await this.client.schema.createTable(table, closure); | ||
| } | ||
| /** | ||
| * Alter a table in database. | ||
| */ | ||
| async alterTable(table, closure) { | ||
| await this.client.schema.alterTable(table, closure); | ||
| } | ||
| /** | ||
| * Drop a table in database. | ||
| */ | ||
| async dropTable(table) { | ||
| await this.client.schema.dropTableIfExists(table); | ||
| } | ||
| /** | ||
| * Remove all data inside some database table | ||
| * and restart the identity of the table. | ||
| */ | ||
| async truncate(table) { | ||
| try { | ||
| await this.raw('SET FOREIGN_KEY_CHECKS = 0'); | ||
| await this.raw('TRUNCATE TABLE ??', table); | ||
| } | ||
| finally { | ||
| await this.raw('SET FOREIGN_KEY_CHECKS = 1'); | ||
| } | ||
| } | ||
| /** | ||
| * Make a raw query in database. | ||
| */ | ||
| raw(sql, bindings) { | ||
| return this.client.raw(sql, bindings); | ||
| } | ||
| /** | ||
| * Calculate the average of a given column. | ||
| */ | ||
| async avg(column) { | ||
| const [{ avg }] = await this.qb.avg({ avg: column }); | ||
| return avg; | ||
| } | ||
| /** | ||
| * Calculate the average of a given column using distinct. | ||
| */ | ||
| async avgDistinct(column) { | ||
| const [{ avg }] = await this.qb.avgDistinct({ avg: column }); | ||
| return avg; | ||
| } | ||
| /** | ||
| * Get the max number of a given column. | ||
| */ | ||
| async max(column) { | ||
| const [{ max }] = await this.qb.max({ max: column }); | ||
| return max; | ||
| } | ||
| /** | ||
| * Get the min number of a given column. | ||
| */ | ||
| async min(column) { | ||
| const [{ min }] = await this.qb.min({ min: column }); | ||
| return min; | ||
| } | ||
| /** | ||
| * Sum all numbers of a given column. | ||
| */ | ||
| async sum(column) { | ||
| const [{ sum }] = await this.qb.sum({ sum: column }); | ||
| return sum; | ||
| } | ||
| /** | ||
| * Sum all numbers of a given column in distinct mode. | ||
| */ | ||
| async sumDistinct(column) { | ||
| const [{ sum }] = await this.qb.sumDistinct({ sum: column }); | ||
| return sum; | ||
| } | ||
| /** | ||
| * Increment a value of a given column. | ||
| */ | ||
| async increment(column) { | ||
| await this.qb.increment(column); | ||
| } | ||
| /** | ||
| * Decrement a value of a given column. | ||
| */ | ||
| async decrement(column) { | ||
| await this.qb.decrement(column); | ||
| } | ||
| /** | ||
| * Calculate the average of a given column using distinct. | ||
| */ | ||
| async count(column = '*') { | ||
| const [{ count }] = await this.qb.count({ count: column }); | ||
| return Number(count); | ||
| } | ||
| /** | ||
| * Calculate the average of a given column using distinct. | ||
| */ | ||
| async countDistinct(column) { | ||
| const [{ count }] = await this.qb.countDistinct({ count: column }); | ||
| return Number(count); | ||
| } | ||
| /** | ||
| * Find a value in database. | ||
| */ | ||
| async find() { | ||
| return this.qb.first(); | ||
| } | ||
| /** | ||
| * Find many values in database. | ||
| */ | ||
| async findMany() { | ||
| const data = await this.qb; | ||
| this.qb = this.query(); | ||
| return data; | ||
| } | ||
| /** | ||
| * Find many values in database and return as paginated response. | ||
| */ | ||
| async paginate(page = { page: 0, limit: 10, resourceUrl: '/' }, limit = 10, resourceUrl = '/') { | ||
| if (Is.Number(page)) { | ||
| page = { page, limit, resourceUrl }; | ||
| } | ||
| const [{ count }] = await this.qb | ||
| .clone() | ||
| .clearOrder() | ||
| .clearSelect() | ||
| .count({ count: '*' }); | ||
| const data = await this.offset(page.page * page.limit) | ||
| .limit(page.limit) | ||
| .findMany(); | ||
| return Exec.pagination(data, Number(count), page); | ||
| } | ||
| /** | ||
| * Create a value in database. | ||
| */ | ||
| async create(data = {}) { | ||
| if (Is.Array(data)) { | ||
| throw new WrongMethodException('create', 'createMany'); | ||
| } | ||
| const created = await this.createMany([data]); | ||
| return created[0]; | ||
| } | ||
| /** | ||
| * Create many values in database. | ||
| */ | ||
| async createMany(data = []) { | ||
| if (!Is.Array(data)) { | ||
| throw new WrongMethodException('createMany', 'create'); | ||
| } | ||
| const preparedData = data.map(data => this.prepareInsert(data)); | ||
| const ids = []; | ||
| const promises = preparedData.map((prepared, index) => { | ||
| return this.qb | ||
| .clone() | ||
| .insert(prepared) | ||
| .then(([id]) => ids.push(data[index][this.primaryKey] || id)); | ||
| }); | ||
| await Promise.all(promises); | ||
| return this.whereIn(this.primaryKey, ids).findMany(); | ||
| } | ||
| /** | ||
| * Create data or update if already exists. | ||
| */ | ||
| async createOrUpdate(data) { | ||
| const query = this.qb.clone(); | ||
| const hasValue = await query.first(); | ||
| const preparedData = this.prepareInsert(data); | ||
| if (hasValue) { | ||
| await this.qb | ||
| .where(this.primaryKey, hasValue[this.primaryKey]) | ||
| .limit(1) | ||
| .update(preparedData); | ||
| return this.where(this.primaryKey, hasValue[this.primaryKey]).find(); | ||
| } | ||
| return this.create(data); | ||
| } | ||
| /** | ||
| * Update a value in database. | ||
| */ | ||
| async update(data) { | ||
| const preparedData = this.prepareInsert(data); | ||
| await this.qb.clone().update(preparedData); | ||
| const result = await this.findMany(); | ||
| if (result.length === 1) { | ||
| return result[0]; | ||
| } | ||
| return result; | ||
| } | ||
| /** | ||
| * Stringify object-like values before persisting with Knex. | ||
| */ | ||
| prepareInsert(data) { | ||
| return Object.entries(data).reduce((prepared, [key, value]) => { | ||
| if (!this.shouldStringifyJsonValue(value)) { | ||
| prepared[key] = value; | ||
| return prepared; | ||
| } | ||
| prepared[key] = JSON.stringify(value); | ||
| return prepared; | ||
| }, {}); | ||
| } | ||
| /** | ||
| * Verify if a value should be serialized before persisting. | ||
| */ | ||
| shouldStringifyJsonValue(value) { | ||
| return !!value && (Is.Array(value) || Is.Object(value)); | ||
| } | ||
| /** | ||
| * Delete one value in database. | ||
| */ | ||
| async delete() { | ||
| await this.qb.delete(); | ||
| } | ||
| /** | ||
| * Set the table that this query will be executed. | ||
| */ | ||
| table(table) { | ||
| if (!this.isConnected) { | ||
| throw new NotConnectedDatabaseException(); | ||
| } | ||
| if (!Is.String(table)) { | ||
| throw new Error('Table must be a string value'); | ||
| } | ||
| this.tableName = table; | ||
| this.qb = this.query(); | ||
| return this; | ||
| } | ||
| /** | ||
| * Log in console the actual query built. | ||
| */ | ||
| dump() { | ||
| process.stdout.write(`${JSON.stringify(this.qb.toSQL().toNative())}\n`); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set the columns that should be selected on query. | ||
| */ | ||
| select(...columns) { | ||
| if (!Is.Array(columns)) { | ||
| throw new EmptyValueException('select'); | ||
| } | ||
| this.qb.select(...columns); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set the columns that should be selected on query raw. | ||
| */ | ||
| selectRaw(sql, bindings) { | ||
| if (Is.Undefined(sql)) { | ||
| throw new EmptyValueException('selectRaw'); | ||
| } | ||
| return this.select(this.raw(sql, bindings)); | ||
| } | ||
| /** | ||
| * Set the table that should be used on query. | ||
| * Different from `table()` method, this method | ||
| * doesn't change the driver table. | ||
| */ | ||
| from(table) { | ||
| if (Is.Undefined(table)) { | ||
| throw new Error('Table must be a string value'); | ||
| } | ||
| this.qb.from(table); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set the table that should be used on query raw. | ||
| * Different from `table()` method, this method | ||
| * doesn't change the driver table. | ||
| */ | ||
| fromRaw(sql, bindings) { | ||
| if (Is.Undefined(sql)) { | ||
| throw new EmptyValueException('fromRaw'); | ||
| } | ||
| return this.from(this.raw(sql, bindings)); | ||
| } | ||
| /** | ||
| * Set a join statement in your query. | ||
| */ | ||
| join(table, column1, operation, column2) { | ||
| return this.joinByType('join', table, column1, operation, column2); | ||
| } | ||
| /** | ||
| * Set a left join statement in your query. | ||
| */ | ||
| leftJoin(table, column1, operation, column2) { | ||
| return this.joinByType('leftJoin', table, column1, operation, column2); | ||
| } | ||
| /** | ||
| * Set a right join statement in your query. | ||
| */ | ||
| rightJoin(table, column1, operation, column2) { | ||
| return this.joinByType('rightJoin', table, column1, operation, column2); | ||
| } | ||
| /** | ||
| * Set a cross join statement in your query. | ||
| */ | ||
| crossJoin(table, column1, operation, column2) { | ||
| return this.joinByType('crossJoin', table, column1, operation, column2); | ||
| } | ||
| /** | ||
| * Set a full outer join statement in your query. | ||
| */ | ||
| fullOuterJoin(table, column1, operation, column2) { | ||
| // TODO https://github.com/knex/knex/issues/3949 | ||
| return this.joinByType('leftJoin', table, column1, operation, column2); | ||
| } | ||
| /** | ||
| * Set a left outer join statement in your query. | ||
| */ | ||
| leftOuterJoin(table, column1, operation, column2) { | ||
| return this.joinByType('leftOuterJoin', table, column1, operation, column2); | ||
| } | ||
| /** | ||
| * Set a right outer join statement in your query. | ||
| */ | ||
| rightOuterJoin(table, column1, operation, column2) { | ||
| return this.joinByType('rightOuterJoin', table, column1, operation, column2); | ||
| } | ||
| /** | ||
| * Set a join raw statement in your query. | ||
| */ | ||
| joinRaw(sql, bindings) { | ||
| if (Is.Undefined(sql)) { | ||
| throw new EmptyValueException('joinRaw'); | ||
| } | ||
| this.qb.joinRaw(sql, bindings); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a group by statement in your query. | ||
| */ | ||
| groupBy(...columns) { | ||
| if (Is.Undefined(columns)) { | ||
| throw new EmptyColumnException('groupBy'); | ||
| } | ||
| this.qb.groupBy(...columns); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a group by raw statement in your query. | ||
| */ | ||
| groupByRaw(sql, bindings) { | ||
| if (Is.Undefined(sql)) { | ||
| throw new EmptyValueException('groupByRaw'); | ||
| } | ||
| this.qb.groupByRaw(sql, bindings); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a having statement in your query. | ||
| */ | ||
| having(column, operation, value) { | ||
| if (Is.Undefined(operation)) { | ||
| if (Is.Undefined(column)) { | ||
| throw new EmptyColumnException('having'); | ||
| } | ||
| this.qb.having(column); | ||
| return this; | ||
| } | ||
| if (Is.Undefined(value)) { | ||
| if (Is.Undefined(column) || !Is.String(column)) { | ||
| throw new EmptyColumnException('having'); | ||
| } | ||
| if (Is.Undefined(operation)) { | ||
| throw new EmptyValueException('having'); | ||
| } | ||
| this.qb.having(column, '=', operation); | ||
| return this; | ||
| } | ||
| if (Is.Undefined(column) || !Is.String(column)) { | ||
| throw new EmptyColumnException('having'); | ||
| } | ||
| if (Is.Undefined(value)) { | ||
| throw new EmptyValueException('having'); | ||
| } | ||
| this.qb.having(column, operation, value); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a having raw statement in your query. | ||
| */ | ||
| havingRaw(sql, bindings) { | ||
| if (Is.Undefined(sql)) { | ||
| throw new EmptyValueException('havingRaw'); | ||
| } | ||
| this.qb.havingRaw(sql, bindings); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a having in statement in your query. | ||
| */ | ||
| havingIn(column, values) { | ||
| if (Is.Undefined(column) || !Is.String(column)) { | ||
| throw new EmptyColumnException('havingIn'); | ||
| } | ||
| if (Is.Undefined(values)) { | ||
| throw new EmptyValueException('havingIn'); | ||
| } | ||
| this.qb.havingIn(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a having not in statement in your query. | ||
| */ | ||
| havingNotIn(column, values) { | ||
| if (Is.Undefined(column) || !Is.String(column)) { | ||
| throw new EmptyColumnException('havingNotIn'); | ||
| } | ||
| if (Is.Undefined(values)) { | ||
| throw new EmptyValueException('havingNotIn'); | ||
| } | ||
| this.qb.havingNotIn(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a having between statement in your query. | ||
| */ | ||
| havingBetween(column, values) { | ||
| if (Is.Undefined(column) || !Is.String(column)) { | ||
| throw new EmptyColumnException('havingBetween'); | ||
| } | ||
| if (Is.Undefined(values?.[0]) || Is.Undefined(values?.[1])) { | ||
| throw new EmptyValueException('havingBetween'); | ||
| } | ||
| this.qb.havingBetween(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a having not between statement in your query. | ||
| */ | ||
| havingNotBetween(column, values) { | ||
| if (Is.Undefined(column) || !Is.String(column)) { | ||
| throw new EmptyColumnException('havingNotBetween'); | ||
| } | ||
| if (Is.Undefined(values?.[0]) || Is.Undefined(values?.[1])) { | ||
| throw new EmptyValueException('havingNotBetween'); | ||
| } | ||
| this.qb.havingNotBetween(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a having null statement in your query. | ||
| */ | ||
| havingNull(column) { | ||
| if (Is.Undefined(column) || !Is.String(column)) { | ||
| throw new EmptyColumnException('havingNull'); | ||
| } | ||
| this.qb.havingNull(column); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a having not null statement in your query. | ||
| */ | ||
| havingNotNull(column) { | ||
| if (Is.Undefined(column) || !Is.String(column)) { | ||
| throw new EmptyColumnException('havingNotNull'); | ||
| } | ||
| this.qb.havingNotNull(column); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or having statement in your query. | ||
| */ | ||
| orHaving(column, operation, value) { | ||
| if (Is.Undefined(operation)) { | ||
| if (Is.Undefined(column)) { | ||
| throw new EmptyColumnException('orHaving'); | ||
| } | ||
| this.qb.orHaving(column); | ||
| return this; | ||
| } | ||
| if (Is.Undefined(value)) { | ||
| if (Is.Undefined(column) || !Is.String(column)) { | ||
| throw new EmptyColumnException('orHaving'); | ||
| } | ||
| if (Is.Undefined(operation)) { | ||
| throw new EmptyValueException('orHaving'); | ||
| } | ||
| this.qb.orHaving(column, '=', operation); | ||
| return this; | ||
| } | ||
| if (Is.Undefined(column) || !Is.String(column)) { | ||
| throw new EmptyColumnException('orHaving'); | ||
| } | ||
| if (Is.Undefined(value)) { | ||
| throw new EmptyValueException('orHaving'); | ||
| } | ||
| this.qb.orHaving(column, operation, value); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or having raw statement in your query. | ||
| */ | ||
| orHavingRaw(sql, bindings) { | ||
| if (Is.Undefined(sql) || !Is.String(sql)) { | ||
| throw new EmptyValueException('orHavingRaw'); | ||
| } | ||
| this.qb.orHavingRaw(sql, bindings); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or having not in statement in your query. | ||
| */ | ||
| orHavingNotIn(column, values) { | ||
| if (Is.Undefined(column) || !Is.String(column)) { | ||
| throw new EmptyColumnException('orHavingNotIn'); | ||
| } | ||
| if (Is.Undefined(values)) { | ||
| throw new EmptyValueException('orHavingNotIn'); | ||
| } | ||
| this.qb.orHavingNotIn(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or having between statement in your query. | ||
| */ | ||
| orHavingBetween(column, values) { | ||
| if (Is.Undefined(column) || !Is.String(column)) { | ||
| throw new EmptyColumnException('orHavingBetween'); | ||
| } | ||
| if (Is.Undefined(values?.[0]) || Is.Undefined(values?.[1])) { | ||
| throw new EmptyValueException('orHavingBetween'); | ||
| } | ||
| this.qb.orHavingBetween(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or having not between statement in your query. | ||
| */ | ||
| orHavingNotBetween(column, values) { | ||
| if (Is.Undefined(column) || !Is.String(column)) { | ||
| throw new EmptyColumnException('orHavingNotBetween'); | ||
| } | ||
| if (Is.Undefined(values?.[0]) || Is.Undefined(values?.[1])) { | ||
| throw new EmptyValueException('orHavingNotBetween'); | ||
| } | ||
| this.qb.orHavingNotBetween(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or having null statement in your query. | ||
| */ | ||
| orHavingNull(column) { | ||
| if (Is.Undefined(column) || !Is.String(column)) { | ||
| throw new EmptyColumnException('orHavingNull'); | ||
| } | ||
| this.qb.orHavingNull(column); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or having not null statement in your query. | ||
| */ | ||
| orHavingNotNull(column) { | ||
| if (Is.Undefined(column) || !Is.String(column)) { | ||
| throw new EmptyColumnException('orHavingNotNull'); | ||
| } | ||
| this.qb.orHavingNotNull(column); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a where statement in your query. | ||
| */ | ||
| where(statement, operation, value) { | ||
| if (Is.Function(statement)) { | ||
| const driver = this.clone(); | ||
| this.qb.where(function () { | ||
| statement(driver.setQueryBuilder(this, { useSetQB: true })); | ||
| }); | ||
| return this; | ||
| } | ||
| if (Is.Undefined(operation)) { | ||
| if (Is.Undefined(statement) || !Is.Object(statement)) { | ||
| throw new EmptyValueException('where'); | ||
| } | ||
| this.qb.where(statement); | ||
| return this; | ||
| } | ||
| if (Is.Undefined(value)) { | ||
| if (Is.Undefined(statement) || !Is.String(statement)) { | ||
| throw new EmptyValueException('where'); | ||
| } | ||
| if (this.isUsingJsonSelector(statement)) { | ||
| this.whereJson(statement, operation); | ||
| return this; | ||
| } | ||
| this.qb.where(statement, operation); | ||
| return this; | ||
| } | ||
| if (Is.Undefined(statement) || !Is.String(statement)) { | ||
| throw new EmptyColumnException('where'); | ||
| } | ||
| if (Is.Undefined(value)) { | ||
| throw new EmptyValueException('where'); | ||
| } | ||
| if (this.isUsingJsonSelector(statement)) { | ||
| this.whereJson(statement, operation, value); | ||
| return this; | ||
| } | ||
| this.qb.where(statement, operation, value); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a where not statement in your query. | ||
| */ | ||
| whereNot(statement, value) { | ||
| if (Is.Function(statement)) { | ||
| const driver = this.clone(); | ||
| this.qb.whereNot(function () { | ||
| statement(driver.setQueryBuilder(this, { useSetQB: true })); | ||
| }); | ||
| return this; | ||
| } | ||
| if (Is.Undefined(value)) { | ||
| if (Is.Undefined(statement) || !Is.Object(statement)) { | ||
| throw new EmptyValueException('whereNot'); | ||
| } | ||
| this.qb.whereNot(statement); | ||
| return this; | ||
| } | ||
| if (Is.Undefined(statement) || !Is.String(statement)) { | ||
| throw new EmptyColumnException('whereNot'); | ||
| } | ||
| if (Is.Undefined(value)) { | ||
| throw new EmptyValueException('whereNot'); | ||
| } | ||
| this.qb.whereNot(statement, value); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a where raw statement in your query. | ||
| */ | ||
| whereRaw(sql, bindings) { | ||
| if (Is.Undefined(sql) || !Is.String(sql)) { | ||
| throw new EmptyValueException('whereRaw'); | ||
| } | ||
| this.qb.whereRaw(sql, bindings); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a where exists statement in your query. | ||
| */ | ||
| whereExists(closure) { | ||
| const driver = this.clone(); | ||
| this.qb.whereExists(function () { | ||
| closure(driver.setQueryBuilder(this, { useSetQB: true })); | ||
| }); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a where not exists statement in your query. | ||
| */ | ||
| whereNotExists(closure) { | ||
| const driver = this.clone(); | ||
| this.qb.whereNotExists(function () { | ||
| closure(driver.setQueryBuilder(this, { useSetQB: true })); | ||
| }); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a where like statement in your query. | ||
| */ | ||
| whereLike(column, value) { | ||
| if (Is.Undefined(column) || !Is.String(column)) { | ||
| throw new EmptyColumnException('whereLike'); | ||
| } | ||
| if (Is.Undefined(value)) { | ||
| throw new EmptyValueException('whereLike'); | ||
| } | ||
| this.qb.whereLike(column, value); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a where ILike statement in your query. | ||
| */ | ||
| whereILike(column, value) { | ||
| if (Is.Undefined(column) || !Is.String(column)) { | ||
| throw new EmptyColumnException('whereILike'); | ||
| } | ||
| if (Is.Undefined(value)) { | ||
| throw new EmptyValueException('whereILike'); | ||
| } | ||
| this.qb.whereILike(column, value); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a where in statement in your query. | ||
| */ | ||
| whereIn(column, values) { | ||
| if (Is.Undefined(column) || !Is.String(column)) { | ||
| throw new EmptyColumnException('whereIn'); | ||
| } | ||
| if (Is.Undefined(values)) { | ||
| throw new EmptyValueException('whereIn'); | ||
| } | ||
| this.qb.whereIn(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a where not in statement in your query. | ||
| */ | ||
| whereNotIn(column, values) { | ||
| if (Is.Undefined(column) || !Is.String(column)) { | ||
| throw new EmptyColumnException('whereNotIn'); | ||
| } | ||
| if (Is.Undefined(values)) { | ||
| throw new EmptyValueException('whereNotIn'); | ||
| } | ||
| this.qb.whereNotIn(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a where between statement in your query. | ||
| */ | ||
| whereBetween(column, values) { | ||
| if (Is.Undefined(column) || !Is.String(column)) { | ||
| throw new EmptyColumnException('whereBetween'); | ||
| } | ||
| if (Is.Undefined(values?.[0]) || Is.Undefined(values?.[1])) { | ||
| throw new EmptyValueException('whereBetween'); | ||
| } | ||
| this.qb.whereBetween(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a where not between statement in your query. | ||
| */ | ||
| whereNotBetween(column, values) { | ||
| if (Is.Undefined(column) || !Is.String(column)) { | ||
| throw new EmptyColumnException('whereNotBetween'); | ||
| } | ||
| if (Is.Undefined(values?.[0]) || Is.Undefined(values?.[1])) { | ||
| throw new EmptyValueException('whereNotBetween'); | ||
| } | ||
| this.qb.whereNotBetween(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a where null statement in your query. | ||
| */ | ||
| whereNull(column) { | ||
| if (Is.Undefined(column) || !Is.String(column)) { | ||
| throw new EmptyColumnException('whereNull'); | ||
| } | ||
| this.qb.whereNull(column); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a where not null statement in your query. | ||
| */ | ||
| whereNotNull(column) { | ||
| if (Is.Undefined(column) || !Is.String(column)) { | ||
| throw new EmptyColumnException('whereNotNull'); | ||
| } | ||
| this.qb.whereNotNull(column); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a where json statement in your query. | ||
| */ | ||
| whereJson(column, operator, value) { | ||
| const parsed = this.parseJsonSelector(column); | ||
| if (!parsed) { | ||
| throw new Error(`Invalid JSON selector: ${column}`); | ||
| } | ||
| const path = this.parseJsonSelectorToPath(parsed.path); | ||
| if (Is.Undefined(value)) { | ||
| this.qb.whereJsonPath(parsed.column, path, '=', operator); | ||
| return this; | ||
| } | ||
| this.qb.whereJsonPath(parsed.column, path, operator, value); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a or where statement in your query. | ||
| */ | ||
| orWhere(statement, operation, value) { | ||
| if (Is.Function(statement)) { | ||
| const driver = this.clone(); | ||
| this.qb.orWhere(function () { | ||
| statement(driver.setQueryBuilder(this, { useSetQB: true })); | ||
| }); | ||
| return this; | ||
| } | ||
| if (Is.Undefined(operation)) { | ||
| if (Is.Undefined(statement) || !Is.Object(statement)) { | ||
| throw new EmptyValueException('orWhere'); | ||
| } | ||
| this.qb.orWhere(statement); | ||
| return this; | ||
| } | ||
| if (Is.Undefined(value)) { | ||
| if (Is.Undefined(statement) || !Is.String(statement)) { | ||
| throw new EmptyColumnException('orWhere'); | ||
| } | ||
| this.qb.orWhere(statement, operation); | ||
| return this; | ||
| } | ||
| if (Is.Undefined(statement) || !Is.String(statement)) { | ||
| throw new EmptyColumnException('orWhere'); | ||
| } | ||
| if (Is.Undefined(value)) { | ||
| throw new EmptyValueException('orWhere'); | ||
| } | ||
| this.qb.orWhere(statement, operation, value); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or where not statement in your query. | ||
| */ | ||
| orWhereNot(statement, value) { | ||
| if (Is.Function(statement)) { | ||
| const driver = this.clone(); | ||
| this.qb.orWhereNot(function () { | ||
| statement(driver.setQueryBuilder(this, { useSetQB: true })); | ||
| }); | ||
| return this; | ||
| } | ||
| if (Is.Undefined(value)) { | ||
| if (Is.Undefined(statement) || !Is.Object(statement)) { | ||
| throw new EmptyValueException('orWhereNot'); | ||
| } | ||
| this.qb.orWhereNot(statement); | ||
| return this; | ||
| } | ||
| if (Is.Undefined(statement) || !Is.String(statement)) { | ||
| throw new EmptyColumnException('orWhereNot'); | ||
| } | ||
| if (Is.Undefined(value)) { | ||
| throw new EmptyValueException('orWhereNot'); | ||
| } | ||
| this.qb.orWhereNot(statement, value); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a or where raw statement in your query. | ||
| */ | ||
| orWhereRaw(sql, bindings) { | ||
| if (Is.Undefined(sql) || !Is.String(sql)) { | ||
| throw new EmptyValueException('orWhereRaw'); | ||
| } | ||
| this.qb.orWhereRaw(sql, bindings); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or where exists statement in your query. | ||
| */ | ||
| orWhereExists(closure) { | ||
| const driver = this.clone(); | ||
| this.qb.orWhereExists(function () { | ||
| closure(driver.setQueryBuilder(this, { useSetQB: true })); | ||
| }); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or where not exists statement in your query. | ||
| */ | ||
| orWhereNotExists(closure) { | ||
| const driver = this.clone(); | ||
| this.qb.orWhereNotExists(function () { | ||
| closure(driver.setQueryBuilder(this, { useSetQB: true })); | ||
| }); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or where like statement in your query. | ||
| */ | ||
| orWhereLike(column, value) { | ||
| if (Is.Undefined(column) || !Is.String(column)) { | ||
| throw new EmptyColumnException('orWhereLike'); | ||
| } | ||
| if (Is.Undefined(value)) { | ||
| throw new EmptyValueException('orWhereLike'); | ||
| } | ||
| this.qb.orWhereLike(column, value); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or where ILike statement in your query. | ||
| */ | ||
| orWhereILike(column, value) { | ||
| if (Is.Undefined(column) || !Is.String(column)) { | ||
| throw new EmptyColumnException('orWhereILike'); | ||
| } | ||
| if (Is.Undefined(value)) { | ||
| throw new EmptyValueException('orWhereILike'); | ||
| } | ||
| this.qb.orWhereILike(column, value); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or where in statement in your query. | ||
| */ | ||
| orWhereIn(column, values) { | ||
| if (Is.Undefined(column) || !Is.String(column)) { | ||
| throw new EmptyColumnException('orWhereIn'); | ||
| } | ||
| if (Is.Undefined(values)) { | ||
| throw new EmptyValueException('orWhereIn'); | ||
| } | ||
| this.qb.orWhereIn(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or where not in statement in your query. | ||
| */ | ||
| orWhereNotIn(column, values) { | ||
| if (Is.Undefined(column) || !Is.String(column)) { | ||
| throw new EmptyColumnException('orWhereNotIn'); | ||
| } | ||
| if (Is.Undefined(values)) { | ||
| throw new EmptyValueException('orWhereNotIn'); | ||
| } | ||
| this.qb.orWhereNotIn(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or where between statement in your query. | ||
| */ | ||
| orWhereBetween(column, values) { | ||
| if (Is.Undefined(column) || !Is.String(column)) { | ||
| throw new EmptyColumnException('orWhereBetween'); | ||
| } | ||
| if (Is.Undefined(values?.[0]) || Is.Undefined(values?.[1])) { | ||
| throw new EmptyValueException('orWhereBetween'); | ||
| } | ||
| this.qb.orWhereBetween(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or where not between statement in your query. | ||
| */ | ||
| orWhereNotBetween(column, values) { | ||
| if (Is.Undefined(column) || !Is.String(column)) { | ||
| throw new EmptyColumnException('orWhereNotBetween'); | ||
| } | ||
| if (Is.Undefined(values?.[0]) || Is.Undefined(values?.[1])) { | ||
| throw new EmptyValueException('orWhereNotBetween'); | ||
| } | ||
| this.qb.orWhereNotBetween(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or where null statement in your query. | ||
| */ | ||
| orWhereNull(column) { | ||
| if (Is.Undefined(column) || !Is.String(column)) { | ||
| throw new EmptyColumnException('orWhereNull'); | ||
| } | ||
| this.qb.orWhereNull(column); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or where not null statement in your query. | ||
| */ | ||
| orWhereNotNull(column) { | ||
| if (Is.Undefined(column) || !Is.String(column)) { | ||
| throw new EmptyColumnException('orWhereNotNull'); | ||
| } | ||
| this.qb.orWhereNotNull(column); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or where json statement in your query. | ||
| */ | ||
| orWhereJson(column, operator, value) { | ||
| if (Is.Undefined(column) || !Is.String(column)) { | ||
| throw new EmptyColumnException('orWhereJson'); | ||
| } | ||
| const parsed = this.parseJsonSelector(column); | ||
| if (!parsed) { | ||
| throw new Error(`Invalid JSON selector: ${column}`); | ||
| } | ||
| const path = this.parseJsonSelectorToPath(parsed.path); | ||
| if (Is.Undefined(value)) { | ||
| this.qb.orWhereJsonPath(parsed.column, path, '=', operator); | ||
| return this; | ||
| } | ||
| if (Is.Undefined(value)) { | ||
| throw new EmptyValueException('orWhereJson'); | ||
| } | ||
| this.qb.orWhereJsonPath(parsed.column, path, operator, value); | ||
| return this; | ||
| } | ||
| /** | ||
| * Convert a json selector path to a valid json path. | ||
| */ | ||
| parseJsonSelectorToPath(path) { | ||
| const parts = path | ||
| .split('->') | ||
| .map(part => part.trim()) | ||
| .filter(Boolean); | ||
| return parts.reduce((jsonPath, part) => { | ||
| if (part === '*') { | ||
| return `${jsonPath}[*]`; | ||
| } | ||
| if (/^\d+$/.test(part)) { | ||
| return `${jsonPath}[${part}]`; | ||
| } | ||
| return `${jsonPath}.${part}`; | ||
| }, '$'); | ||
| } | ||
| /** | ||
| * Set an order by statement in your query. | ||
| */ | ||
| orderBy(column, direction = 'ASC') { | ||
| if (Is.Undefined(column) || !Is.String(column)) { | ||
| throw new EmptyColumnException('orderBy'); | ||
| } | ||
| this.qb.orderBy(column, direction.toUpperCase()); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an order by raw statement in your query. | ||
| */ | ||
| orderByRaw(sql, bindings) { | ||
| if (Is.Undefined(sql) || !Is.String(sql)) { | ||
| throw new EmptyValueException('orderByRaw'); | ||
| } | ||
| this.qb.orderByRaw(sql, bindings); | ||
| return this; | ||
| } | ||
| /** | ||
| * Order the results easily by the latest date. By default, the result will | ||
| * be ordered by the table's "createdAt" column. | ||
| */ | ||
| latest(column = 'createdAt') { | ||
| if (Is.Undefined(column) || !Is.String(column)) { | ||
| throw new EmptyColumnException('latest'); | ||
| } | ||
| return this.orderBy(column, 'DESC'); | ||
| } | ||
| /** | ||
| * Order the results easily by the oldest date. By default, the result will | ||
| * be ordered by the table's "createdAt" column. | ||
| */ | ||
| oldest(column = 'createdAt') { | ||
| if (Is.Undefined(column) || !Is.String(column)) { | ||
| throw new EmptyColumnException('oldest'); | ||
| } | ||
| return this.orderBy(column, 'ASC'); | ||
| } | ||
| /** | ||
| * Set the skip number in your query. | ||
| */ | ||
| offset(number) { | ||
| if (Is.Undefined(number) || !Is.Number(number)) { | ||
| throw new EmptyValueException('offset'); | ||
| } | ||
| this.qb.offset(number); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set the limit number in your query. | ||
| */ | ||
| limit(number) { | ||
| if (Is.Undefined(number) || !Is.Number(number)) { | ||
| throw new EmptyValueException('limit'); | ||
| } | ||
| this.qb.limit(number); | ||
| return this; | ||
| } | ||
| } |
| /** | ||
| * @athenna/database | ||
| * | ||
| * (c) João Lenon <lenon@athenna.io> | ||
| * | ||
| * For the full copyright and license information, please view the LICENSE | ||
| * file that was distributed with this source code. | ||
| */ | ||
| import { Exception } from '@athenna/common'; | ||
| export declare class EmptyColumnException extends Exception { | ||
| constructor(method: string); | ||
| } |
| /** | ||
| * @athenna/database | ||
| * | ||
| * (c) João Lenon <lenon@athenna.io> | ||
| * | ||
| * For the full copyright and license information, please view the LICENSE | ||
| * file that was distributed with this source code. | ||
| */ | ||
| import { Exception } from '@athenna/common'; | ||
| export class EmptyColumnException extends Exception { | ||
| constructor(method) { | ||
| const message = `The column set in your query builder ${method} method is empty or with wrong type.`; | ||
| super({ | ||
| message, | ||
| code: 'E_EMPTY_COLUMN_ERROR', | ||
| help: `You must set a column value to perform the ${method}() operation. Accepted values are only strings and objects.` | ||
| }); | ||
| } | ||
| } |
| /** | ||
| * @athenna/database | ||
| * | ||
| * (c) João Lenon <lenon@athenna.io> | ||
| * | ||
| * For the full copyright and license information, please view the LICENSE | ||
| * file that was distributed with this source code. | ||
| */ | ||
| import { Exception } from '@athenna/common'; | ||
| export declare class EmptyValueException extends Exception { | ||
| constructor(method: string); | ||
| } |
| /** | ||
| * @athenna/database | ||
| * | ||
| * (c) João Lenon <lenon@athenna.io> | ||
| * | ||
| * For the full copyright and license information, please view the LICENSE | ||
| * file that was distributed with this source code. | ||
| */ | ||
| import { Exception } from '@athenna/common'; | ||
| export class EmptyValueException extends Exception { | ||
| constructor(method) { | ||
| const message = `The value set in your query builder ${method}() operation is undefined.`; | ||
| super({ | ||
| message, | ||
| code: 'E_EMPTY_VALUE_ERROR', | ||
| help: `You must set a value to perform the ${method}() operation, undefined is not supported.` | ||
| }); | ||
| } | ||
| } |
+1
-1
| { | ||
| "name": "@athenna/database", | ||
| "version": "5.38.0", | ||
| "version": "5.39.0", | ||
| "description": "The Athenna database handler for SQL/NoSQL.", | ||
@@ -5,0 +5,0 @@ "license": "MIT", |
@@ -86,7 +86,7 @@ /** | ||
| */ | ||
| count(column?: string | ModelColumns<T>): Promise<string>; | ||
| count(column?: string | ModelColumns<T>): Promise<number>; | ||
| /** | ||
| * Calculate the average of a given column using distinct. | ||
| */ | ||
| countDistinct(column: string | ModelColumns<T>): Promise<string>; | ||
| countDistinct(column: string | ModelColumns<T>): Promise<number>; | ||
| /** | ||
@@ -236,10 +236,2 @@ * Find a value in database or throw exception if undefined. | ||
| /** | ||
| * Set a having exists statement in your query. | ||
| */ | ||
| havingExists(closure: (query: Driver) => void): this; | ||
| /** | ||
| * Set a having not exists statement in your query. | ||
| */ | ||
| havingNotExists(closure: (query: Driver) => void): this; | ||
| /** | ||
| * Set a having in statement in your query. | ||
@@ -277,14 +269,2 @@ */ | ||
| /** | ||
| * Set an or having exists statement in your query. | ||
| */ | ||
| orHavingExists(closure: (query: Driver) => void): this; | ||
| /** | ||
| * Set an or having not exists statement in your query. | ||
| */ | ||
| orHavingNotExists(closure: (query: Driver) => void): this; | ||
| /** | ||
| * Set an or having in statement in your query. | ||
| */ | ||
| orHavingIn(column: string | ModelColumns<T>, values: any[]): this; | ||
| /** | ||
| * Set an or having not in statement in your query. | ||
@@ -362,2 +342,4 @@ */ | ||
| whereNotNull(column: string | ModelColumns<T>): this; | ||
| whereJson(column: string | ModelColumns<T>, operation: any, value?: any): this; | ||
| whereJson(column: string | ModelColumns<T>, value: any): this; | ||
| orWhere(statement: (query: this) => void): this; | ||
@@ -414,2 +396,4 @@ orWhere(statement: Partial<T>): this; | ||
| orWhereNotNull(column: string | ModelColumns<T>): this; | ||
| orWhereJson(column: string | ModelColumns<T>, operation: Operations, value?: any): this; | ||
| orWhereJson(column: string | ModelColumns<T>, value: any): this; | ||
| /** | ||
@@ -416,0 +400,0 @@ * Set an order by statement in your query. |
@@ -355,16 +355,2 @@ /** | ||
| /** | ||
| * Set a having exists statement in your query. | ||
| */ | ||
| havingExists(closure) { | ||
| this.driver.havingExists(closure); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a having not exists statement in your query. | ||
| */ | ||
| havingNotExists(closure) { | ||
| this.driver.havingNotExists(closure); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a having in statement in your query. | ||
@@ -426,23 +412,2 @@ */ | ||
| /** | ||
| * Set an or having exists statement in your query. | ||
| */ | ||
| orHavingExists(closure) { | ||
| this.driver.orHavingExists(closure); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or having not exists statement in your query. | ||
| */ | ||
| orHavingNotExists(closure) { | ||
| this.driver.orHavingNotExists(closure); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or having in statement in your query. | ||
| */ | ||
| orHavingIn(column, values) { | ||
| this.driver.orHavingIn(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or having not in statement in your query. | ||
@@ -574,2 +539,9 @@ */ | ||
| /** | ||
| * Set a where json statement in your query. | ||
| */ | ||
| whereJson(column, operation, value) { | ||
| this.driver.whereJson(column, operation, value); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a or where statement in your query. | ||
@@ -666,2 +638,9 @@ */ | ||
| /** | ||
| * Set an orWhereJson statement in your query. | ||
| */ | ||
| orWhereJson(column, operation, value) { | ||
| this.driver.orWhereJson(column, operation, value); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an order by statement in your query. | ||
@@ -668,0 +647,0 @@ */ |
@@ -91,2 +91,13 @@ /** | ||
| /** | ||
| * Verify if a statement is using the json selector syntax. | ||
| */ | ||
| isUsingJsonSelector(statement: string): boolean; | ||
| /** | ||
| * Parse a statement using the json selector syntax. | ||
| */ | ||
| parseJsonSelector(statement: string): { | ||
| column: string; | ||
| path: string; | ||
| }; | ||
| /** | ||
| * Connect to database. | ||
@@ -211,7 +222,7 @@ */ | ||
| */ | ||
| abstract count(column?: string): Promise<string>; | ||
| abstract count(column?: string): Promise<number>; | ||
| /** | ||
| * Calculate the average of a given column using distinct. | ||
| */ | ||
| abstract countDistinct(column?: string): Promise<string>; | ||
| abstract countDistinct(column?: string): Promise<number>; | ||
| /** | ||
@@ -271,3 +282,3 @@ * Find a value in database and return as boolean. | ||
| */ | ||
| abstract createOrUpdate<T = any>(data?: Partial<T>): Promise<T | T[]>; | ||
| abstract createOrUpdate<T = any>(data?: Partial<T>): Promise<T>; | ||
| /** | ||
@@ -362,10 +373,2 @@ * Update a value in database. | ||
| /** | ||
| * Set a having exists statement in your query. | ||
| */ | ||
| abstract havingExists(closure: (query: Driver<Client, QB>) => void): this; | ||
| /** | ||
| * Set a having not exists statement in your query. | ||
| */ | ||
| abstract havingNotExists(closure: (query: Driver<Client, QB>) => void): this; | ||
| /** | ||
| * Set a having in statement in your query. | ||
@@ -403,14 +406,2 @@ */ | ||
| /** | ||
| * Set an or having exists statement in your query. | ||
| */ | ||
| abstract orHavingExists(closure: (query: Driver<Client, QB>) => void): this; | ||
| /** | ||
| * Set an or having not exists statement in your query. | ||
| */ | ||
| abstract orHavingNotExists(closure: (query: Driver<Client, QB>) => void): this; | ||
| /** | ||
| * Set an or having in statement in your query. | ||
| */ | ||
| abstract orHavingIn(column: string, values: any[]): this; | ||
| /** | ||
| * Set an or having not in statement in your query. | ||
@@ -487,3 +478,9 @@ */ | ||
| abstract whereNotNull(column: string): this; | ||
| abstract whereJson(column: string, value: any): this; | ||
| abstract whereJson(column: string, operation: Operations, value?: any): this; | ||
| /** | ||
| * Set a where json statement in your query. | ||
| */ | ||
| abstract whereJson(column: string, operation: Operations, value?: any): this; | ||
| /** | ||
| * Set a or where statement in your query. | ||
@@ -544,3 +541,9 @@ */ | ||
| abstract orWhereNotNull(column: string): this; | ||
| abstract orWhereJson(column: string, value: any): this; | ||
| abstract orWhereJson(column: string, operation: Operations, value?: any): this; | ||
| /** | ||
| * Set an orWhereJson statement in your query. | ||
| */ | ||
| abstract orWhereJson(column: string, operation: Operations, value?: any): this; | ||
| /** | ||
| * Set an order by statement in your query. | ||
@@ -547,0 +550,0 @@ */ |
@@ -9,3 +9,5 @@ /** | ||
| */ | ||
| import { Module, Options, Collection } from '@athenna/common'; | ||
| import { Is, Module, Options, Collection } from '@athenna/common'; | ||
| import { EmptyValueException } from '#src/exceptions/EmptyValueException'; | ||
| import { EmptyColumnException } from '#src/exceptions/EmptyColumnException'; | ||
| import { NotFoundDataException } from '#src/exceptions/NotFoundDataException'; | ||
@@ -117,2 +119,5 @@ export class Driver { | ||
| setPrimaryKey(primaryKey) { | ||
| if (!Is.String(primaryKey)) { | ||
| throw new Error('Primary key must be a string value'); | ||
| } | ||
| this.primaryKey = primaryKey; | ||
@@ -125,2 +130,5 @@ return this; | ||
| joinByType(joinType, table, column1, operation, column2) { | ||
| if (Is.Undefined(table)) { | ||
| throw new Error('Table is required for join methods'); | ||
| } | ||
| if (!column1) { | ||
@@ -131,2 +139,5 @@ this.qb[joinType](table); | ||
| if (!operation) { | ||
| if (Is.Undefined(column1)) { | ||
| throw new EmptyColumnException(joinType); | ||
| } | ||
| this.qb[joinType](table, column1); | ||
@@ -136,5 +147,17 @@ return this; | ||
| if (!column2) { | ||
| if (Is.Undefined(column1) || !Is.String(column1)) { | ||
| throw new EmptyColumnException(joinType); | ||
| } | ||
| if (Is.Undefined(operation) || !Is.String(operation)) { | ||
| throw new EmptyValueException(joinType); | ||
| } | ||
| this.qb[joinType](table, column1, operation); | ||
| return this; | ||
| } | ||
| if (Is.Undefined(column1) || !Is.String(column1)) { | ||
| throw new EmptyColumnException(joinType); | ||
| } | ||
| if (Is.Undefined(column2) || !Is.String(column2)) { | ||
| throw new EmptyColumnException(joinType); | ||
| } | ||
| this.qb[joinType](table, column1, operation, column2); | ||
@@ -144,2 +167,25 @@ return this; | ||
| /** | ||
| * Verify if a statement is using the json selector syntax. | ||
| */ | ||
| isUsingJsonSelector(statement) { | ||
| return Is.String(statement) && statement.includes('->'); | ||
| } | ||
| /** | ||
| * Parse a statement using the json selector syntax. | ||
| */ | ||
| parseJsonSelector(statement) { | ||
| if (!this.isUsingJsonSelector(statement)) { | ||
| return null; | ||
| } | ||
| const [column, ...pathParts] = statement.split('->'); | ||
| const path = pathParts.join('->').trim(); | ||
| if (!column?.trim() || !path) { | ||
| return null; | ||
| } | ||
| return { | ||
| column: column.trim(), | ||
| path | ||
| }; | ||
| } | ||
| /** | ||
| * Find a value in database and return as boolean. | ||
@@ -146,0 +192,0 @@ */ |
@@ -153,7 +153,7 @@ /** | ||
| */ | ||
| static count(): Promise<string>; | ||
| static count(): Promise<number>; | ||
| /** | ||
| * Calculate the average of a given column using distinct. | ||
| */ | ||
| static countDistinct(): Promise<string>; | ||
| static countDistinct(): Promise<number>; | ||
| /** | ||
@@ -220,3 +220,3 @@ * Find value in database but returns only the value of | ||
| */ | ||
| static createOrUpdate<T = any>(data?: Partial<T>): Promise<T | T[]>; | ||
| static createOrUpdate<T = any>(data?: Partial<T>): Promise<T>; | ||
| /** | ||
@@ -306,10 +306,2 @@ * Update a value in database. | ||
| /** | ||
| * Set a having exists statement in your query. | ||
| */ | ||
| static havingExists(): typeof FakeDriver; | ||
| /** | ||
| * Set a having not exists statement in your query. | ||
| */ | ||
| static havingNotExists(): typeof FakeDriver; | ||
| /** | ||
| * Set a having in statement in your query. | ||
@@ -346,14 +338,2 @@ */ | ||
| /** | ||
| * Set an or having exists statement in your query. | ||
| */ | ||
| static orHavingExists(): typeof FakeDriver; | ||
| /** | ||
| * Set an or having not exists statement in your query. | ||
| */ | ||
| static orHavingNotExists(): typeof FakeDriver; | ||
| /** | ||
| * Set an or having in statement in your query. | ||
| */ | ||
| static orHavingIn(): typeof FakeDriver; | ||
| /** | ||
| * Set an or having not in statement in your query. | ||
@@ -427,2 +407,4 @@ */ | ||
| static whereNotNull(): typeof FakeDriver; | ||
| static whereJson(column: string, value: any): typeof FakeDriver; | ||
| static whereJson(column: string, operation: Operations, value: any): typeof FakeDriver; | ||
| static orWhere(statement: Record<string, any>): typeof FakeDriver; | ||
@@ -477,2 +459,4 @@ static orWhere(key: string, value: any): typeof FakeDriver; | ||
| static orWhereNotNull(): typeof FakeDriver; | ||
| static orWhereJson(column: string, value: any): typeof FakeDriver; | ||
| static orWhereJson(column: string, operation: Operations, value: any): typeof FakeDriver; | ||
| /** | ||
@@ -504,2 +488,5 @@ * Set an order by statement in your query. | ||
| static limit(): typeof FakeDriver; | ||
| static isUsingJsonSelector(): boolean; | ||
| static parseJsonSelector(): any; | ||
| static parseJsonSelectorToPath(path: string): string; | ||
| } |
@@ -256,3 +256,3 @@ /** | ||
| static async count() { | ||
| return '1'; | ||
| return 1; | ||
| } | ||
@@ -263,3 +263,3 @@ /** | ||
| static async countDistinct() { | ||
| return '1'; | ||
| return 1; | ||
| } | ||
@@ -506,14 +506,2 @@ /** | ||
| /** | ||
| * Set a having exists statement in your query. | ||
| */ | ||
| static havingExists() { | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a having not exists statement in your query. | ||
| */ | ||
| static havingNotExists() { | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a having in statement in your query. | ||
@@ -567,20 +555,2 @@ */ | ||
| /** | ||
| * Set an or having exists statement in your query. | ||
| */ | ||
| static orHavingExists() { | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or having not exists statement in your query. | ||
| */ | ||
| static orHavingNotExists() { | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or having in statement in your query. | ||
| */ | ||
| static orHavingIn() { | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or having not in statement in your query. | ||
@@ -694,2 +664,8 @@ */ | ||
| /** | ||
| * Set a where json statement in your query. | ||
| */ | ||
| static whereJson() { | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a or where statement in your query. | ||
@@ -773,2 +749,8 @@ */ | ||
| /** | ||
| * Set an or where json statement in your query. | ||
| */ | ||
| static orWhereJson() { | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an order by statement in your query. | ||
@@ -811,2 +793,11 @@ */ | ||
| } | ||
| static isUsingJsonSelector() { | ||
| return false; | ||
| } | ||
| static parseJsonSelector() { | ||
| return null; | ||
| } | ||
| static parseJsonSelectorToPath(path) { | ||
| return path; | ||
| } | ||
| } |
@@ -154,7 +154,7 @@ /** | ||
| */ | ||
| count(column?: string): Promise<string>; | ||
| count(column?: string): Promise<number>; | ||
| /** | ||
| * Calculate the average of a given column using distinct. | ||
| */ | ||
| countDistinct(column: string): Promise<string>; | ||
| countDistinct(column: string): Promise<number>; | ||
| /** | ||
@@ -183,3 +183,3 @@ * Find a value in database. | ||
| */ | ||
| createOrUpdate<T = any>(data?: Partial<T>): Promise<T | T[]>; | ||
| createOrUpdate<T = any>(data?: Partial<T>): Promise<T>; | ||
| /** | ||
@@ -269,10 +269,2 @@ * Update a value in database. | ||
| /** | ||
| * Set a having exists statement in your query. | ||
| */ | ||
| havingExists(): this; | ||
| /** | ||
| * Set a having not exists statement in your query. | ||
| */ | ||
| havingNotExists(): this; | ||
| /** | ||
| * Set a having in statement in your query. | ||
@@ -309,14 +301,2 @@ */ | ||
| /** | ||
| * Set an or having exists statement in your query. | ||
| */ | ||
| orHavingExists(): this; | ||
| /** | ||
| * Set an or having not exists statement in your query. | ||
| */ | ||
| orHavingNotExists(): this; | ||
| /** | ||
| * Set an or having in statement in your query. | ||
| */ | ||
| orHavingIn(column: string, values: any[]): this; | ||
| /** | ||
| * Set an or having not in statement in your query. | ||
@@ -390,2 +370,4 @@ */ | ||
| whereNotNull(column: string): this; | ||
| whereJson(column: string, value: any): this; | ||
| whereJson(column: string, operation: Operations, value: any): this; | ||
| orWhere(statement: Record<string, any>): this; | ||
@@ -396,2 +378,4 @@ orWhere(key: string, value: any): this; | ||
| orWhereNot(key: string, value: any): this; | ||
| orWhereJson(column: string, value: any): this; | ||
| orWhereJson(column: string, operation: Operations, value: any): this; | ||
| /** | ||
@@ -472,2 +456,6 @@ * Set a or where raw statement in your query. | ||
| /** | ||
| * Convert a json selector path to mongo dot notation. | ||
| */ | ||
| private jsonSelectorToDotPath; | ||
| /** | ||
| * Creates the where clause with where and orWhere. | ||
@@ -474,0 +462,0 @@ */ |
@@ -17,2 +17,4 @@ /** | ||
| import { ConnectionFactory } from '#src/factories/ConnectionFactory'; | ||
| import { EmptyValueException } from '#src/exceptions/EmptyValueException'; | ||
| import { EmptyColumnException } from '#src/exceptions/EmptyColumnException'; | ||
| import { WrongMethodException } from '#src/exceptions/WrongMethodException'; | ||
@@ -431,3 +433,3 @@ import { MONGO_OPERATIONS_DICTIONARY } from '#src/constants/MongoOperationsDictionary'; | ||
| .toArray(); | ||
| return `${result[0]?.count || 0}`; | ||
| return Number(result[0]?.count || 0); | ||
| } | ||
@@ -452,3 +454,3 @@ /** | ||
| .toArray(); | ||
| return `${count}`; | ||
| return Number(count); | ||
| } | ||
@@ -568,2 +570,5 @@ /** | ||
| } | ||
| if (!Is.String(table)) { | ||
| throw new Error('Table must be a string value'); | ||
| } | ||
| this.tableName = table; | ||
@@ -577,7 +582,7 @@ this.qb = this.query(); | ||
| dump() { | ||
| console.log({ | ||
| process.stdout.write(`${JSON.stringify({ | ||
| where: this._where, | ||
| orWhere: this._orWhere, | ||
| pipeline: this.pipeline | ||
| }); | ||
| })}\n`); | ||
| return this; | ||
@@ -589,2 +594,5 @@ } | ||
| select(...columns) { | ||
| if (!Is.Array(columns)) { | ||
| throw new EmptyValueException('select'); | ||
| } | ||
| if (columns.includes('*')) { | ||
@@ -713,2 +721,5 @@ return this; | ||
| groupBy(...columns) { | ||
| if (Is.Undefined(columns) || !Is.Array(columns)) { | ||
| throw new EmptyColumnException('groupBy'); | ||
| } | ||
| const $group = { [this.primaryKey]: {} }; | ||
@@ -739,14 +750,2 @@ columns.forEach(column => ($group[this.primaryKey][column] = `$${column}`)); | ||
| /** | ||
| * Set a having exists statement in your query. | ||
| */ | ||
| havingExists() { | ||
| throw new NotImplementedMethodException(this.havingExists.name, 'mongo'); | ||
| } | ||
| /** | ||
| * Set a having not exists statement in your query. | ||
| */ | ||
| havingNotExists() { | ||
| throw new NotImplementedMethodException(this.havingNotExists.name, 'mongo'); | ||
| } | ||
| /** | ||
| * Set a having in statement in your query. | ||
@@ -800,20 +799,2 @@ */ | ||
| /** | ||
| * Set an or having exists statement in your query. | ||
| */ | ||
| orHavingExists() { | ||
| throw new NotImplementedMethodException(this.orHavingExists.name, 'mongo'); | ||
| } | ||
| /** | ||
| * Set an or having not exists statement in your query. | ||
| */ | ||
| orHavingNotExists() { | ||
| throw new NotImplementedMethodException(this.orHavingNotExists.name, 'mongo'); | ||
| } | ||
| /** | ||
| * Set an or having in statement in your query. | ||
| */ | ||
| orHavingIn(column, values) { | ||
| return this.orWhereIn(column, values); | ||
| } | ||
| /** | ||
| * Set an or having not in statement in your query. | ||
@@ -856,7 +837,16 @@ */ | ||
| } | ||
| if (operation === undefined) { | ||
| if (Is.Undefined(operation)) { | ||
| if (Is.Undefined(statement) || !Is.Object(statement)) { | ||
| throw new EmptyValueException('where'); | ||
| } | ||
| this._where.push(statement); | ||
| return this; | ||
| } | ||
| if (value === undefined) { | ||
| if (Is.Undefined(value)) { | ||
| if (Is.Undefined(statement) || !Is.String(statement)) { | ||
| throw new EmptyValueException('where'); | ||
| } | ||
| if (this.isUsingJsonSelector(statement)) { | ||
| return this.whereJson(statement, operation); | ||
| } | ||
| this._where.push({ | ||
@@ -867,2 +857,11 @@ [statement]: this.setOperator(operation, '=') | ||
| } | ||
| if (Is.Undefined(statement) || !Is.String(statement)) { | ||
| throw new EmptyColumnException('where'); | ||
| } | ||
| if (Is.Undefined(value)) { | ||
| throw new EmptyValueException('where'); | ||
| } | ||
| if (this.isUsingJsonSelector(statement)) { | ||
| return this.whereJson(statement, operation, value); | ||
| } | ||
| this._where.push({ [statement]: this.setOperator(value, operation) }); | ||
@@ -911,2 +910,8 @@ return this; | ||
| whereIn(column, values) { | ||
| if (Is.Undefined(column) || !Is.String(column)) { | ||
| throw new EmptyColumnException('whereIn'); | ||
| } | ||
| if (Is.Undefined(values)) { | ||
| throw new EmptyValueException('whereIn'); | ||
| } | ||
| values = values.flatMap(value => { | ||
@@ -925,2 +930,8 @@ if (ObjectId.isValidStringOrObject(value)) { | ||
| whereNotIn(column, values) { | ||
| if (Is.Undefined(column) || !Is.String(column)) { | ||
| throw new EmptyColumnException('whereNotIn'); | ||
| } | ||
| if (Is.Undefined(values)) { | ||
| throw new EmptyValueException('whereNotIn'); | ||
| } | ||
| values = values.flatMap(value => { | ||
@@ -939,2 +950,8 @@ if (ObjectId.isValidStringOrObject(value)) { | ||
| whereBetween(column, values) { | ||
| if (Is.Undefined(column) || !Is.String(column)) { | ||
| throw new EmptyColumnException('whereBetween'); | ||
| } | ||
| if (Is.Undefined(values?.[0]) || Is.Undefined(values?.[1])) { | ||
| throw new EmptyValueException('whereBetween'); | ||
| } | ||
| this._where.push({ [column]: { $gte: values[0], $lte: values[1] } }); | ||
@@ -947,2 +964,8 @@ return this; | ||
| whereNotBetween(column, values) { | ||
| if (Is.Undefined(column) || !Is.String(column)) { | ||
| throw new EmptyColumnException('whereNotBetween'); | ||
| } | ||
| if (Is.Undefined(values?.[0]) || Is.Undefined(values?.[1])) { | ||
| throw new EmptyValueException('whereNotBetween'); | ||
| } | ||
| this._where.push({ | ||
@@ -957,2 +980,5 @@ [column]: { $not: { $gte: values[0], $lte: values[1] } } | ||
| whereNull(column) { | ||
| if (Is.Undefined(column) || !Is.String(column)) { | ||
| throw new EmptyColumnException('whereNull'); | ||
| } | ||
| this._where.push({ [column]: null }); | ||
@@ -965,2 +991,5 @@ return this; | ||
| whereNotNull(column) { | ||
| if (Is.Undefined(column) || !Is.String(column)) { | ||
| throw new EmptyColumnException('whereNotNull'); | ||
| } | ||
| this._where.push({ [column]: { $ne: null } }); | ||
@@ -970,2 +999,25 @@ return this; | ||
| /** | ||
| * Set a where json statement in your query. | ||
| */ | ||
| whereJson(column, operation, value) { | ||
| if (Is.Undefined(column) || !Is.String(column)) { | ||
| throw new EmptyColumnException('whereJson'); | ||
| } | ||
| const parsed = this.parseJsonSelector(column); | ||
| if (!parsed) { | ||
| throw new Error(`Invalid JSON selector: ${column}`); | ||
| } | ||
| const path = this.jsonSelectorToDotPath(parsed.path); | ||
| if (value === undefined) { | ||
| this._where.push({ | ||
| [`${parsed.column}.${path}`]: this.setOperator(operation, '=') | ||
| }); | ||
| return this; | ||
| } | ||
| this._where.push({ | ||
| [`${parsed.column}.${path}`]: this.setOperator(value, operation) | ||
| }); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a or where statement in your query. | ||
@@ -978,10 +1030,28 @@ */ | ||
| } | ||
| if (operation === undefined) { | ||
| if (Is.Undefined(operation)) { | ||
| if (Is.Undefined(statement) || !Is.Object(statement)) { | ||
| throw new EmptyValueException('orWhere'); | ||
| } | ||
| this._orWhere.push(statement); | ||
| return this; | ||
| } | ||
| if (value === undefined) { | ||
| if (Is.Undefined(value)) { | ||
| if (Is.Undefined(statement) || !Is.String(statement)) { | ||
| throw new EmptyColumnException('orWhere'); | ||
| } | ||
| if (this.isUsingJsonSelector(statement)) { | ||
| return this.orWhereJson(statement, operation); | ||
| } | ||
| this._orWhere.push({ [statement]: this.setOperator(operation, '=') }); | ||
| return this; | ||
| } | ||
| if (Is.Undefined(statement) || !Is.String(statement)) { | ||
| throw new EmptyColumnException('orWhere'); | ||
| } | ||
| if (Is.Undefined(value)) { | ||
| throw new EmptyValueException('orWhere'); | ||
| } | ||
| if (this.isUsingJsonSelector(statement)) { | ||
| return this.orWhereJson(statement, operation, value); | ||
| } | ||
| this._orWhere.push({ [statement]: this.setOperator(value, operation) }); | ||
@@ -997,2 +1067,25 @@ return this; | ||
| /** | ||
| * Set an or where json statement in your query. | ||
| */ | ||
| orWhereJson(column, operation, value) { | ||
| if (Is.Undefined(column) || !Is.String(column)) { | ||
| throw new EmptyColumnException('orWhereJson'); | ||
| } | ||
| const parsed = this.parseJsonSelector(column); | ||
| if (!parsed) { | ||
| throw new Error(`Invalid JSON selector: ${column}`); | ||
| } | ||
| const path = this.jsonSelectorToDotPath(parsed.path); | ||
| if (value === undefined) { | ||
| this._orWhere.push({ | ||
| [`${parsed.column}.${path}`]: this.setOperator(operation, '=') | ||
| }); | ||
| return this; | ||
| } | ||
| this._orWhere.push({ | ||
| [`${parsed.column}.${path}`]: this.setOperator(value, operation) | ||
| }); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a or where raw statement in your query. | ||
@@ -1031,2 +1124,8 @@ */ | ||
| orWhereIn(column, values) { | ||
| if (Is.Undefined(column) || !Is.String(column)) { | ||
| throw new EmptyColumnException('orWhereIn'); | ||
| } | ||
| if (Is.Undefined(values)) { | ||
| throw new EmptyValueException('orWhereIn'); | ||
| } | ||
| values = values.flatMap(value => { | ||
@@ -1045,2 +1144,8 @@ if (ObjectId.isValidStringOrObject(value)) { | ||
| orWhereNotIn(column, values) { | ||
| if (Is.Undefined(column) || !Is.String(column)) { | ||
| throw new EmptyColumnException('orWhereNotIn'); | ||
| } | ||
| if (Is.Undefined(values)) { | ||
| throw new EmptyValueException('orWhereNotIn'); | ||
| } | ||
| values = values.flatMap(value => { | ||
@@ -1059,2 +1164,8 @@ if (ObjectId.isValidStringOrObject(value)) { | ||
| orWhereBetween(column, values) { | ||
| if (Is.Undefined(column) || !Is.String(column)) { | ||
| throw new EmptyColumnException('orWhereBetween'); | ||
| } | ||
| if (Is.Undefined(values?.[0]) || Is.Undefined(values?.[1])) { | ||
| throw new EmptyValueException('orWhereBetween'); | ||
| } | ||
| this._orWhere.push({ [column]: { $gte: values[0], $lte: values[1] } }); | ||
@@ -1067,2 +1178,8 @@ return this; | ||
| orWhereNotBetween(column, values) { | ||
| if (Is.Undefined(column) || !Is.String(column)) { | ||
| throw new EmptyColumnException('orWhereNotBetween'); | ||
| } | ||
| if (Is.Undefined(values?.[0]) || Is.Undefined(values?.[1])) { | ||
| throw new EmptyValueException('orWhereNotBetween'); | ||
| } | ||
| this._orWhere.push({ | ||
@@ -1077,2 +1194,5 @@ [column]: { $not: { $gte: values[0], $lte: values[1] } } | ||
| orWhereNull(column) { | ||
| if (Is.Undefined(column) || !Is.String(column)) { | ||
| throw new EmptyColumnException('orWhereNull'); | ||
| } | ||
| this._orWhere.push({ [column]: null }); | ||
@@ -1085,2 +1205,5 @@ return this; | ||
| orWhereNotNull(column) { | ||
| if (Is.Undefined(column) || !Is.String(column)) { | ||
| throw new EmptyColumnException('orWhereNotNull'); | ||
| } | ||
| this._orWhere.push({ [column]: { $ne: null } }); | ||
@@ -1093,2 +1216,5 @@ return this; | ||
| orderBy(column, direction = 'ASC') { | ||
| if (Is.Undefined(column) || !Is.String(column)) { | ||
| throw new EmptyColumnException('orderBy'); | ||
| } | ||
| this.pipeline.push({ | ||
@@ -1110,2 +1236,5 @@ $sort: { [column]: direction.toLowerCase() === 'asc' ? 1 : -1 } | ||
| latest(column = 'createdAt') { | ||
| if (Is.Undefined(column) || !Is.String(column)) { | ||
| throw new EmptyColumnException('latest'); | ||
| } | ||
| return this.orderBy(column, 'DESC'); | ||
@@ -1118,2 +1247,5 @@ } | ||
| oldest(column = 'createdAt') { | ||
| if (Is.Undefined(column) || !Is.String(column)) { | ||
| throw new EmptyColumnException('oldest'); | ||
| } | ||
| return this.orderBy(column, 'ASC'); | ||
@@ -1125,2 +1257,5 @@ } | ||
| offset(number) { | ||
| if (Is.Undefined(number) || !Is.Number(number)) { | ||
| throw new EmptyValueException('offset'); | ||
| } | ||
| this.pipeline.push({ $skip: number }); | ||
@@ -1133,2 +1268,5 @@ return this; | ||
| limit(number) { | ||
| if (Is.Undefined(number) || !Is.Number(number)) { | ||
| throw new EmptyValueException('limit'); | ||
| } | ||
| this.pipeline.push({ $limit: number }); | ||
@@ -1162,2 +1300,13 @@ return this; | ||
| /** | ||
| * Convert a json selector path to mongo dot notation. | ||
| */ | ||
| jsonSelectorToDotPath(path) { | ||
| return path | ||
| .split('->') | ||
| .map(part => part.trim()) | ||
| .filter(Boolean) | ||
| .filter(part => part !== '*') | ||
| .join('.'); | ||
| } | ||
| /** | ||
| * Creates the where clause with where and orWhere. | ||
@@ -1164,0 +1313,0 @@ */ |
@@ -9,8 +9,6 @@ /** | ||
| */ | ||
| import { type PaginatedResponse, type PaginationOptions } from '@athenna/common'; | ||
| import type { Knex } from 'knex'; | ||
| import { Driver } from '#src/database/drivers/Driver'; | ||
| import { Transaction } from '#src/database/transactions/Transaction'; | ||
| import type { ConnectionOptions, Direction, Operations } from '#src/types'; | ||
| export declare class MySqlDriver extends Driver<Knex, Knex.QueryBuilder> { | ||
| import type { Operations } from '#src/types'; | ||
| import type { ConnectionOptions } from '#src/types/ConnectionOptions'; | ||
| import { BaseKnexDriver } from '#src/database/drivers/BaseKnexDriver'; | ||
| export declare class MySqlDriver extends BaseKnexDriver { | ||
| /** | ||
@@ -21,34 +19,2 @@ * Connect to database. | ||
| /** | ||
| * Close the connection with database in this instance. | ||
| */ | ||
| close(): Promise<void>; | ||
| /** | ||
| * Creates a new instance of query builder. | ||
| */ | ||
| query(): Knex.QueryBuilder; | ||
| /** | ||
| * Sync a model schema with database. | ||
| */ | ||
| sync(): Promise<void>; | ||
| /** | ||
| * Create a new transaction. | ||
| */ | ||
| startTransaction(): Promise<Transaction<Knex.Transaction, Knex.QueryBuilder>>; | ||
| /** | ||
| * Commit the transaction. | ||
| */ | ||
| commitTransaction(): Promise<void>; | ||
| /** | ||
| * Rollback the transaction. | ||
| */ | ||
| rollbackTransaction(): Promise<void>; | ||
| /** | ||
| * Run database migrations. | ||
| */ | ||
| runMigrations(): Promise<void>; | ||
| /** | ||
| * Revert database migrations. | ||
| */ | ||
| revertMigrations(): Promise<void>; | ||
| /** | ||
| * List all databases available. | ||
@@ -58,10 +24,2 @@ */ | ||
| /** | ||
| * Get the current database name. | ||
| */ | ||
| getCurrentDatabase(): Promise<string | undefined>; | ||
| /** | ||
| * Verify if database exists. | ||
| */ | ||
| hasDatabase(database: string): Promise<boolean>; | ||
| /** | ||
| * Create a new database. | ||
@@ -79,18 +37,2 @@ */ | ||
| /** | ||
| * Verify if table exists. | ||
| */ | ||
| hasTable(table: string): Promise<boolean>; | ||
| /** | ||
| * Create a new table in database. | ||
| */ | ||
| createTable(table: string, closure: (builder: Knex.TableBuilder) => void | Promise<void>): Promise<void>; | ||
| /** | ||
| * Alter a table in database. | ||
| */ | ||
| alterTable(table: string, closure: (builder: Knex.TableBuilder) => void | Promise<void>): Promise<void>; | ||
| /** | ||
| * Drop a table in database. | ||
| */ | ||
| dropTable(table: string): Promise<void>; | ||
| /** | ||
| * Remove all data inside some database table | ||
@@ -101,347 +43,25 @@ * and restart the identity of the table. | ||
| /** | ||
| * Make a raw query in database. | ||
| */ | ||
| raw<T = any>(sql: string, bindings?: any): T; | ||
| /** | ||
| * Calculate the average of a given column. | ||
| */ | ||
| avg(column: string): Promise<string>; | ||
| /** | ||
| * Calculate the average of a given column using distinct. | ||
| */ | ||
| avgDistinct(column: string): Promise<string>; | ||
| /** | ||
| * Get the max number of a given column. | ||
| */ | ||
| max(column: string): Promise<string>; | ||
| /** | ||
| * Get the min number of a given column. | ||
| */ | ||
| min(column: string): Promise<string>; | ||
| /** | ||
| * Sum all numbers of a given column. | ||
| */ | ||
| sum(column: string): Promise<string>; | ||
| /** | ||
| * Sum all numbers of a given column in distinct mode. | ||
| */ | ||
| sumDistinct(column: string): Promise<string>; | ||
| /** | ||
| * Increment a value of a given column. | ||
| */ | ||
| increment(column: string): Promise<void>; | ||
| /** | ||
| * Decrement a value of a given column. | ||
| */ | ||
| decrement(column: string): Promise<void>; | ||
| /** | ||
| * Calculate the average of a given column using distinct. | ||
| */ | ||
| count(column?: string): Promise<string>; | ||
| /** | ||
| * Calculate the average of a given column using distinct. | ||
| */ | ||
| countDistinct(column: string): Promise<string>; | ||
| /** | ||
| * Find a value in database. | ||
| */ | ||
| find<T = any>(): Promise<T>; | ||
| /** | ||
| * Find many values in database. | ||
| */ | ||
| findMany<T = any>(): Promise<T[]>; | ||
| /** | ||
| * Find many values in database and return as paginated response. | ||
| */ | ||
| paginate<T = any>(page?: PaginationOptions | number, limit?: number, resourceUrl?: string): Promise<PaginatedResponse<T>>; | ||
| /** | ||
| * Create a value in database. | ||
| */ | ||
| create<T = any>(data?: Partial<T>): Promise<T>; | ||
| /** | ||
| * Create many values in database. | ||
| */ | ||
| createMany<T = any>(data?: Partial<T>[]): Promise<T[]>; | ||
| whereJson(column: string, value: any): this; | ||
| whereJson(column: string, operation: Operations, value: any): this; | ||
| orWhereJson(column: string, value: any): this; | ||
| orWhereJson(column: string, operation: Operations, value: any): this; | ||
| /** | ||
| * Create data or update if already exists. | ||
| * Convert a json selector path to mysql json path. | ||
| */ | ||
| createOrUpdate<T = any>(data?: Partial<T>): Promise<T | T[]>; | ||
| private parseJsonSelectorToMySqlPath; | ||
| /** | ||
| * Update a value in database. | ||
| * Split a json selector around the wildcard. | ||
| */ | ||
| update<T = any>(data: Partial<T>): Promise<T | T[]>; | ||
| private parseJsonSelectorToWildcardParts; | ||
| /** | ||
| * Delete one value in database. | ||
| * Convert path parts to a valid json path. | ||
| */ | ||
| delete(): Promise<void>; | ||
| private toJsonPath; | ||
| /** | ||
| * Set the table that this query will be executed. | ||
| * Normalize operator/value pairs from the whereJson overloads. | ||
| */ | ||
| table(table: string): this; | ||
| /** | ||
| * Log in console the actual query built. | ||
| */ | ||
| dump(): this; | ||
| /** | ||
| * Set the columns that should be selected on query. | ||
| */ | ||
| select(...columns: string[]): this; | ||
| /** | ||
| * Set the columns that should be selected on query raw. | ||
| */ | ||
| selectRaw(sql: string, bindings?: any): this; | ||
| /** | ||
| * Set the table that should be used on query. | ||
| * Different from `table()` method, this method | ||
| * doesn't change the driver table. | ||
| */ | ||
| from(table: string): this; | ||
| /** | ||
| * Set the table that should be used on query raw. | ||
| * Different from `table()` method, this method | ||
| * doesn't change the driver table. | ||
| */ | ||
| fromRaw(sql: string, bindings?: any): this; | ||
| /** | ||
| * Set a join statement in your query. | ||
| */ | ||
| join(table: any, column1?: any, operation?: any | Operations, column2?: any): this; | ||
| /** | ||
| * Set a left join statement in your query. | ||
| */ | ||
| leftJoin(table: any, column1?: any, operation?: any | Operations, column2?: any): this; | ||
| /** | ||
| * Set a right join statement in your query. | ||
| */ | ||
| rightJoin(table: any, column1?: any, operation?: any | Operations, column2?: any): this; | ||
| /** | ||
| * Set a cross join statement in your query. | ||
| */ | ||
| crossJoin(table: any, column1?: any, operation?: any | Operations, column2?: any): this; | ||
| /** | ||
| * Set a full outer join statement in your query. | ||
| */ | ||
| fullOuterJoin(table: any, column1?: any, operation?: any | Operations, column2?: any): this; | ||
| /** | ||
| * Set a left outer join statement in your query. | ||
| */ | ||
| leftOuterJoin(table: any, column1?: any, operation?: any | Operations, column2?: any): this; | ||
| /** | ||
| * Set a right outer join statement in your query. | ||
| */ | ||
| rightOuterJoin(table: any, column1?: any, operation?: any | Operations, column2?: any): this; | ||
| /** | ||
| * Set a join raw statement in your query. | ||
| */ | ||
| joinRaw(sql: string, bindings?: any): this; | ||
| /** | ||
| * Set a group by statement in your query. | ||
| */ | ||
| groupBy(...columns: string[]): this; | ||
| /** | ||
| * Set a group by raw statement in your query. | ||
| */ | ||
| groupByRaw(sql: string, bindings?: any): this; | ||
| having(column: string): this; | ||
| having(column: string, value: any): this; | ||
| having(column: string, operation: Operations, value: any): this; | ||
| /** | ||
| * Set a having raw statement in your query. | ||
| */ | ||
| havingRaw(sql: string, bindings?: any): this; | ||
| /** | ||
| * Set a having exists statement in your query. | ||
| */ | ||
| havingExists(closure: (query: MySqlDriver) => void): this; | ||
| /** | ||
| * Set a having not exists statement in your query. | ||
| */ | ||
| havingNotExists(closure: (query: MySqlDriver) => void): this; | ||
| /** | ||
| * Set a having in statement in your query. | ||
| */ | ||
| havingIn(column: string, values: any[]): this; | ||
| /** | ||
| * Set a having not in statement in your query. | ||
| */ | ||
| havingNotIn(column: string, values: any[]): this; | ||
| /** | ||
| * Set a having between statement in your query. | ||
| */ | ||
| havingBetween(column: string, values: [any, any]): this; | ||
| /** | ||
| * Set a having not between statement in your query. | ||
| */ | ||
| havingNotBetween(column: string, values: [any, any]): this; | ||
| /** | ||
| * Set a having null statement in your query. | ||
| */ | ||
| havingNull(column: string): this; | ||
| /** | ||
| * Set a having not null statement in your query. | ||
| */ | ||
| havingNotNull(column: string): this; | ||
| orHaving(column: string): this; | ||
| orHaving(column: string, value: any): this; | ||
| orHaving(column: string, operation: Operations, value: any): this; | ||
| /** | ||
| * Set an or having raw statement in your query. | ||
| */ | ||
| orHavingRaw(sql: string, bindings?: any): this; | ||
| /** | ||
| * Set an or having exists statement in your query. | ||
| */ | ||
| orHavingExists(closure: (query: MySqlDriver) => void): this; | ||
| /** | ||
| * Set an or having not exists statement in your query. | ||
| */ | ||
| orHavingNotExists(closure: (query: MySqlDriver) => void): this; | ||
| /** | ||
| * Set an or having in statement in your query. | ||
| */ | ||
| orHavingIn(column: string, values: any[]): this; | ||
| /** | ||
| * Set an or having not in statement in your query. | ||
| */ | ||
| orHavingNotIn(column: string, values: any[]): this; | ||
| /** | ||
| * Set an or having between statement in your query. | ||
| */ | ||
| orHavingBetween(column: string, values: [any, any]): this; | ||
| /** | ||
| * Set an or having not between statement in your query. | ||
| */ | ||
| orHavingNotBetween(column: string, values: [any, any]): this; | ||
| /** | ||
| * Set an or having null statement in your query. | ||
| */ | ||
| orHavingNull(column: string): this; | ||
| /** | ||
| * Set an or having not null statement in your query. | ||
| */ | ||
| orHavingNotNull(column: string): this; | ||
| where(statement: Record<string, any>): this; | ||
| where(key: string, value: any): this; | ||
| where(key: string, operation: Operations, value: any): this; | ||
| whereNot(statement: Record<string, any>): this; | ||
| whereNot(key: string, value: any): this; | ||
| /** | ||
| * Set a where raw statement in your query. | ||
| */ | ||
| whereRaw(sql: string, bindings?: any): this; | ||
| /** | ||
| * Set a where exists statement in your query. | ||
| */ | ||
| whereExists(closure: (query: MySqlDriver) => void): this; | ||
| /** | ||
| * Set a where not exists statement in your query. | ||
| */ | ||
| whereNotExists(closure: (query: MySqlDriver) => void): this; | ||
| /** | ||
| * Set a where like statement in your query. | ||
| */ | ||
| whereLike(column: string, value: any): this; | ||
| /** | ||
| * Set a where ILike statement in your query. | ||
| */ | ||
| whereILike(column: string, value: any): this; | ||
| /** | ||
| * Set a where in statement in your query. | ||
| */ | ||
| whereIn(column: string, values: any[]): this; | ||
| /** | ||
| * Set a where not in statement in your query. | ||
| */ | ||
| whereNotIn(column: string, values: any[]): this; | ||
| /** | ||
| * Set a where between statement in your query. | ||
| */ | ||
| whereBetween(column: string, values: [any, any]): this; | ||
| /** | ||
| * Set a where not between statement in your query. | ||
| */ | ||
| whereNotBetween(column: string, values: [any, any]): this; | ||
| /** | ||
| * Set a where null statement in your query. | ||
| */ | ||
| whereNull(column: string): this; | ||
| /** | ||
| * Set a where not null statement in your query. | ||
| */ | ||
| whereNotNull(column: string): this; | ||
| orWhere(statement: Record<string, any>): this; | ||
| orWhere(key: string, value: any): this; | ||
| orWhere(key: string, operation: Operations, value: any): this; | ||
| orWhereNot(statement: Record<string, any>): this; | ||
| orWhereNot(key: string, value: any): this; | ||
| /** | ||
| * Set a or where raw statement in your query. | ||
| */ | ||
| orWhereRaw(sql: string, bindings?: any): this; | ||
| /** | ||
| * Set an or where exists statement in your query. | ||
| */ | ||
| orWhereExists(closure: (query: MySqlDriver) => void): this; | ||
| /** | ||
| * Set an or where not exists statement in your query. | ||
| */ | ||
| orWhereNotExists(closure: (query: MySqlDriver) => void): this; | ||
| /** | ||
| * Set an or where like statement in your query. | ||
| */ | ||
| orWhereLike(column: string, value: any): this; | ||
| /** | ||
| * Set an or where ILike statement in your query. | ||
| */ | ||
| orWhereILike(column: string, value: any): this; | ||
| /** | ||
| * Set an or where in statement in your query. | ||
| */ | ||
| orWhereIn(column: string, values: any[]): this; | ||
| /** | ||
| * Set an or where not in statement in your query. | ||
| */ | ||
| orWhereNotIn(column: string, values: any[]): this; | ||
| /** | ||
| * Set an or where between statement in your query. | ||
| */ | ||
| orWhereBetween(column: string, values: [any, any]): this; | ||
| /** | ||
| * Set an or where not between statement in your query. | ||
| */ | ||
| orWhereNotBetween(column: string, values: [any, any]): this; | ||
| /** | ||
| * Set an or where null statement in your query. | ||
| */ | ||
| orWhereNull(column: string): this; | ||
| /** | ||
| * Set an or where not null statement in your query. | ||
| */ | ||
| orWhereNotNull(column: string): this; | ||
| /** | ||
| * Set an order by statement in your query. | ||
| */ | ||
| orderBy(column: string, direction?: Direction): this; | ||
| /** | ||
| * Set an order by raw statement in your query. | ||
| */ | ||
| orderByRaw(sql: string, bindings?: any): this; | ||
| /** | ||
| * Order the results easily by the latest date. By default, the result will | ||
| * be ordered by the table's "createdAt" column. | ||
| */ | ||
| latest(column?: string): this; | ||
| /** | ||
| * Order the results easily by the oldest date. By default, the result will | ||
| * be ordered by the table's "createdAt" column. | ||
| */ | ||
| oldest(column?: string): this; | ||
| /** | ||
| * Set the skip number in your query. | ||
| */ | ||
| offset(number: number): this; | ||
| /** | ||
| * Set the limit number in your query. | ||
| */ | ||
| limit(number: number): this; | ||
| private normalizeJsonOperation; | ||
| } |
@@ -1,2 +0,1 @@ | ||
| /* eslint-disable @typescript-eslint/ban-ts-comment */ | ||
| /** | ||
@@ -10,13 +9,10 @@ * @athenna/database | ||
| */ | ||
| import { Exec, Is, Json, Options } from '@athenna/common'; | ||
| import { debug } from '#src/debug'; | ||
| import { Log } from '@athenna/logger'; | ||
| import { Driver } from '#src/database/drivers/Driver'; | ||
| import { Is, Json, Options } from '@athenna/common'; | ||
| import { ConnectionFactory } from '#src/factories/ConnectionFactory'; | ||
| import { Transaction } from '#src/database/transactions/Transaction'; | ||
| import { MigrationSource } from '#src/database/migrations/MigrationSource'; | ||
| import { BaseKnexDriver } from '#src/database/drivers/BaseKnexDriver'; | ||
| import { WrongMethodException } from '#src/exceptions/WrongMethodException'; | ||
| import { PROTECTED_QUERY_METHODS } from '#src/constants/ProtectedQueryMethods'; | ||
| import { NotConnectedDatabaseException } from '#src/exceptions/NotConnectedDatabaseException'; | ||
| export class MySqlDriver extends Driver { | ||
| import { EmptyColumnException } from '#src/exceptions/EmptyColumnException'; | ||
| export class MySqlDriver extends BaseKnexDriver { | ||
| /** | ||
@@ -66,85 +62,2 @@ * Connect to database. | ||
| /** | ||
| * Close the connection with database in this instance. | ||
| */ | ||
| async close() { | ||
| if (!this.isConnected) { | ||
| return; | ||
| } | ||
| await this.client.destroy(); | ||
| this.qb = null; | ||
| this.tableName = null; | ||
| this.client = null; | ||
| this.isConnected = false; | ||
| ConnectionFactory.setClient(this.connection, null); | ||
| } | ||
| /** | ||
| * Creates a new instance of query builder. | ||
| */ | ||
| query() { | ||
| if (!this.isConnected) { | ||
| throw new NotConnectedDatabaseException(); | ||
| } | ||
| const query = this.useSetQB | ||
| ? this.qb.table(this.tableName) | ||
| : this.client.queryBuilder().table(this.tableName); | ||
| const handler = { | ||
| get: (target, propertyKey) => { | ||
| if (PROTECTED_QUERY_METHODS.includes(propertyKey)) { | ||
| this.qb = this.query(); | ||
| } | ||
| return target[propertyKey]; | ||
| } | ||
| }; | ||
| return new Proxy(query, handler); | ||
| } | ||
| /** | ||
| * Sync a model schema with database. | ||
| */ | ||
| async sync() { | ||
| debug(`database sync with ${MySqlDriver.name} is not available yet, use migration instead.`); | ||
| } | ||
| /** | ||
| * Create a new transaction. | ||
| */ | ||
| async startTransaction() { | ||
| const trx = await this.client.transaction(); | ||
| return new Transaction(this.clone().setClient(trx)); | ||
| } | ||
| /** | ||
| * Commit the transaction. | ||
| */ | ||
| async commitTransaction() { | ||
| const client = this.client; | ||
| await client.commit(); | ||
| this.tableName = null; | ||
| this.client = null; | ||
| this.isConnected = false; | ||
| } | ||
| /** | ||
| * Rollback the transaction. | ||
| */ | ||
| async rollbackTransaction() { | ||
| const client = this.client; | ||
| await client.rollback(); | ||
| this.tableName = null; | ||
| this.client = null; | ||
| this.isConnected = false; | ||
| } | ||
| /** | ||
| * Run database migrations. | ||
| */ | ||
| async runMigrations() { | ||
| await this.client.migrate.latest({ | ||
| migrationSource: new MigrationSource(this.connection) | ||
| }); | ||
| } | ||
| /** | ||
| * Revert database migrations. | ||
| */ | ||
| async revertMigrations() { | ||
| await this.client.migrate.rollback({ | ||
| migrationSource: new MigrationSource(this.connection) | ||
| }); | ||
| } | ||
| /** | ||
| * List all databases available. | ||
@@ -157,15 +70,2 @@ */ | ||
| /** | ||
| * Get the current database name. | ||
| */ | ||
| async getCurrentDatabase() { | ||
| return this.client.client.database(); | ||
| } | ||
| /** | ||
| * Verify if database exists. | ||
| */ | ||
| async hasDatabase(database) { | ||
| const databases = await this.getDatabases(); | ||
| return databases.includes(database); | ||
| } | ||
| /** | ||
| * Create a new database. | ||
@@ -190,26 +90,2 @@ */ | ||
| /** | ||
| * Verify if table exists. | ||
| */ | ||
| async hasTable(table) { | ||
| return this.client.schema.hasTable(table); | ||
| } | ||
| /** | ||
| * Create a new table in database. | ||
| */ | ||
| async createTable(table, closure) { | ||
| await this.client.schema.createTable(table, closure); | ||
| } | ||
| /** | ||
| * Alter a table in database. | ||
| */ | ||
| async alterTable(table, closure) { | ||
| await this.client.schema.alterTable(table, closure); | ||
| } | ||
| /** | ||
| * Drop a table in database. | ||
| */ | ||
| async dropTable(table) { | ||
| await this.client.schema.dropTableIfExists(table); | ||
| } | ||
| /** | ||
| * Remove all data inside some database table | ||
@@ -228,117 +104,2 @@ * and restart the identity of the table. | ||
| /** | ||
| * Make a raw query in database. | ||
| */ | ||
| raw(sql, bindings) { | ||
| return this.client.raw(sql, bindings); | ||
| } | ||
| /** | ||
| * Calculate the average of a given column. | ||
| */ | ||
| async avg(column) { | ||
| const [{ avg }] = await this.qb.avg({ avg: column }); | ||
| return avg; | ||
| } | ||
| /** | ||
| * Calculate the average of a given column using distinct. | ||
| */ | ||
| async avgDistinct(column) { | ||
| const [{ avg }] = await this.qb.avgDistinct({ avg: column }); | ||
| return avg; | ||
| } | ||
| /** | ||
| * Get the max number of a given column. | ||
| */ | ||
| async max(column) { | ||
| const [{ max }] = await this.qb.max({ max: column }); | ||
| return max; | ||
| } | ||
| /** | ||
| * Get the min number of a given column. | ||
| */ | ||
| async min(column) { | ||
| const [{ min }] = await this.qb.min({ min: column }); | ||
| return min; | ||
| } | ||
| /** | ||
| * Sum all numbers of a given column. | ||
| */ | ||
| async sum(column) { | ||
| const [{ sum }] = await this.qb.sum({ sum: column }); | ||
| return sum; | ||
| } | ||
| /** | ||
| * Sum all numbers of a given column in distinct mode. | ||
| */ | ||
| async sumDistinct(column) { | ||
| const [{ sum }] = await this.qb.sumDistinct({ sum: column }); | ||
| return sum; | ||
| } | ||
| /** | ||
| * Increment a value of a given column. | ||
| */ | ||
| async increment(column) { | ||
| await this.qb.increment(column); | ||
| } | ||
| /** | ||
| * Decrement a value of a given column. | ||
| */ | ||
| async decrement(column) { | ||
| await this.qb.decrement(column); | ||
| } | ||
| /** | ||
| * Calculate the average of a given column using distinct. | ||
| */ | ||
| async count(column = '*') { | ||
| const [{ count }] = await this.qb.count({ count: column }); | ||
| return `${count}`; | ||
| } | ||
| /** | ||
| * Calculate the average of a given column using distinct. | ||
| */ | ||
| async countDistinct(column) { | ||
| const [{ count }] = await this.qb.countDistinct({ count: column }); | ||
| return `${count}`; | ||
| } | ||
| /** | ||
| * Find a value in database. | ||
| */ | ||
| async find() { | ||
| return this.qb.first(); | ||
| } | ||
| /** | ||
| * Find many values in database. | ||
| */ | ||
| async findMany() { | ||
| const data = await this.qb; | ||
| this.qb = this.query(); | ||
| return data; | ||
| } | ||
| /** | ||
| * Find many values in database and return as paginated response. | ||
| */ | ||
| async paginate(page = { page: 0, limit: 10, resourceUrl: '/' }, limit = 10, resourceUrl = '/') { | ||
| if (Is.Number(page)) { | ||
| page = { page, limit, resourceUrl }; | ||
| } | ||
| const [{ count }] = await this.qb | ||
| .clone() | ||
| .clearOrder() | ||
| .clearSelect() | ||
| .count({ count: '*' }); | ||
| const data = await this.offset(page.page * page.limit) | ||
| .limit(page.limit) | ||
| .findMany(); | ||
| return Exec.pagination(data, parseInt(count), page); | ||
| } | ||
| /** | ||
| * Create a value in database. | ||
| */ | ||
| async create(data = {}) { | ||
| if (Is.Array(data)) { | ||
| throw new WrongMethodException('create', 'createMany'); | ||
| } | ||
| const created = await this.createMany([data]); | ||
| return created[0]; | ||
| } | ||
| /** | ||
| * Create many values in database. | ||
@@ -350,8 +111,9 @@ */ | ||
| } | ||
| const preparedData = data.map(data => this.prepareInsert(data)); | ||
| const ids = []; | ||
| const promises = data.map(data => { | ||
| const promises = preparedData.map((prepared, index) => { | ||
| return this.qb | ||
| .clone() | ||
| .insert(data) | ||
| .then(([id]) => ids.push(data[this.primaryKey] || id)); | ||
| .insert(prepared) | ||
| .then(([id]) => ids.push(data[index][this.primaryKey] || id)); | ||
| }); | ||
@@ -362,613 +124,103 @@ await Promise.all(promises); | ||
| /** | ||
| * Create data or update if already exists. | ||
| * Set a where json statement in your query. | ||
| */ | ||
| async createOrUpdate(data = {}) { | ||
| const query = this.qb.clone(); | ||
| const hasValue = await query.first(); | ||
| if (hasValue) { | ||
| await this.qb | ||
| .where(this.primaryKey, hasValue[this.primaryKey]) | ||
| .update(data); | ||
| return this.where(this.primaryKey, hasValue[this.primaryKey]).find(); | ||
| whereJson(column, operator, value) { | ||
| if (Is.Undefined(column) || !Is.String(column)) { | ||
| throw new EmptyColumnException('whereJson'); | ||
| } | ||
| return this.create(data); | ||
| } | ||
| /** | ||
| * Update a value in database. | ||
| */ | ||
| async update(data) { | ||
| await this.qb.clone().update(data); | ||
| const result = await this.findMany(); | ||
| if (result.length === 1) { | ||
| return result[0]; | ||
| const parsed = this.parseJsonSelector(column); | ||
| if (!parsed) { | ||
| throw new Error(`Invalid JSON selector: ${column}`); | ||
| } | ||
| return result; | ||
| } | ||
| /** | ||
| * Delete one value in database. | ||
| */ | ||
| async delete() { | ||
| await this.qb.delete(); | ||
| } | ||
| /** | ||
| * Set the table that this query will be executed. | ||
| */ | ||
| table(table) { | ||
| if (!this.isConnected) { | ||
| throw new NotConnectedDatabaseException(); | ||
| const normalized = this.normalizeJsonOperation(operator, value); | ||
| if (!parsed.path.includes('*')) { | ||
| this.qb.whereRaw('JSON_UNQUOTE(JSON_EXTRACT(??, ?)) ' + normalized.operator + ' ?', [ | ||
| parsed.column, | ||
| this.parseJsonSelectorToMySqlPath(parsed.path), | ||
| normalized.value | ||
| ]); | ||
| return this; | ||
| } | ||
| this.tableName = table; | ||
| this.qb = this.query(); | ||
| const wildcard = this.parseJsonSelectorToWildcardParts(parsed.path); | ||
| this.qb.whereRaw("exists (select 1 from json_table(json_extract(??, ?), '$[*]' columns (value json path ?)) as jt where JSON_UNQUOTE(jt.value) " + | ||
| normalized.operator + | ||
| ' ?)', [parsed.column, wildcard.arrayPath, wildcard.valuePath, normalized.value]); | ||
| return this; | ||
| } | ||
| /** | ||
| * Log in console the actual query built. | ||
| * Set an or where json statement in your query. | ||
| */ | ||
| dump() { | ||
| console.log(this.qb.toSQL().toNative()); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set the columns that should be selected on query. | ||
| */ | ||
| select(...columns) { | ||
| this.qb.select(...columns); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set the columns that should be selected on query raw. | ||
| */ | ||
| selectRaw(sql, bindings) { | ||
| return this.select(this.raw(sql, bindings)); | ||
| } | ||
| /** | ||
| * Set the table that should be used on query. | ||
| * Different from `table()` method, this method | ||
| * doesn't change the driver table. | ||
| */ | ||
| from(table) { | ||
| this.qb.from(table); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set the table that should be used on query raw. | ||
| * Different from `table()` method, this method | ||
| * doesn't change the driver table. | ||
| */ | ||
| fromRaw(sql, bindings) { | ||
| return this.from(this.raw(sql, bindings)); | ||
| } | ||
| /** | ||
| * Set a join statement in your query. | ||
| */ | ||
| join(table, column1, operation, column2) { | ||
| return this.joinByType('join', table, column1, operation, column2); | ||
| } | ||
| /** | ||
| * Set a left join statement in your query. | ||
| */ | ||
| leftJoin(table, column1, operation, column2) { | ||
| return this.joinByType('leftJoin', table, column1, operation, column2); | ||
| } | ||
| /** | ||
| * Set a right join statement in your query. | ||
| */ | ||
| rightJoin(table, column1, operation, column2) { | ||
| return this.joinByType('rightJoin', table, column1, operation, column2); | ||
| } | ||
| /** | ||
| * Set a cross join statement in your query. | ||
| */ | ||
| crossJoin(table, column1, operation, column2) { | ||
| return this.joinByType('crossJoin', table, column1, operation, column2); | ||
| } | ||
| /** | ||
| * Set a full outer join statement in your query. | ||
| */ | ||
| fullOuterJoin(table, column1, operation, column2) { | ||
| // TODO https://github.com/knex/knex/issues/3949 | ||
| return this.joinByType('leftJoin', table, column1, operation, column2); | ||
| } | ||
| /** | ||
| * Set a left outer join statement in your query. | ||
| */ | ||
| leftOuterJoin(table, column1, operation, column2) { | ||
| return this.joinByType('leftOuterJoin', table, column1, operation, column2); | ||
| } | ||
| /** | ||
| * Set a right outer join statement in your query. | ||
| */ | ||
| rightOuterJoin(table, column1, operation, column2) { | ||
| return this.joinByType('rightOuterJoin', table, column1, operation, column2); | ||
| } | ||
| /** | ||
| * Set a join raw statement in your query. | ||
| */ | ||
| joinRaw(sql, bindings) { | ||
| this.qb.joinRaw(sql, bindings); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a group by statement in your query. | ||
| */ | ||
| groupBy(...columns) { | ||
| this.qb.groupBy(...columns); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a group by raw statement in your query. | ||
| */ | ||
| groupByRaw(sql, bindings) { | ||
| this.qb.groupByRaw(sql, bindings); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a having statement in your query. | ||
| */ | ||
| having(column, operation, value) { | ||
| if (operation === undefined) { | ||
| this.qb.having(column); | ||
| return this; | ||
| orWhereJson(column, operator, value) { | ||
| if (Is.Undefined(column) || !Is.String(column)) { | ||
| throw new EmptyColumnException('orWhereJson'); | ||
| } | ||
| if (value === undefined) { | ||
| this.qb.having(column, '=', operation); | ||
| return this; | ||
| const parsed = this.parseJsonSelector(column); | ||
| if (!parsed) { | ||
| throw new Error(`Invalid JSON selector: ${column}`); | ||
| } | ||
| this.qb.having(column, operation, value); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a having raw statement in your query. | ||
| */ | ||
| havingRaw(sql, bindings) { | ||
| this.qb.havingRaw(sql, bindings); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a having exists statement in your query. | ||
| */ | ||
| havingExists(closure) { | ||
| const driver = this.clone(); | ||
| // @ts-ignore | ||
| this.qb.havingExists(function () { | ||
| closure(driver.setQueryBuilder(this, { useSetQB: true })); | ||
| }); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a having not exists statement in your query. | ||
| */ | ||
| havingNotExists(closure) { | ||
| const driver = this.clone(); | ||
| // @ts-ignore | ||
| this.qb.havingNotExists(function () { | ||
| closure(driver.setQueryBuilder(this, { useSetQB: true })); | ||
| }); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a having in statement in your query. | ||
| */ | ||
| havingIn(column, values) { | ||
| this.qb.havingIn(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a having not in statement in your query. | ||
| */ | ||
| havingNotIn(column, values) { | ||
| this.qb.havingNotIn(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a having between statement in your query. | ||
| */ | ||
| havingBetween(column, values) { | ||
| this.qb.havingBetween(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a having not between statement in your query. | ||
| */ | ||
| havingNotBetween(column, values) { | ||
| this.qb.havingNotBetween(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a having null statement in your query. | ||
| */ | ||
| havingNull(column) { | ||
| this.qb.havingNull(column); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a having not null statement in your query. | ||
| */ | ||
| havingNotNull(column) { | ||
| this.qb.havingNotNull(column); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or having statement in your query. | ||
| */ | ||
| orHaving(column, operation, value) { | ||
| if (operation === undefined) { | ||
| this.qb.orHaving(column); | ||
| const normalized = this.normalizeJsonOperation(operator, value); | ||
| if (!parsed.path.includes('*')) { | ||
| this.qb.orWhereRaw('JSON_UNQUOTE(JSON_EXTRACT(??, ?)) ' + normalized.operator + ' ?', [ | ||
| parsed.column, | ||
| this.parseJsonSelectorToMySqlPath(parsed.path), | ||
| normalized.value | ||
| ]); | ||
| return this; | ||
| } | ||
| if (value === undefined) { | ||
| this.qb.orHaving(column, '=', operation); | ||
| return this; | ||
| } | ||
| this.qb.orHaving(column, operation, value); | ||
| const wildcard = this.parseJsonSelectorToWildcardParts(parsed.path); | ||
| this.qb.orWhereRaw("exists (select 1 from json_table(json_extract(??, ?), '$[*]' columns (value json path ?)) as jt where JSON_UNQUOTE(jt.value) " + | ||
| normalized.operator + | ||
| ' ?)', [parsed.column, wildcard.arrayPath, wildcard.valuePath, normalized.value]); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or having raw statement in your query. | ||
| * Convert a json selector path to mysql json path. | ||
| */ | ||
| orHavingRaw(sql, bindings) { | ||
| this.qb.orHavingRaw(sql, bindings); | ||
| return this; | ||
| parseJsonSelectorToMySqlPath(path) { | ||
| const parts = path | ||
| .split('->') | ||
| .map(part => part.trim()) | ||
| .filter(Boolean); | ||
| return this.toJsonPath(parts); | ||
| } | ||
| /** | ||
| * Set an or having exists statement in your query. | ||
| * Split a json selector around the wildcard. | ||
| */ | ||
| orHavingExists(closure) { | ||
| const driver = this.clone(); | ||
| // @ts-ignore | ||
| this.qb.orHavingExists(function () { | ||
| closure(driver.setQueryBuilder(this, { useSetQB: true })); | ||
| }); | ||
| return this; | ||
| parseJsonSelectorToWildcardParts(path) { | ||
| const parts = path | ||
| .split('->') | ||
| .map(part => part.trim()) | ||
| .filter(Boolean); | ||
| const wildcardIndex = parts.indexOf('*'); | ||
| return { | ||
| arrayPath: this.toJsonPath(parts.slice(0, wildcardIndex)), | ||
| valuePath: this.toJsonPath(parts.slice(wildcardIndex + 1)) | ||
| }; | ||
| } | ||
| /** | ||
| * Set an or having not exists statement in your query. | ||
| * Convert path parts to a valid json path. | ||
| */ | ||
| orHavingNotExists(closure) { | ||
| const driver = this.clone(); | ||
| // @ts-ignore | ||
| this.qb.orHavingNotExists(function () { | ||
| closure(driver.setQueryBuilder(this, { useSetQB: true })); | ||
| }); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or having in statement in your query. | ||
| */ | ||
| orHavingIn(column, values) { | ||
| // @ts-ignore | ||
| this.qb.orHavingIn(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or having not in statement in your query. | ||
| */ | ||
| orHavingNotIn(column, values) { | ||
| this.qb.orHavingNotIn(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or having between statement in your query. | ||
| */ | ||
| orHavingBetween(column, values) { | ||
| this.qb.orHavingBetween(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or having not between statement in your query. | ||
| */ | ||
| orHavingNotBetween(column, values) { | ||
| this.qb.orHavingNotBetween(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or having null statement in your query. | ||
| */ | ||
| orHavingNull(column) { | ||
| // @ts-ignore | ||
| this.qb.orHavingNull(column); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or having not null statement in your query. | ||
| */ | ||
| orHavingNotNull(column) { | ||
| // @ts-ignore | ||
| this.qb.orHavingNotNull(column); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a where statement in your query. | ||
| */ | ||
| where(statement, operation, value) { | ||
| if (Is.Function(statement)) { | ||
| const driver = this.clone(); | ||
| this.qb.where(function () { | ||
| statement(driver.setQueryBuilder(this, { useSetQB: true })); | ||
| }); | ||
| return this; | ||
| } | ||
| if (operation === undefined) { | ||
| if (Is.Array(statement)) { | ||
| throw new Error('Arrays as statement are not supported.'); | ||
| toJsonPath(parts) { | ||
| return parts.reduce((jsonPath, part) => { | ||
| if (/^\d+$/.test(part)) { | ||
| return `${jsonPath}[${part}]`; | ||
| } | ||
| if (Is.String(statement)) { | ||
| throw new Error(`The value for the "${statement}" column is undefined and where will not work.`); | ||
| } | ||
| this.qb.where(statement); | ||
| return this; | ||
| } | ||
| if (value === undefined) { | ||
| this.qb.where(statement, operation); | ||
| return this; | ||
| } | ||
| this.qb.where(statement, operation, value); | ||
| return this; | ||
| return `${jsonPath}.${part}`; | ||
| }, '$'); | ||
| } | ||
| /** | ||
| * Set a where not statement in your query. | ||
| * Normalize operator/value pairs from the whereJson overloads. | ||
| */ | ||
| whereNot(statement, value) { | ||
| if (Is.Function(statement)) { | ||
| const driver = this.clone(); | ||
| this.qb.whereNot(function () { | ||
| statement(driver.setQueryBuilder(this, { useSetQB: true })); | ||
| }); | ||
| return this; | ||
| normalizeJsonOperation(operator, value) { | ||
| if (Is.Undefined(value)) { | ||
| return { | ||
| operator: '=', | ||
| value: operator | ||
| }; | ||
| } | ||
| if (value === undefined) { | ||
| this.qb.whereNot(statement); | ||
| return this; | ||
| } | ||
| this.qb.whereNot(statement, value); | ||
| return this; | ||
| return { | ||
| operator, | ||
| value | ||
| }; | ||
| } | ||
| /** | ||
| * Set a where raw statement in your query. | ||
| */ | ||
| whereRaw(sql, bindings) { | ||
| this.qb.whereRaw(sql, bindings); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a where exists statement in your query. | ||
| */ | ||
| whereExists(closure) { | ||
| const driver = this.clone(); | ||
| this.qb.whereExists(function () { | ||
| closure(driver.setQueryBuilder(this, { useSetQB: true })); | ||
| }); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a where not exists statement in your query. | ||
| */ | ||
| whereNotExists(closure) { | ||
| const driver = this.clone(); | ||
| this.qb.whereNotExists(function () { | ||
| closure(driver.setQueryBuilder(this, { useSetQB: true })); | ||
| }); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a where like statement in your query. | ||
| */ | ||
| whereLike(column, value) { | ||
| this.qb.whereLike(column, value); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a where ILike statement in your query. | ||
| */ | ||
| whereILike(column, value) { | ||
| this.qb.whereILike(column, value); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a where in statement in your query. | ||
| */ | ||
| whereIn(column, values) { | ||
| this.qb.whereIn(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a where not in statement in your query. | ||
| */ | ||
| whereNotIn(column, values) { | ||
| this.qb.whereNotIn(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a where between statement in your query. | ||
| */ | ||
| whereBetween(column, values) { | ||
| this.qb.whereBetween(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a where not between statement in your query. | ||
| */ | ||
| whereNotBetween(column, values) { | ||
| this.qb.whereNotBetween(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a where null statement in your query. | ||
| */ | ||
| whereNull(column) { | ||
| this.qb.whereNull(column); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a where not null statement in your query. | ||
| */ | ||
| whereNotNull(column) { | ||
| this.qb.whereNotNull(column); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a or where statement in your query. | ||
| */ | ||
| orWhere(statement, operation, value) { | ||
| if (Is.Function(statement)) { | ||
| const driver = this.clone(); | ||
| this.qb.orWhere(function () { | ||
| statement(driver.setQueryBuilder(this, { useSetQB: true })); | ||
| }); | ||
| return this; | ||
| } | ||
| if (operation === undefined) { | ||
| this.qb.orWhere(statement); | ||
| return this; | ||
| } | ||
| if (value === undefined) { | ||
| this.qb.orWhere(statement, operation); | ||
| return this; | ||
| } | ||
| this.qb.orWhere(statement, operation, value); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or where not statement in your query. | ||
| */ | ||
| orWhereNot(statement, value) { | ||
| if (Is.Function(statement)) { | ||
| const driver = this.clone(); | ||
| this.qb.orWhereNot(function () { | ||
| statement(driver.setQueryBuilder(this, { useSetQB: true })); | ||
| }); | ||
| return this; | ||
| } | ||
| if (value === undefined) { | ||
| this.qb.orWhereNot(statement); | ||
| return this; | ||
| } | ||
| this.qb.orWhereNot(statement, value); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a or where raw statement in your query. | ||
| */ | ||
| orWhereRaw(sql, bindings) { | ||
| this.qb.orWhereRaw(sql, bindings); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or where exists statement in your query. | ||
| */ | ||
| orWhereExists(closure) { | ||
| const driver = this.clone(); | ||
| this.qb.orWhereExists(function () { | ||
| closure(driver.setQueryBuilder(this, { useSetQB: true })); | ||
| }); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or where not exists statement in your query. | ||
| */ | ||
| orWhereNotExists(closure) { | ||
| const driver = this.clone(); | ||
| this.qb.orWhereNotExists(function () { | ||
| closure(driver.setQueryBuilder(this, { useSetQB: true })); | ||
| }); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or where like statement in your query. | ||
| */ | ||
| orWhereLike(column, value) { | ||
| this.qb.orWhereLike(column, value); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or where ILike statement in your query. | ||
| */ | ||
| orWhereILike(column, value) { | ||
| this.qb.orWhereILike(column, value); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or where in statement in your query. | ||
| */ | ||
| orWhereIn(column, values) { | ||
| this.qb.orWhereIn(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or where not in statement in your query. | ||
| */ | ||
| orWhereNotIn(column, values) { | ||
| this.qb.orWhereNotIn(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or where between statement in your query. | ||
| */ | ||
| orWhereBetween(column, values) { | ||
| this.qb.orWhereBetween(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or where not between statement in your query. | ||
| */ | ||
| orWhereNotBetween(column, values) { | ||
| this.qb.orWhereNotBetween(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or where null statement in your query. | ||
| */ | ||
| orWhereNull(column) { | ||
| this.qb.orWhereNull(column); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or where not null statement in your query. | ||
| */ | ||
| orWhereNotNull(column) { | ||
| this.qb.orWhereNotNull(column); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an order by statement in your query. | ||
| */ | ||
| orderBy(column, direction = 'ASC') { | ||
| this.qb.orderBy(column, direction.toUpperCase()); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an order by raw statement in your query. | ||
| */ | ||
| orderByRaw(sql, bindings) { | ||
| this.qb.orderByRaw(sql, bindings); | ||
| return this; | ||
| } | ||
| /** | ||
| * Order the results easily by the latest date. By default, the result will | ||
| * be ordered by the table's "createdAt" column. | ||
| */ | ||
| latest(column = 'createdAt') { | ||
| return this.orderBy(column, 'DESC'); | ||
| } | ||
| /** | ||
| * Order the results easily by the oldest date. By default, the result will | ||
| * be ordered by the table's "createdAt" column. | ||
| */ | ||
| oldest(column = 'createdAt') { | ||
| return this.orderBy(column, 'ASC'); | ||
| } | ||
| /** | ||
| * Set the skip number in your query. | ||
| */ | ||
| offset(number) { | ||
| this.qb.offset(number); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set the limit number in your query. | ||
| */ | ||
| limit(number) { | ||
| this.qb.limit(number); | ||
| return this; | ||
| } | ||
| } |
@@ -9,8 +9,6 @@ /** | ||
| */ | ||
| import { type PaginatedResponse, type PaginationOptions } from '@athenna/common'; | ||
| import type { Knex } from 'knex'; | ||
| import { Driver } from '#src/database/drivers/Driver'; | ||
| import { Transaction } from '#src/database/transactions/Transaction'; | ||
| import type { ConnectionOptions, Direction, Operations } from '#src/types'; | ||
| export declare class PostgresDriver extends Driver<Knex, Knex.QueryBuilder> { | ||
| import type { Operations } from '#src/types'; | ||
| import { BaseKnexDriver } from '#src/database/drivers/BaseKnexDriver'; | ||
| import type { ConnectionOptions } from '#src/types/ConnectionOptions'; | ||
| export declare class PostgresDriver extends BaseKnexDriver { | ||
| /** | ||
@@ -25,30 +23,2 @@ * Connect to database. | ||
| /** | ||
| * Creates a new instance of query builder. | ||
| */ | ||
| query(): Knex.QueryBuilder; | ||
| /** | ||
| * Sync a model schema with database. | ||
| */ | ||
| sync(): Promise<void>; | ||
| /** | ||
| * Create a new transaction. | ||
| */ | ||
| startTransaction(): Promise<Transaction<Knex.Transaction, Knex.QueryBuilder>>; | ||
| /** | ||
| * Commit the transaction. | ||
| */ | ||
| commitTransaction(): Promise<void>; | ||
| /** | ||
| * Rollback the transaction. | ||
| */ | ||
| rollbackTransaction(): Promise<void>; | ||
| /** | ||
| * Run database migrations. | ||
| */ | ||
| runMigrations(): Promise<void>; | ||
| /** | ||
| * Revert database migrations. | ||
| */ | ||
| revertMigrations(): Promise<void>; | ||
| /** | ||
| * List all databases available. | ||
@@ -58,10 +28,2 @@ */ | ||
| /** | ||
| * Get the current database name. | ||
| */ | ||
| getCurrentDatabase(): Promise<string | undefined>; | ||
| /** | ||
| * Verify if database exists. | ||
| */ | ||
| hasDatabase(database: string): Promise<boolean>; | ||
| /** | ||
| * Create a new database. | ||
@@ -79,18 +41,2 @@ */ | ||
| /** | ||
| * Verify if table exists. | ||
| */ | ||
| hasTable(table: string): Promise<boolean>; | ||
| /** | ||
| * Create a new table in database. | ||
| */ | ||
| createTable(table: string, closure: (builder: Knex.TableBuilder) => void | Promise<void>): Promise<void>; | ||
| /** | ||
| * Alter a table in database. | ||
| */ | ||
| alterTable(table: string, closure: (builder: Knex.TableBuilder) => void | Promise<void>): Promise<void>; | ||
| /** | ||
| * Drop a table in database. | ||
| */ | ||
| dropTable(table: string): Promise<void>; | ||
| /** | ||
| * Remove all data inside some database table | ||
@@ -101,347 +47,21 @@ * and restart the identity of the table. | ||
| /** | ||
| * Make a raw query in database. | ||
| */ | ||
| raw<T = any>(sql: string, bindings?: any): T; | ||
| /** | ||
| * Calculate the average of a given column. | ||
| */ | ||
| avg(column: string): Promise<string>; | ||
| /** | ||
| * Calculate the average of a given column using distinct. | ||
| */ | ||
| avgDistinct(column: string): Promise<string>; | ||
| /** | ||
| * Get the max number of a given column. | ||
| */ | ||
| max(column: string): Promise<string>; | ||
| /** | ||
| * Get the min number of a given column. | ||
| */ | ||
| min(column: string): Promise<string>; | ||
| /** | ||
| * Sum all numbers of a given column. | ||
| */ | ||
| sum(column: string): Promise<string>; | ||
| /** | ||
| * Sum all numbers of a given column in distinct mode. | ||
| */ | ||
| sumDistinct(column: string): Promise<string>; | ||
| /** | ||
| * Increment a value of a given column. | ||
| */ | ||
| increment(column: string): Promise<void>; | ||
| /** | ||
| * Decrement a value of a given column. | ||
| */ | ||
| decrement(column: string): Promise<void>; | ||
| /** | ||
| * Calculate the average of a given column using distinct. | ||
| */ | ||
| count(column?: string): Promise<string>; | ||
| /** | ||
| * Calculate the average of a given column using distinct. | ||
| */ | ||
| countDistinct(column: string): Promise<string>; | ||
| /** | ||
| * Find a value in database. | ||
| */ | ||
| find<T = any>(): Promise<T>; | ||
| /** | ||
| * Find many values in database. | ||
| */ | ||
| findMany<T = any>(): Promise<T[]>; | ||
| /** | ||
| * Find many values in database and return as paginated response. | ||
| */ | ||
| paginate<T = any>(page?: PaginationOptions | number, limit?: number, resourceUrl?: string): Promise<PaginatedResponse<T>>; | ||
| /** | ||
| * Create a value in database. | ||
| */ | ||
| create<T = any>(data?: Partial<T>): Promise<T>; | ||
| /** | ||
| * Create many values in database. | ||
| */ | ||
| createMany<T = any>(data?: Partial<T>[]): Promise<T[]>; | ||
| whereJson(column: string, value: any): this; | ||
| whereJson(column: string, operation: Operations, value: any): this; | ||
| orWhereJson(column: string, value: any): this; | ||
| orWhereJson(column: string, operation: Operations, value: any): this; | ||
| /** | ||
| * Create data or update if already exists. | ||
| * Convert a json selector path to a valid postgres json path. | ||
| */ | ||
| createOrUpdate<T = any>(data?: Partial<T>): Promise<T | T[]>; | ||
| private parseJsonSelectorToWildcardPath; | ||
| /** | ||
| * Update a value in database. | ||
| * Normalize operator/value pair for postgres json path comparisons. | ||
| */ | ||
| update<T = any>(data: Partial<T>): Promise<T | T[]>; | ||
| private normalizeJsonOperation; | ||
| /** | ||
| * Delete one value in database. | ||
| * Convert query operators to postgres json path operators. | ||
| */ | ||
| delete(): Promise<void>; | ||
| /** | ||
| * Set the table that this query will be executed. | ||
| */ | ||
| table(table: string): this; | ||
| /** | ||
| * Log in console the actual query built. | ||
| */ | ||
| dump(): this; | ||
| /** | ||
| * Set the columns that should be selected on query. | ||
| */ | ||
| select(...columns: string[]): this; | ||
| /** | ||
| * Set the columns that should be selected on query raw. | ||
| */ | ||
| selectRaw(sql: string, bindings?: any): this; | ||
| /** | ||
| * Set the table that should be used on query. | ||
| * Different from `table()` method, this method | ||
| * doesn't change the driver table. | ||
| */ | ||
| from(table: string): this; | ||
| /** | ||
| * Set the table that should be used on query raw. | ||
| * Different from `table()` method, this method | ||
| * doesn't change the driver table. | ||
| */ | ||
| fromRaw(sql: string, bindings?: any): this; | ||
| /** | ||
| * Set a join statement in your query. | ||
| */ | ||
| join(table: any, column1?: any, operation?: any | Operations, column2?: any): this; | ||
| /** | ||
| * Set a left join statement in your query. | ||
| */ | ||
| leftJoin(table: any, column1?: any, operation?: any | Operations, column2?: any): this; | ||
| /** | ||
| * Set a right join statement in your query. | ||
| */ | ||
| rightJoin(table: any, column1?: any, operation?: any | Operations, column2?: any): this; | ||
| /** | ||
| * Set a cross join statement in your query. | ||
| */ | ||
| crossJoin(table: any, column1?: any, operation?: any | Operations, column2?: any): this; | ||
| /** | ||
| * Set a full outer join statement in your query. | ||
| */ | ||
| fullOuterJoin(table: any, column1?: any, operation?: any | Operations, column2?: any): this; | ||
| /** | ||
| * Set a left outer join statement in your query. | ||
| */ | ||
| leftOuterJoin(table: any, column1?: any, operation?: any | Operations, column2?: any): this; | ||
| /** | ||
| * Set a right outer join statement in your query. | ||
| */ | ||
| rightOuterJoin(table: any, column1?: any, operation?: any | Operations, column2?: any): this; | ||
| /** | ||
| * Set a join raw statement in your query. | ||
| */ | ||
| joinRaw(sql: string, bindings?: any): this; | ||
| /** | ||
| * Set a group by statement in your query. | ||
| */ | ||
| groupBy(...columns: string[]): this; | ||
| /** | ||
| * Set a group by raw statement in your query. | ||
| */ | ||
| groupByRaw(sql: string, bindings?: any): this; | ||
| having(column: string): this; | ||
| having(column: string, value: any): this; | ||
| having(column: string, operation: Operations, value: any): this; | ||
| /** | ||
| * Set a having raw statement in your query. | ||
| */ | ||
| havingRaw(sql: string, bindings?: any): this; | ||
| /** | ||
| * Set a having exists statement in your query. | ||
| */ | ||
| havingExists(closure: (query: PostgresDriver) => void): this; | ||
| /** | ||
| * Set a having not exists statement in your query. | ||
| */ | ||
| havingNotExists(closure: (query: PostgresDriver) => void): this; | ||
| /** | ||
| * Set a having in statement in your query. | ||
| */ | ||
| havingIn(column: string, values: any[]): this; | ||
| /** | ||
| * Set a having not in statement in your query. | ||
| */ | ||
| havingNotIn(column: string, values: any[]): this; | ||
| /** | ||
| * Set a having between statement in your query. | ||
| */ | ||
| havingBetween(column: string, values: [any, any]): this; | ||
| /** | ||
| * Set a having not between statement in your query. | ||
| */ | ||
| havingNotBetween(column: string, values: [any, any]): this; | ||
| /** | ||
| * Set a having null statement in your query. | ||
| */ | ||
| havingNull(column: string): this; | ||
| /** | ||
| * Set a having not null statement in your query. | ||
| */ | ||
| havingNotNull(column: string): this; | ||
| orHaving(column: string): this; | ||
| orHaving(column: string, value: any): this; | ||
| orHaving(column: string, operation: Operations, value: any): this; | ||
| /** | ||
| * Set an or having raw statement in your query. | ||
| */ | ||
| orHavingRaw(sql: string, bindings?: any): this; | ||
| /** | ||
| * Set an or having exists statement in your query. | ||
| */ | ||
| orHavingExists(closure: (query: PostgresDriver) => void): this; | ||
| /** | ||
| * Set an or having not exists statement in your query. | ||
| */ | ||
| orHavingNotExists(closure: (query: PostgresDriver) => void): this; | ||
| /** | ||
| * Set an or having in statement in your query. | ||
| */ | ||
| orHavingIn(column: string, values: any[]): this; | ||
| /** | ||
| * Set an or having not in statement in your query. | ||
| */ | ||
| orHavingNotIn(column: string, values: any[]): this; | ||
| /** | ||
| * Set an or having between statement in your query. | ||
| */ | ||
| orHavingBetween(column: string, values: [any, any]): this; | ||
| /** | ||
| * Set an or having not between statement in your query. | ||
| */ | ||
| orHavingNotBetween(column: string, values: [any, any]): this; | ||
| /** | ||
| * Set an or having null statement in your query. | ||
| */ | ||
| orHavingNull(column: string): this; | ||
| /** | ||
| * Set an or having not null statement in your query. | ||
| */ | ||
| orHavingNotNull(column: string): this; | ||
| where(statement: Record<string, any>): this; | ||
| where(key: string, value: any): this; | ||
| where(key: string, operation: Operations, value: any): this; | ||
| whereNot(statement: Record<string, any>): this; | ||
| whereNot(key: string, value: any): this; | ||
| /** | ||
| * Set a where raw statement in your query. | ||
| */ | ||
| whereRaw(sql: string, bindings?: any): this; | ||
| /** | ||
| * Set a where exists statement in your query. | ||
| */ | ||
| whereExists(closure: (query: PostgresDriver) => void): this; | ||
| /** | ||
| * Set a where not exists statement in your query. | ||
| */ | ||
| whereNotExists(closure: (query: PostgresDriver) => void): this; | ||
| /** | ||
| * Set a where like statement in your query. | ||
| */ | ||
| whereLike(column: string, value: any): this; | ||
| /** | ||
| * Set a where ILike statement in your query. | ||
| */ | ||
| whereILike(column: string, value: any): this; | ||
| /** | ||
| * Set a where in statement in your query. | ||
| */ | ||
| whereIn(column: string, values: any[]): this; | ||
| /** | ||
| * Set a where not in statement in your query. | ||
| */ | ||
| whereNotIn(column: string, values: any[]): this; | ||
| /** | ||
| * Set a where between statement in your query. | ||
| */ | ||
| whereBetween(column: string, values: [any, any]): this; | ||
| /** | ||
| * Set a where not between statement in your query. | ||
| */ | ||
| whereNotBetween(column: string, values: [any, any]): this; | ||
| /** | ||
| * Set a where null statement in your query. | ||
| */ | ||
| whereNull(column: string): this; | ||
| /** | ||
| * Set a where not null statement in your query. | ||
| */ | ||
| whereNotNull(column: string): this; | ||
| orWhere(statement: Record<string, any>): this; | ||
| orWhere(key: string, value: any): this; | ||
| orWhere(key: string, operation: Operations, value: any): this; | ||
| orWhereNot(statement: Record<string, any>): this; | ||
| orWhereNot(key: string, value: any): this; | ||
| /** | ||
| * Set a or where raw statement in your query. | ||
| */ | ||
| orWhereRaw(sql: string, bindings?: any): this; | ||
| /** | ||
| * Set an or where exists statement in your query. | ||
| */ | ||
| orWhereExists(closure: (query: PostgresDriver) => void): this; | ||
| /** | ||
| * Set an or where not exists statement in your query. | ||
| */ | ||
| orWhereNotExists(closure: (query: PostgresDriver) => void): this; | ||
| /** | ||
| * Set an or where like statement in your query. | ||
| */ | ||
| orWhereLike(column: string, value: any): this; | ||
| /** | ||
| * Set an or where ILike statement in your query. | ||
| */ | ||
| orWhereILike(column: string, value: any): this; | ||
| /** | ||
| * Set an or where in statement in your query. | ||
| */ | ||
| orWhereIn(column: string, values: any[]): this; | ||
| /** | ||
| * Set an or where not in statement in your query. | ||
| */ | ||
| orWhereNotIn(column: string, values: any[]): this; | ||
| /** | ||
| * Set an or where between statement in your query. | ||
| */ | ||
| orWhereBetween(column: string, values: [any, any]): this; | ||
| /** | ||
| * Set an or where not between statement in your query. | ||
| */ | ||
| orWhereNotBetween(column: string, values: [any, any]): this; | ||
| /** | ||
| * Set an or where null statement in your query. | ||
| */ | ||
| orWhereNull(column: string): this; | ||
| /** | ||
| * Set an or where not null statement in your query. | ||
| */ | ||
| orWhereNotNull(column: string): this; | ||
| /** | ||
| * Set an order by statement in your query. | ||
| */ | ||
| orderBy(column: string, direction?: Direction): this; | ||
| /** | ||
| * Set an order by raw statement in your query. | ||
| */ | ||
| orderByRaw(sql: string, bindings?: any): this; | ||
| /** | ||
| * Order the results easily by the latest date. By default, the result will | ||
| * be ordered by the table's "createdAt" column. | ||
| */ | ||
| latest(column?: string): this; | ||
| /** | ||
| * Order the results easily by the oldest date. By default, the result will | ||
| * be ordered by the table's "createdAt" column. | ||
| */ | ||
| oldest(column?: string): this; | ||
| /** | ||
| * Set the skip number in your query. | ||
| */ | ||
| offset(number: number): this; | ||
| /** | ||
| * Set the limit number in your query. | ||
| */ | ||
| limit(number: number): this; | ||
| private getJsonPathOperator; | ||
| } |
@@ -1,2 +0,1 @@ | ||
| /* eslint-disable @typescript-eslint/ban-ts-comment */ | ||
| /** | ||
@@ -10,13 +9,10 @@ * @athenna/database | ||
| */ | ||
| import { Exec, Is, Json, Options } from '@athenna/common'; | ||
| import { debug } from '#src/debug'; | ||
| import { Log } from '@athenna/logger'; | ||
| import { Driver } from '#src/database/drivers/Driver'; | ||
| import { Is, Json, Options } from '@athenna/common'; | ||
| import { ConnectionFactory } from '#src/factories/ConnectionFactory'; | ||
| import { Transaction } from '#src/database/transactions/Transaction'; | ||
| import { MigrationSource } from '#src/database/migrations/MigrationSource'; | ||
| import { BaseKnexDriver } from '#src/database/drivers/BaseKnexDriver'; | ||
| import { WrongMethodException } from '#src/exceptions/WrongMethodException'; | ||
| import { PROTECTED_QUERY_METHODS } from '#src/constants/ProtectedQueryMethods'; | ||
| import { NotConnectedDatabaseException } from '#src/exceptions/NotConnectedDatabaseException'; | ||
| export class PostgresDriver extends Driver { | ||
| import { EmptyColumnException } from '#src/exceptions/EmptyColumnException'; | ||
| export class PostgresDriver extends BaseKnexDriver { | ||
| /** | ||
@@ -80,71 +76,2 @@ * Connect to database. | ||
| /** | ||
| * Creates a new instance of query builder. | ||
| */ | ||
| query() { | ||
| if (!this.isConnected) { | ||
| throw new NotConnectedDatabaseException(); | ||
| } | ||
| const query = this.useSetQB | ||
| ? this.qb.table(this.tableName) | ||
| : this.client.queryBuilder().table(this.tableName); | ||
| const handler = { | ||
| get: (target, propertyKey) => { | ||
| if (PROTECTED_QUERY_METHODS.includes(propertyKey)) { | ||
| this.qb = this.query(); | ||
| } | ||
| return target[propertyKey]; | ||
| } | ||
| }; | ||
| return new Proxy(query, handler); | ||
| } | ||
| /** | ||
| * Sync a model schema with database. | ||
| */ | ||
| async sync() { | ||
| debug(`database sync with ${PostgresDriver.name} is not available yet, use migration instead.`); | ||
| } | ||
| /** | ||
| * Create a new transaction. | ||
| */ | ||
| async startTransaction() { | ||
| const trx = await this.client.transaction(); | ||
| return new Transaction(this.clone().setClient(trx)); | ||
| } | ||
| /** | ||
| * Commit the transaction. | ||
| */ | ||
| async commitTransaction() { | ||
| const client = this.client; | ||
| await client.commit(); | ||
| this.tableName = null; | ||
| this.client = null; | ||
| this.isConnected = false; | ||
| } | ||
| /** | ||
| * Rollback the transaction. | ||
| */ | ||
| async rollbackTransaction() { | ||
| const client = this.client; | ||
| await client.rollback(); | ||
| this.tableName = null; | ||
| this.client = null; | ||
| this.isConnected = false; | ||
| } | ||
| /** | ||
| * Run database migrations. | ||
| */ | ||
| async runMigrations() { | ||
| await this.client.migrate.latest({ | ||
| migrationSource: new MigrationSource(this.connection) | ||
| }); | ||
| } | ||
| /** | ||
| * Revert database migrations. | ||
| */ | ||
| async revertMigrations() { | ||
| await this.client.migrate.rollback({ | ||
| migrationSource: new MigrationSource(this.connection) | ||
| }); | ||
| } | ||
| /** | ||
| * List all databases available. | ||
@@ -157,15 +84,2 @@ */ | ||
| /** | ||
| * Get the current database name. | ||
| */ | ||
| async getCurrentDatabase() { | ||
| return this.client.client.database(); | ||
| } | ||
| /** | ||
| * Verify if database exists. | ||
| */ | ||
| async hasDatabase(database) { | ||
| const databases = await this.getDatabases(); | ||
| return databases.includes(database); | ||
| } | ||
| /** | ||
| * Create a new database. | ||
@@ -202,26 +116,2 @@ */ | ||
| /** | ||
| * Verify if table exists. | ||
| */ | ||
| async hasTable(table) { | ||
| return this.client.schema.hasTable(table); | ||
| } | ||
| /** | ||
| * Create a new table in database. | ||
| */ | ||
| async createTable(table, closure) { | ||
| await this.client.schema.createTable(table, closure); | ||
| } | ||
| /** | ||
| * Alter a table in database. | ||
| */ | ||
| async alterTable(table, closure) { | ||
| await this.client.schema.alterTable(table, closure); | ||
| } | ||
| /** | ||
| * Drop a table in database. | ||
| */ | ||
| async dropTable(table) { | ||
| await this.client.schema.dropTableIfExists(table); | ||
| } | ||
| /** | ||
| * Remove all data inside some database table | ||
@@ -234,117 +124,2 @@ * and restart the identity of the table. | ||
| /** | ||
| * Make a raw query in database. | ||
| */ | ||
| raw(sql, bindings) { | ||
| return this.client.raw(sql, bindings); | ||
| } | ||
| /** | ||
| * Calculate the average of a given column. | ||
| */ | ||
| async avg(column) { | ||
| const [{ avg }] = await this.qb.avg({ avg: column }); | ||
| return avg; | ||
| } | ||
| /** | ||
| * Calculate the average of a given column using distinct. | ||
| */ | ||
| async avgDistinct(column) { | ||
| const [{ avg }] = await this.qb.avgDistinct({ avg: column }); | ||
| return avg; | ||
| } | ||
| /** | ||
| * Get the max number of a given column. | ||
| */ | ||
| async max(column) { | ||
| const [{ max }] = await this.qb.max({ max: column }); | ||
| return max; | ||
| } | ||
| /** | ||
| * Get the min number of a given column. | ||
| */ | ||
| async min(column) { | ||
| const [{ min }] = await this.qb.min({ min: column }); | ||
| return min; | ||
| } | ||
| /** | ||
| * Sum all numbers of a given column. | ||
| */ | ||
| async sum(column) { | ||
| const [{ sum }] = await this.qb.sum({ sum: column }); | ||
| return sum; | ||
| } | ||
| /** | ||
| * Sum all numbers of a given column in distinct mode. | ||
| */ | ||
| async sumDistinct(column) { | ||
| const [{ sum }] = await this.qb.sumDistinct({ sum: column }); | ||
| return sum; | ||
| } | ||
| /** | ||
| * Increment a value of a given column. | ||
| */ | ||
| async increment(column) { | ||
| await this.qb.increment(column); | ||
| } | ||
| /** | ||
| * Decrement a value of a given column. | ||
| */ | ||
| async decrement(column) { | ||
| await this.qb.decrement(column); | ||
| } | ||
| /** | ||
| * Calculate the average of a given column using distinct. | ||
| */ | ||
| async count(column = '*') { | ||
| const [{ count }] = await this.qb.count({ count: column }); | ||
| return `${count}`; | ||
| } | ||
| /** | ||
| * Calculate the average of a given column using distinct. | ||
| */ | ||
| async countDistinct(column) { | ||
| const [{ count }] = await this.qb.countDistinct({ count: column }); | ||
| return `${count}`; | ||
| } | ||
| /** | ||
| * Find a value in database. | ||
| */ | ||
| async find() { | ||
| return this.qb.first(); | ||
| } | ||
| /** | ||
| * Find many values in database. | ||
| */ | ||
| async findMany() { | ||
| const data = await this.qb; | ||
| this.qb = this.query(); | ||
| return data; | ||
| } | ||
| /** | ||
| * Find many values in database and return as paginated response. | ||
| */ | ||
| async paginate(page = { page: 0, limit: 10, resourceUrl: '/' }, limit = 10, resourceUrl = '/') { | ||
| if (Is.Number(page)) { | ||
| page = { page, limit, resourceUrl }; | ||
| } | ||
| const [{ count }] = await this.qb | ||
| .clone() | ||
| .clearOrder() | ||
| .clearSelect() | ||
| .count({ count: '*' }); | ||
| const data = await this.offset(page.page * page.limit) | ||
| .limit(page.limit) | ||
| .findMany(); | ||
| return Exec.pagination(data, parseInt(count), page); | ||
| } | ||
| /** | ||
| * Create a value in database. | ||
| */ | ||
| async create(data = {}) { | ||
| if (Is.Array(data)) { | ||
| throw new WrongMethodException('create', 'createMany'); | ||
| } | ||
| const created = await this.createMany([data]); | ||
| return created[0]; | ||
| } | ||
| /** | ||
| * Create many values in database. | ||
@@ -356,615 +131,100 @@ */ | ||
| } | ||
| return this.qb.insert(data, '*'); | ||
| const preparedData = data.map(data => this.prepareInsert(data)); | ||
| return this.qb.insert(preparedData, '*'); | ||
| } | ||
| /** | ||
| * Create data or update if already exists. | ||
| * Set a where json statement in your query. | ||
| */ | ||
| async createOrUpdate(data = {}) { | ||
| const query = this.qb.clone(); | ||
| const hasValue = await query.first(); | ||
| if (hasValue) { | ||
| await this.qb | ||
| .where(this.primaryKey, hasValue[this.primaryKey]) | ||
| .update(data); | ||
| return this.where(this.primaryKey, hasValue[this.primaryKey]).find(); | ||
| whereJson(column, operator, value) { | ||
| if (Is.Undefined(column) || !Is.String(column)) { | ||
| throw new EmptyColumnException('whereJson'); | ||
| } | ||
| return this.create(data); | ||
| } | ||
| /** | ||
| * Update a value in database. | ||
| */ | ||
| async update(data) { | ||
| await this.qb.clone().update(data); | ||
| const result = await this.findMany(); | ||
| if (result.length === 1) { | ||
| return result[0]; | ||
| const parsed = this.parseJsonSelector(column); | ||
| if (!parsed) { | ||
| throw new Error(`Invalid JSON selector: ${column}`); | ||
| } | ||
| return result; | ||
| } | ||
| /** | ||
| * Delete one value in database. | ||
| */ | ||
| async delete() { | ||
| await this.qb.delete(); | ||
| } | ||
| /** | ||
| * Set the table that this query will be executed. | ||
| */ | ||
| table(table) { | ||
| if (!this.isConnected) { | ||
| throw new NotConnectedDatabaseException(); | ||
| if (!parsed.path.includes('*')) { | ||
| return super.whereJson(column, operator, value); | ||
| } | ||
| this.tableName = table; | ||
| this.qb = this.query(); | ||
| const path = this.parseJsonSelectorToWildcardPath(parsed.path); | ||
| const normalized = this.normalizeJsonOperation(operator, value); | ||
| this.qb.whereRaw('jsonb_path_exists(??, ?::jsonpath, ?::jsonb)', [ | ||
| parsed.column, | ||
| `${path} ? (@ ${normalized.operator} $value)`, | ||
| JSON.stringify({ value: normalized.value }) | ||
| ]); | ||
| return this; | ||
| } | ||
| /** | ||
| * Log in console the actual query built. | ||
| * Set an or where json statement in your query. | ||
| */ | ||
| dump() { | ||
| console.log(this.qb.toSQL().toNative()); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set the columns that should be selected on query. | ||
| */ | ||
| select(...columns) { | ||
| this.qb.select(...columns); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set the columns that should be selected on query raw. | ||
| */ | ||
| selectRaw(sql, bindings) { | ||
| return this.select(this.raw(sql, bindings)); | ||
| } | ||
| /** | ||
| * Set the table that should be used on query. | ||
| * Different from `table()` method, this method | ||
| * doesn't change the driver table. | ||
| */ | ||
| from(table) { | ||
| this.qb.from(table); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set the table that should be used on query raw. | ||
| * Different from `table()` method, this method | ||
| * doesn't change the driver table. | ||
| */ | ||
| fromRaw(sql, bindings) { | ||
| return this.from(this.raw(sql, bindings)); | ||
| } | ||
| /** | ||
| * Set a join statement in your query. | ||
| */ | ||
| join(table, column1, operation, column2) { | ||
| return this.joinByType('join', table, column1, operation, column2); | ||
| } | ||
| /** | ||
| * Set a left join statement in your query. | ||
| */ | ||
| leftJoin(table, column1, operation, column2) { | ||
| return this.joinByType('leftJoin', table, column1, operation, column2); | ||
| } | ||
| /** | ||
| * Set a right join statement in your query. | ||
| */ | ||
| rightJoin(table, column1, operation, column2) { | ||
| return this.joinByType('rightJoin', table, column1, operation, column2); | ||
| } | ||
| /** | ||
| * Set a cross join statement in your query. | ||
| */ | ||
| crossJoin(table, column1, operation, column2) { | ||
| return this.joinByType('crossJoin', table, column1, operation, column2); | ||
| } | ||
| /** | ||
| * Set a full outer join statement in your query. | ||
| */ | ||
| fullOuterJoin(table, column1, operation, column2) { | ||
| return this.joinByType('fullOuterJoin', table, column1, operation, column2); | ||
| } | ||
| /** | ||
| * Set a left outer join statement in your query. | ||
| */ | ||
| leftOuterJoin(table, column1, operation, column2) { | ||
| return this.joinByType('leftOuterJoin', table, column1, operation, column2); | ||
| } | ||
| /** | ||
| * Set a right outer join statement in your query. | ||
| */ | ||
| rightOuterJoin(table, column1, operation, column2) { | ||
| return this.joinByType('rightOuterJoin', table, column1, operation, column2); | ||
| } | ||
| /** | ||
| * Set a join raw statement in your query. | ||
| */ | ||
| joinRaw(sql, bindings) { | ||
| this.qb.joinRaw(sql, bindings); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a group by statement in your query. | ||
| */ | ||
| groupBy(...columns) { | ||
| this.qb.groupBy(...columns); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a group by raw statement in your query. | ||
| */ | ||
| groupByRaw(sql, bindings) { | ||
| this.qb.groupByRaw(sql, bindings); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a having statement in your query. | ||
| */ | ||
| having(column, operation, value) { | ||
| if (operation === undefined) { | ||
| this.qb.having(column); | ||
| return this; | ||
| orWhereJson(column, operator, value) { | ||
| if (Is.Undefined(column) || !Is.String(column)) { | ||
| throw new EmptyColumnException('orWhereJson'); | ||
| } | ||
| if (value === undefined) { | ||
| this.qb.having(column, '=', operation); | ||
| return this; | ||
| const parsed = this.parseJsonSelector(column); | ||
| if (!parsed) { | ||
| throw new Error(`Invalid JSON selector: ${column}`); | ||
| } | ||
| this.qb.having(column, operation, value); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a having raw statement in your query. | ||
| */ | ||
| havingRaw(sql, bindings) { | ||
| this.qb.havingRaw(sql, bindings); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a having exists statement in your query. | ||
| */ | ||
| havingExists(closure) { | ||
| const driver = this.clone(); | ||
| // @ts-ignore | ||
| this.qb.havingExists(function () { | ||
| closure(driver.setQueryBuilder(this, { useSetQB: true })); | ||
| }); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a having not exists statement in your query. | ||
| */ | ||
| havingNotExists(closure) { | ||
| const driver = this.clone(); | ||
| // @ts-ignore | ||
| this.qb.havingNotExists(function () { | ||
| closure(driver.setQueryBuilder(this, { useSetQB: true })); | ||
| }); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a having in statement in your query. | ||
| */ | ||
| havingIn(column, values) { | ||
| this.qb.havingIn(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a having not in statement in your query. | ||
| */ | ||
| havingNotIn(column, values) { | ||
| this.qb.havingNotIn(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a having between statement in your query. | ||
| */ | ||
| havingBetween(column, values) { | ||
| this.qb.havingBetween(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a having not between statement in your query. | ||
| */ | ||
| havingNotBetween(column, values) { | ||
| this.qb.havingNotBetween(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a having null statement in your query. | ||
| */ | ||
| havingNull(column) { | ||
| this.qb.havingNull(column); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a having not null statement in your query. | ||
| */ | ||
| havingNotNull(column) { | ||
| this.qb.havingNotNull(column); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or having statement in your query. | ||
| */ | ||
| orHaving(column, operation, value) { | ||
| if (operation === undefined) { | ||
| this.qb.orHaving(column); | ||
| return this; | ||
| if (!parsed.path.includes('*')) { | ||
| return super.orWhereJson(column, operator, value); | ||
| } | ||
| if (value === undefined) { | ||
| this.qb.orHaving(column, '=', operation); | ||
| return this; | ||
| } | ||
| this.qb.orHaving(column, operation, value); | ||
| const path = this.parseJsonSelectorToWildcardPath(parsed.path); | ||
| const normalized = this.normalizeJsonOperation(operator, value); | ||
| this.qb.orWhereRaw('jsonb_path_exists(??, ?::jsonpath, ?::jsonb)', [ | ||
| parsed.column, | ||
| `${path} ? (@ ${normalized.operator} $value)`, | ||
| JSON.stringify({ value: normalized.value }) | ||
| ]); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or having raw statement in your query. | ||
| * Convert a json selector path to a valid postgres json path. | ||
| */ | ||
| orHavingRaw(sql, bindings) { | ||
| this.qb.orHavingRaw(sql, bindings); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or having exists statement in your query. | ||
| */ | ||
| orHavingExists(closure) { | ||
| const driver = this.clone(); | ||
| // @ts-ignore | ||
| this.qb.orHavingExists(function () { | ||
| closure(driver.setQueryBuilder(this, { useSetQB: true })); | ||
| }); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or having not exists statement in your query. | ||
| */ | ||
| orHavingNotExists(closure) { | ||
| const driver = this.clone(); | ||
| // @ts-ignore | ||
| this.qb.orHavingNotExists(function () { | ||
| closure(driver.setQueryBuilder(this, { useSetQB: true })); | ||
| }); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or having in statement in your query. | ||
| */ | ||
| orHavingIn(column, values) { | ||
| // @ts-ignore | ||
| this.qb.orHavingIn(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or having not in statement in your query. | ||
| */ | ||
| orHavingNotIn(column, values) { | ||
| this.qb.orHavingNotIn(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or having between statement in your query. | ||
| */ | ||
| orHavingBetween(column, values) { | ||
| this.qb.orHavingBetween(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or having not between statement in your query. | ||
| */ | ||
| orHavingNotBetween(column, values) { | ||
| this.qb.orHavingNotBetween(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or having null statement in your query. | ||
| */ | ||
| orHavingNull(column) { | ||
| // @ts-ignore | ||
| this.qb.orHavingNull(column); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or having not null statement in your query. | ||
| */ | ||
| orHavingNotNull(column) { | ||
| // @ts-ignore | ||
| this.qb.orHavingNotNull(column); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a where statement in your query. | ||
| */ | ||
| where(statement, operation, value) { | ||
| if (Is.Function(statement)) { | ||
| const driver = this.clone(); | ||
| this.qb.where(function () { | ||
| statement(driver.setQueryBuilder(this, { useSetQB: true })); | ||
| }); | ||
| return this; | ||
| } | ||
| if (operation === undefined) { | ||
| if (Is.Array(statement)) { | ||
| throw new Error('Arrays as statement are not supported.'); | ||
| parseJsonSelectorToWildcardPath(path) { | ||
| const parts = path | ||
| .split('->') | ||
| .map(part => part.trim()) | ||
| .filter(Boolean); | ||
| return parts.reduce((jsonPath, part) => { | ||
| if (part === '*') { | ||
| return `${jsonPath}[*]`; | ||
| } | ||
| if (Is.String(statement)) { | ||
| throw new Error(`The value for the "${statement}" column is undefined and where will not work.`); | ||
| if (/^\d+$/.test(part)) { | ||
| return `${jsonPath}[${part}]`; | ||
| } | ||
| this.qb.where(statement); | ||
| return this; | ||
| } | ||
| if (value === undefined) { | ||
| this.qb.where(statement, operation); | ||
| return this; | ||
| } | ||
| this.qb.where(statement, operation, value); | ||
| return this; | ||
| return `${jsonPath}.${part}`; | ||
| }, '$'); | ||
| } | ||
| /** | ||
| * Set a where not statement in your query. | ||
| * Normalize operator/value pair for postgres json path comparisons. | ||
| */ | ||
| whereNot(statement, value) { | ||
| if (Is.Function(statement)) { | ||
| const driver = this.clone(); | ||
| this.qb.whereNot(function () { | ||
| statement(driver.setQueryBuilder(this, { useSetQB: true })); | ||
| }); | ||
| return this; | ||
| normalizeJsonOperation(operator, value) { | ||
| if (Is.Undefined(value)) { | ||
| return { | ||
| operator: '==', | ||
| value: operator | ||
| }; | ||
| } | ||
| if (value === undefined) { | ||
| this.qb.whereNot(statement); | ||
| return this; | ||
| } | ||
| this.qb.whereNot(statement, value); | ||
| return this; | ||
| return { | ||
| operator: this.getJsonPathOperator(operator), | ||
| value | ||
| }; | ||
| } | ||
| /** | ||
| * Set a where raw statement in your query. | ||
| * Convert query operators to postgres json path operators. | ||
| */ | ||
| whereRaw(sql, bindings) { | ||
| this.qb.whereRaw(sql, bindings); | ||
| return this; | ||
| getJsonPathOperator(operator) { | ||
| const operators = { | ||
| '=': '==', | ||
| '==': '==', | ||
| '!=': '!=', | ||
| '<>': '!=', | ||
| '>': '>', | ||
| '>=': '>=', | ||
| '<': '<', | ||
| '<=': '<=' | ||
| }; | ||
| return operators[operator] || operator; | ||
| } | ||
| /** | ||
| * Set a where exists statement in your query. | ||
| */ | ||
| whereExists(closure) { | ||
| const driver = this.clone(); | ||
| this.qb.whereExists(function () { | ||
| closure(driver.setQueryBuilder(this, { useSetQB: true })); | ||
| }); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a where not exists statement in your query. | ||
| */ | ||
| whereNotExists(closure) { | ||
| const driver = this.clone(); | ||
| this.qb.whereNotExists(function () { | ||
| closure(driver.setQueryBuilder(this, { useSetQB: true })); | ||
| }); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a where like statement in your query. | ||
| */ | ||
| whereLike(column, value) { | ||
| this.qb.whereLike(column, value); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a where ILike statement in your query. | ||
| */ | ||
| whereILike(column, value) { | ||
| this.qb.whereILike(column, value); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a where in statement in your query. | ||
| */ | ||
| whereIn(column, values) { | ||
| this.qb.whereIn(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a where not in statement in your query. | ||
| */ | ||
| whereNotIn(column, values) { | ||
| this.qb.whereNotIn(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a where between statement in your query. | ||
| */ | ||
| whereBetween(column, values) { | ||
| this.qb.whereBetween(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a where not between statement in your query. | ||
| */ | ||
| whereNotBetween(column, values) { | ||
| this.qb.whereNotBetween(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a where null statement in your query. | ||
| */ | ||
| whereNull(column) { | ||
| this.qb.whereNull(column); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a where not null statement in your query. | ||
| */ | ||
| whereNotNull(column) { | ||
| this.qb.whereNotNull(column); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a or where statement in your query. | ||
| */ | ||
| orWhere(statement, operation, value) { | ||
| if (Is.Function(statement)) { | ||
| const driver = this.clone(); | ||
| this.qb.orWhere(function () { | ||
| statement(driver.setQueryBuilder(this, { useSetQB: true })); | ||
| }); | ||
| return this; | ||
| } | ||
| if (operation === undefined) { | ||
| this.qb.orWhere(statement); | ||
| return this; | ||
| } | ||
| if (value === undefined) { | ||
| this.qb.orWhere(statement, operation); | ||
| return this; | ||
| } | ||
| this.qb.orWhere(statement, operation, value); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or where not statement in your query. | ||
| */ | ||
| orWhereNot(statement, value) { | ||
| if (Is.Function(statement)) { | ||
| const driver = this.clone(); | ||
| this.qb.orWhereNot(function () { | ||
| statement(driver.setQueryBuilder(this, { useSetQB: true })); | ||
| }); | ||
| return this; | ||
| } | ||
| if (value === undefined) { | ||
| this.qb.orWhereNot(statement); | ||
| return this; | ||
| } | ||
| this.qb.orWhereNot(statement, value); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a or where raw statement in your query. | ||
| */ | ||
| orWhereRaw(sql, bindings) { | ||
| this.qb.orWhereRaw(sql, bindings); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or where exists statement in your query. | ||
| */ | ||
| orWhereExists(closure) { | ||
| const driver = this.clone(); | ||
| this.qb.orWhereExists(function () { | ||
| closure(driver.setQueryBuilder(this, { useSetQB: true })); | ||
| }); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or where not exists statement in your query. | ||
| */ | ||
| orWhereNotExists(closure) { | ||
| const driver = this.clone(); | ||
| this.qb.orWhereNotExists(function () { | ||
| closure(driver.setQueryBuilder(this, { useSetQB: true })); | ||
| }); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or where like statement in your query. | ||
| */ | ||
| orWhereLike(column, value) { | ||
| this.qb.orWhereLike(column, value); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or where ILike statement in your query. | ||
| */ | ||
| orWhereILike(column, value) { | ||
| this.qb.orWhereILike(column, value); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or where in statement in your query. | ||
| */ | ||
| orWhereIn(column, values) { | ||
| this.qb.orWhereIn(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or where not in statement in your query. | ||
| */ | ||
| orWhereNotIn(column, values) { | ||
| this.qb.orWhereNotIn(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or where between statement in your query. | ||
| */ | ||
| orWhereBetween(column, values) { | ||
| this.qb.orWhereBetween(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or where not between statement in your query. | ||
| */ | ||
| orWhereNotBetween(column, values) { | ||
| this.qb.orWhereNotBetween(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or where null statement in your query. | ||
| */ | ||
| orWhereNull(column) { | ||
| this.qb.orWhereNull(column); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or where not null statement in your query. | ||
| */ | ||
| orWhereNotNull(column) { | ||
| this.qb.orWhereNotNull(column); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an order by statement in your query. | ||
| */ | ||
| orderBy(column, direction = 'ASC') { | ||
| this.qb.orderBy(column, direction.toUpperCase()); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an order by raw statement in your query. | ||
| */ | ||
| orderByRaw(sql, bindings) { | ||
| this.qb.orderByRaw(sql, bindings); | ||
| return this; | ||
| } | ||
| /** | ||
| * Order the results easily by the latest date. By default, the result will | ||
| * be ordered by the table's "createdAt" column. | ||
| */ | ||
| latest(column = 'createdAt') { | ||
| return this.orderBy(column, 'DESC'); | ||
| } | ||
| /** | ||
| * Order the results easily by the oldest date. By default, the result will | ||
| * be ordered by the table's "createdAt" column. | ||
| */ | ||
| oldest(column = 'createdAt') { | ||
| return this.orderBy(column, 'ASC'); | ||
| } | ||
| /** | ||
| * Set the skip number in your query. | ||
| */ | ||
| offset(number) { | ||
| this.qb.offset(number); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set the limit number in your query. | ||
| */ | ||
| limit(number) { | ||
| this.qb.limit(number); | ||
| return this; | ||
| } | ||
| } |
@@ -9,8 +9,6 @@ /** | ||
| */ | ||
| import { type PaginatedResponse, type PaginationOptions } from '@athenna/common'; | ||
| import type { Knex } from 'knex'; | ||
| import { Driver } from '#src/database/drivers/Driver'; | ||
| import { Transaction } from '#src/database/transactions/Transaction'; | ||
| import type { ConnectionOptions, Direction, Operations } from '#src/types'; | ||
| export declare class SqliteDriver extends Driver<Knex, Knex.QueryBuilder> { | ||
| import type { Operations } from '#src/types'; | ||
| import { BaseKnexDriver } from '#src/database/drivers/BaseKnexDriver'; | ||
| import type { ConnectionOptions } from '#src/types/ConnectionOptions'; | ||
| export declare class SqliteDriver extends BaseKnexDriver { | ||
| /** | ||
@@ -21,34 +19,2 @@ * Connect to database. | ||
| /** | ||
| * Close the connection with database in this instance. | ||
| */ | ||
| close(): Promise<void>; | ||
| /** | ||
| * Creates a new instance of query builder. | ||
| */ | ||
| query(): Knex.QueryBuilder; | ||
| /** | ||
| * Sync a model schema with database. | ||
| */ | ||
| sync(): Promise<void>; | ||
| /** | ||
| * Create a new transaction. | ||
| */ | ||
| startTransaction(): Promise<Transaction<Knex.Transaction, Knex.QueryBuilder>>; | ||
| /** | ||
| * Commit the transaction. | ||
| */ | ||
| commitTransaction(): Promise<void>; | ||
| /** | ||
| * Rollback the transaction. | ||
| */ | ||
| rollbackTransaction(): Promise<void>; | ||
| /** | ||
| * Run database migrations. | ||
| */ | ||
| runMigrations(): Promise<void>; | ||
| /** | ||
| * Revert database migrations. | ||
| */ | ||
| revertMigrations(): Promise<void>; | ||
| /** | ||
| * List all databases available. | ||
@@ -58,10 +24,2 @@ */ | ||
| /** | ||
| * Get the current database name. | ||
| */ | ||
| getCurrentDatabase(): Promise<string | undefined>; | ||
| /** | ||
| * Verify if database exists. | ||
| */ | ||
| hasDatabase(database: string): Promise<boolean>; | ||
| /** | ||
| * Create a new database. | ||
@@ -79,18 +37,2 @@ */ | ||
| /** | ||
| * Verify if table exists. | ||
| */ | ||
| hasTable(table: string): Promise<boolean>; | ||
| /** | ||
| * Create a new table in database. | ||
| */ | ||
| createTable(table: string, closure: (builder: Knex.TableBuilder) => void | Promise<void>): Promise<void>; | ||
| /** | ||
| * Alter a table in database. | ||
| */ | ||
| alterTable(table: string, closure: (builder: Knex.TableBuilder) => void | Promise<void>): Promise<void>; | ||
| /** | ||
| * Drop a table in database. | ||
| */ | ||
| dropTable(table: string): Promise<void>; | ||
| /** | ||
| * Remove all data inside some database table | ||
@@ -101,46 +43,6 @@ * and restart the identity of the table. | ||
| /** | ||
| * Make a raw query in database. | ||
| * Create many values in database. | ||
| */ | ||
| raw<T = any>(sql: string, bindings?: any): T; | ||
| createMany<T = any>(data?: Partial<T>[]): Promise<T[]>; | ||
| /** | ||
| * Calculate the average of a given column. | ||
| */ | ||
| avg(column: string): Promise<string>; | ||
| /** | ||
| * Calculate the average of a given column using distinct. | ||
| */ | ||
| avgDistinct(column: string): Promise<string>; | ||
| /** | ||
| * Get the max number of a given column. | ||
| */ | ||
| max(column: string): Promise<string>; | ||
| /** | ||
| * Get the min number of a given column. | ||
| */ | ||
| min(column: string): Promise<string>; | ||
| /** | ||
| * Sum all numbers of a given column. | ||
| */ | ||
| sum(column: string): Promise<string>; | ||
| /** | ||
| * Sum all numbers of a given column in distinct mode. | ||
| */ | ||
| sumDistinct(column: string): Promise<string>; | ||
| /** | ||
| * Increment a value of a given column. | ||
| */ | ||
| increment(column: string): Promise<void>; | ||
| /** | ||
| * Decrement a value of a given column. | ||
| */ | ||
| decrement(column: string): Promise<void>; | ||
| /** | ||
| * Calculate the average of a given column using distinct. | ||
| */ | ||
| count(column?: string): Promise<string>; | ||
| /** | ||
| * Calculate the average of a given column using distinct. | ||
| */ | ||
| countDistinct(column: string): Promise<string>; | ||
| /** | ||
| * Find a value in database. | ||
@@ -153,194 +55,31 @@ */ | ||
| findMany<T = any>(): Promise<T[]>; | ||
| whereJson(column: string, value: any): this; | ||
| whereJson(column: string, operation: Operations, value: any): this; | ||
| orWhereJson(column: string, value: any): this; | ||
| orWhereJson(column: string, operation: Operations, value: any): this; | ||
| /** | ||
| * Find many values in database and return as paginated response. | ||
| * Convert a json selector path to sqlite json path. | ||
| */ | ||
| paginate<T = any>(page?: PaginationOptions | number, limit?: number, resourceUrl?: string): Promise<PaginatedResponse<T>>; | ||
| private parseJsonSelectorToSqlitePath; | ||
| /** | ||
| * Create a value in database. | ||
| * Split a json selector around the wildcard. | ||
| */ | ||
| create<T = any>(data?: Partial<T>): Promise<T>; | ||
| private parseJsonSelectorToWildcardParts; | ||
| /** | ||
| * Create many values in database. | ||
| * Convert path parts to a valid json path. | ||
| */ | ||
| createMany<T = any>(data?: Partial<T>[]): Promise<T[]>; | ||
| private toJsonPath; | ||
| /** | ||
| * Create data or update if already exists. | ||
| * Normalize operator/value pairs from the whereJson overloads. | ||
| */ | ||
| createOrUpdate<T = any>(data?: Partial<T>): Promise<T | T[]>; | ||
| private normalizeJsonOperation; | ||
| /** | ||
| * Update a value in database. | ||
| * Normalize json strings returned by sqlite into arrays/objects. | ||
| */ | ||
| update<T = any>(data: Partial<T>): Promise<T | T[]>; | ||
| private normalizeRow; | ||
| /** | ||
| * Delete one value in database. | ||
| * Parse stringified json objects/arrays returned by sqlite. | ||
| */ | ||
| delete(): Promise<void>; | ||
| private normalizeJsonValue; | ||
| /** | ||
| * Set the table that this query will be executed. | ||
| */ | ||
| table(table: string): this; | ||
| /** | ||
| * Log in console the actual query built. | ||
| */ | ||
| dump(): this; | ||
| /** | ||
| * Set the columns that should be selected on query. | ||
| */ | ||
| select(...columns: string[]): this; | ||
| /** | ||
| * Set the columns that should be selected on query raw. | ||
| */ | ||
| selectRaw(sql: string, bindings?: any): this; | ||
| /** | ||
| * Set the table that should be used on query. | ||
| * Different from `table()` method, this method | ||
| * doesn't change the driver table. | ||
| */ | ||
| from(table: string): this; | ||
| /** | ||
| * Set the table that should be used on query raw. | ||
| * Different from `table()` method, this method | ||
| * doesn't change the driver table. | ||
| */ | ||
| fromRaw(sql: string, bindings?: any): this; | ||
| /** | ||
| * Set a join statement in your query. | ||
| */ | ||
| join(table: any, column1?: any, operation?: any | Operations, column2?: any): this; | ||
| /** | ||
| * Set a left join statement in your query. | ||
| */ | ||
| leftJoin(table: any, column1?: any, operation?: any | Operations, column2?: any): this; | ||
| /** | ||
| * Set a right join statement in your query. | ||
| */ | ||
| rightJoin(table: any, column1?: any, operation?: any | Operations, column2?: any): this; | ||
| /** | ||
| * Set a cross join statement in your query. | ||
| */ | ||
| crossJoin(table: any, column1?: any, operation?: any | Operations, column2?: any): this; | ||
| /** | ||
| * Set a full outer join statement in your query. | ||
| */ | ||
| fullOuterJoin(table: any, column1?: any, operation?: any | Operations, column2?: any): this; | ||
| /** | ||
| * Set a left outer join statement in your query. | ||
| */ | ||
| leftOuterJoin(table: any, column1?: any, operation?: any | Operations, column2?: any): this; | ||
| /** | ||
| * Set a right outer join statement in your query. | ||
| */ | ||
| rightOuterJoin(table: any, column1?: any, operation?: any | Operations, column2?: any): this; | ||
| /** | ||
| * Set a join raw statement in your query. | ||
| */ | ||
| joinRaw(sql: string, bindings?: any): this; | ||
| /** | ||
| * Set a group by statement in your query. | ||
| */ | ||
| groupBy(...columns: string[]): this; | ||
| /** | ||
| * Set a group by raw statement in your query. | ||
| */ | ||
| groupByRaw(sql: string, bindings?: any): this; | ||
| having(column: string): this; | ||
| having(column: string, value: any): this; | ||
| having(column: string, operation: Operations, value: any): this; | ||
| /** | ||
| * Set a having raw statement in your query. | ||
| */ | ||
| havingRaw(sql: string, bindings?: any): this; | ||
| /** | ||
| * Set a having exists statement in your query. | ||
| */ | ||
| havingExists(closure: (query: SqliteDriver) => void): this; | ||
| /** | ||
| * Set a having not exists statement in your query. | ||
| */ | ||
| havingNotExists(closure: (query: SqliteDriver) => void): this; | ||
| /** | ||
| * Set a having in statement in your query. | ||
| */ | ||
| havingIn(column: string, values: any[]): this; | ||
| /** | ||
| * Set a having not in statement in your query. | ||
| */ | ||
| havingNotIn(column: string, values: any[]): this; | ||
| /** | ||
| * Set a having between statement in your query. | ||
| */ | ||
| havingBetween(column: string, values: [any, any]): this; | ||
| /** | ||
| * Set a having not between statement in your query. | ||
| */ | ||
| havingNotBetween(column: string, values: [any, any]): this; | ||
| /** | ||
| * Set a having null statement in your query. | ||
| */ | ||
| havingNull(column: string): this; | ||
| /** | ||
| * Set a having not null statement in your query. | ||
| */ | ||
| havingNotNull(column: string): this; | ||
| orHaving(column: string): this; | ||
| orHaving(column: string, value: any): this; | ||
| orHaving(column: string, operation: Operations, value: any): this; | ||
| /** | ||
| * Set an or having raw statement in your query. | ||
| */ | ||
| orHavingRaw(sql: string, bindings?: any): this; | ||
| /** | ||
| * Set an or having exists statement in your query. | ||
| */ | ||
| orHavingExists(closure: (query: SqliteDriver) => void): this; | ||
| /** | ||
| * Set an or having not exists statement in your query. | ||
| */ | ||
| orHavingNotExists(closure: (query: SqliteDriver) => void): this; | ||
| /** | ||
| * Set an or having in statement in your query. | ||
| */ | ||
| orHavingIn(column: string, values: any[]): this; | ||
| /** | ||
| * Set an or having not in statement in your query. | ||
| */ | ||
| orHavingNotIn(column: string, values: any[]): this; | ||
| /** | ||
| * Set an or having between statement in your query. | ||
| */ | ||
| orHavingBetween(column: string, values: [any, any]): this; | ||
| /** | ||
| * Set an or having not between statement in your query. | ||
| */ | ||
| orHavingNotBetween(column: string, values: [any, any]): this; | ||
| /** | ||
| * Set an or having null statement in your query. | ||
| */ | ||
| orHavingNull(column: string): this; | ||
| /** | ||
| * Set an or having not null statement in your query. | ||
| */ | ||
| orHavingNotNull(column: string): this; | ||
| where(statement: Record<string, any>): this; | ||
| where(key: string, value: any): this; | ||
| where(key: string, operation: Operations, value: any): this; | ||
| whereNot(statement: Record<string, any>): this; | ||
| whereNot(key: string, value: any): this; | ||
| /** | ||
| * Set a where raw statement in your query. | ||
| */ | ||
| whereRaw(sql: string, bindings?: any): this; | ||
| /** | ||
| * Set a where exists statement in your query. | ||
| */ | ||
| whereExists(closure: (query: SqliteDriver) => void): this; | ||
| /** | ||
| * Set a where not exists statement in your query. | ||
| */ | ||
| whereNotExists(closure: (query: SqliteDriver) => void): this; | ||
| /** | ||
| * Set a where like statement in your query. | ||
| */ | ||
| whereLike(column: string, value: any): this; | ||
| /** | ||
| * Set a where ILike statement in your query. | ||
@@ -350,100 +89,5 @@ */ | ||
| /** | ||
| * Set a where in statement in your query. | ||
| * Set a where ILike statement in your query. | ||
| */ | ||
| whereIn(column: string, values: any[]): this; | ||
| /** | ||
| * Set a where not in statement in your query. | ||
| */ | ||
| whereNotIn(column: string, values: any[]): this; | ||
| /** | ||
| * Set a where between statement in your query. | ||
| */ | ||
| whereBetween(column: string, values: [any, any]): this; | ||
| /** | ||
| * Set a where not between statement in your query. | ||
| */ | ||
| whereNotBetween(column: string, values: [any, any]): this; | ||
| /** | ||
| * Set a where null statement in your query. | ||
| */ | ||
| whereNull(column: string): this; | ||
| /** | ||
| * Set a where not null statement in your query. | ||
| */ | ||
| whereNotNull(column: string): this; | ||
| orWhere(statement: Record<string, any>): this; | ||
| orWhere(key: string, value: any): this; | ||
| orWhere(key: string, operation: Operations, value: any): this; | ||
| orWhereNot(statement: Record<string, any>): this; | ||
| orWhereNot(key: string, value: any): this; | ||
| /** | ||
| * Set a or where raw statement in your query. | ||
| */ | ||
| orWhereRaw(sql: string, bindings?: any): this; | ||
| /** | ||
| * Set an or where exists statement in your query. | ||
| */ | ||
| orWhereExists(closure: (query: SqliteDriver) => void): this; | ||
| /** | ||
| * Set an or where not exists statement in your query. | ||
| */ | ||
| orWhereNotExists(closure: (query: SqliteDriver) => void): this; | ||
| /** | ||
| * Set an or where like statement in your query. | ||
| */ | ||
| orWhereLike(column: string, value: any): this; | ||
| /** | ||
| * Set an or where ILike statement in your query. | ||
| */ | ||
| orWhereILike(column: string, value: any): this; | ||
| /** | ||
| * Set an or where in statement in your query. | ||
| */ | ||
| orWhereIn(column: string, values: any[]): this; | ||
| /** | ||
| * Set an or where not in statement in your query. | ||
| */ | ||
| orWhereNotIn(column: string, values: any[]): this; | ||
| /** | ||
| * Set an or where between statement in your query. | ||
| */ | ||
| orWhereBetween(column: string, values: [any, any]): this; | ||
| /** | ||
| * Set an or where not between statement in your query. | ||
| */ | ||
| orWhereNotBetween(column: string, values: [any, any]): this; | ||
| /** | ||
| * Set an or where null statement in your query. | ||
| */ | ||
| orWhereNull(column: string): this; | ||
| /** | ||
| * Set an or where not null statement in your query. | ||
| */ | ||
| orWhereNotNull(column: string): this; | ||
| /** | ||
| * Set an order by statement in your query. | ||
| */ | ||
| orderBy(column: string, direction?: Direction): this; | ||
| /** | ||
| * Set an order by raw statement in your query. | ||
| */ | ||
| orderByRaw(sql: string, bindings?: any): this; | ||
| /** | ||
| * Order the results easily by the latest date. By default, the result will | ||
| * be ordered by the table's "createdAt" column. | ||
| */ | ||
| latest(column?: string): this; | ||
| /** | ||
| * Order the results easily by the oldest date. By default, the result will | ||
| * be ordered by the table's "createdAt" column. | ||
| */ | ||
| oldest(column?: string): this; | ||
| /** | ||
| * Set the skip number in your query. | ||
| */ | ||
| offset(number: number): this; | ||
| /** | ||
| * Set the limit number in your query. | ||
| */ | ||
| limit(number: number): this; | ||
| } |
@@ -9,14 +9,10 @@ /** | ||
| */ | ||
| /* eslint-disable @typescript-eslint/ban-ts-comment */ | ||
| import { Exec, Is, Json, Options } from '@athenna/common'; | ||
| import { debug } from '#src/debug'; | ||
| import { Log } from '@athenna/logger'; | ||
| import { Driver } from '#src/database/drivers/Driver'; | ||
| import { Is, Json, Options } from '@athenna/common'; | ||
| import { ConnectionFactory } from '#src/factories/ConnectionFactory'; | ||
| import { Transaction } from '#src/database/transactions/Transaction'; | ||
| import { MigrationSource } from '#src/database/migrations/MigrationSource'; | ||
| import { BaseKnexDriver } from '#src/database/drivers/BaseKnexDriver'; | ||
| import { WrongMethodException } from '#src/exceptions/WrongMethodException'; | ||
| import { PROTECTED_QUERY_METHODS } from '#src/constants/ProtectedQueryMethods'; | ||
| import { NotConnectedDatabaseException } from '#src/exceptions/NotConnectedDatabaseException'; | ||
| export class SqliteDriver extends Driver { | ||
| import { EmptyColumnException } from '#src/exceptions/EmptyColumnException'; | ||
| export class SqliteDriver extends BaseKnexDriver { | ||
| /** | ||
@@ -66,85 +62,2 @@ * Connect to database. | ||
| /** | ||
| * Close the connection with database in this instance. | ||
| */ | ||
| async close() { | ||
| if (!this.isConnected) { | ||
| return; | ||
| } | ||
| await this.client.destroy(); | ||
| this.qb = null; | ||
| this.tableName = null; | ||
| this.client = null; | ||
| this.isConnected = false; | ||
| ConnectionFactory.setClient(this.connection, null); | ||
| } | ||
| /** | ||
| * Creates a new instance of query builder. | ||
| */ | ||
| query() { | ||
| if (!this.isConnected) { | ||
| throw new NotConnectedDatabaseException(); | ||
| } | ||
| const query = this.useSetQB | ||
| ? this.qb.table(this.tableName) | ||
| : this.client.queryBuilder().table(this.tableName); | ||
| const handler = { | ||
| get: (target, propertyKey) => { | ||
| if (PROTECTED_QUERY_METHODS.includes(propertyKey)) { | ||
| this.qb = this.query(); | ||
| } | ||
| return target[propertyKey]; | ||
| } | ||
| }; | ||
| return new Proxy(query, handler); | ||
| } | ||
| /** | ||
| * Sync a model schema with database. | ||
| */ | ||
| async sync() { | ||
| debug(`database sync with ${SqliteDriver.name} is not available yet, use migration instead.`); | ||
| } | ||
| /** | ||
| * Create a new transaction. | ||
| */ | ||
| async startTransaction() { | ||
| const trx = await this.client.transaction(); | ||
| return new Transaction(this.clone().setClient(trx)); | ||
| } | ||
| /** | ||
| * Commit the transaction. | ||
| */ | ||
| async commitTransaction() { | ||
| const client = this.client; | ||
| await client.commit(); | ||
| this.tableName = null; | ||
| this.client = null; | ||
| this.isConnected = false; | ||
| } | ||
| /** | ||
| * Rollback the transaction. | ||
| */ | ||
| async rollbackTransaction() { | ||
| const client = this.client; | ||
| await client.rollback(); | ||
| this.tableName = null; | ||
| this.client = null; | ||
| this.isConnected = false; | ||
| } | ||
| /** | ||
| * Run database migrations. | ||
| */ | ||
| async runMigrations() { | ||
| await this.client.migrate.latest({ | ||
| migrationSource: new MigrationSource(this.connection) | ||
| }); | ||
| } | ||
| /** | ||
| * Revert database migrations. | ||
| */ | ||
| async revertMigrations() { | ||
| await this.client.migrate.rollback({ | ||
| migrationSource: new MigrationSource(this.connection) | ||
| }); | ||
| } | ||
| /** | ||
| * List all databases available. | ||
@@ -157,15 +70,2 @@ */ | ||
| /** | ||
| * Get the current database name. | ||
| */ | ||
| async getCurrentDatabase() { | ||
| return this.client.client.database(); | ||
| } | ||
| /** | ||
| * Verify if database exists. | ||
| */ | ||
| async hasDatabase(database) { | ||
| const databases = await this.getDatabases(); | ||
| return databases.includes(database); | ||
| } | ||
| /** | ||
| * Create a new database. | ||
@@ -202,26 +102,2 @@ */ | ||
| /** | ||
| * Verify if table exists. | ||
| */ | ||
| async hasTable(table) { | ||
| return this.client.schema.hasTable(table); | ||
| } | ||
| /** | ||
| * Create a new table in database. | ||
| */ | ||
| async createTable(table, closure) { | ||
| await this.client.schema.createTable(table, closure); | ||
| } | ||
| /** | ||
| * Alter a table in database. | ||
| */ | ||
| async alterTable(table, closure) { | ||
| await this.client.schema.alterTable(table, closure); | ||
| } | ||
| /** | ||
| * Drop a table in database. | ||
| */ | ||
| async dropTable(table) { | ||
| await this.client.schema.dropTableIfExists(table); | ||
| } | ||
| /** | ||
| * Remove all data inside some database table | ||
@@ -234,117 +110,2 @@ * and restart the identity of the table. | ||
| /** | ||
| * Make a raw query in database. | ||
| */ | ||
| raw(sql, bindings) { | ||
| return this.client.raw(sql, bindings); | ||
| } | ||
| /** | ||
| * Calculate the average of a given column. | ||
| */ | ||
| async avg(column) { | ||
| const [{ avg }] = await this.qb.avg({ avg: column }); | ||
| return avg; | ||
| } | ||
| /** | ||
| * Calculate the average of a given column using distinct. | ||
| */ | ||
| async avgDistinct(column) { | ||
| const [{ avg }] = await this.qb.avgDistinct({ avg: column }); | ||
| return avg; | ||
| } | ||
| /** | ||
| * Get the max number of a given column. | ||
| */ | ||
| async max(column) { | ||
| const [{ max }] = await this.qb.max({ max: column }); | ||
| return max; | ||
| } | ||
| /** | ||
| * Get the min number of a given column. | ||
| */ | ||
| async min(column) { | ||
| const [{ min }] = await this.qb.min({ min: column }); | ||
| return min; | ||
| } | ||
| /** | ||
| * Sum all numbers of a given column. | ||
| */ | ||
| async sum(column) { | ||
| const [{ sum }] = await this.qb.sum({ sum: column }); | ||
| return sum; | ||
| } | ||
| /** | ||
| * Sum all numbers of a given column in distinct mode. | ||
| */ | ||
| async sumDistinct(column) { | ||
| const [{ sum }] = await this.qb.sumDistinct({ sum: column }); | ||
| return sum; | ||
| } | ||
| /** | ||
| * Increment a value of a given column. | ||
| */ | ||
| async increment(column) { | ||
| await this.qb.increment(column); | ||
| } | ||
| /** | ||
| * Decrement a value of a given column. | ||
| */ | ||
| async decrement(column) { | ||
| await this.qb.decrement(column); | ||
| } | ||
| /** | ||
| * Calculate the average of a given column using distinct. | ||
| */ | ||
| async count(column = '*') { | ||
| const [{ count }] = await this.qb.count({ count: column }); | ||
| return `${count}`; | ||
| } | ||
| /** | ||
| * Calculate the average of a given column using distinct. | ||
| */ | ||
| async countDistinct(column) { | ||
| const [{ count }] = await this.qb.countDistinct({ count: column }); | ||
| return `${count}`; | ||
| } | ||
| /** | ||
| * Find a value in database. | ||
| */ | ||
| async find() { | ||
| return this.qb.first(); | ||
| } | ||
| /** | ||
| * Find many values in database. | ||
| */ | ||
| async findMany() { | ||
| const data = await this.qb; | ||
| this.qb = this.query(); | ||
| return data; | ||
| } | ||
| /** | ||
| * Find many values in database and return as paginated response. | ||
| */ | ||
| async paginate(page = { page: 0, limit: 10, resourceUrl: '/' }, limit = 10, resourceUrl = '/') { | ||
| if (Is.Number(page)) { | ||
| page = { page, limit, resourceUrl }; | ||
| } | ||
| const [{ count }] = await this.qb | ||
| .clone() | ||
| .clearOrder() | ||
| .clearSelect() | ||
| .count({ count: '*' }); | ||
| const data = await this.offset(page.page * page.limit) | ||
| .limit(page.limit) | ||
| .findMany(); | ||
| return Exec.pagination(data, parseInt(count), page); | ||
| } | ||
| /** | ||
| * Create a value in database. | ||
| */ | ||
| async create(data = {}) { | ||
| if (Is.Array(data)) { | ||
| throw new WrongMethodException('create', 'createMany'); | ||
| } | ||
| const created = await this.createMany([data]); | ||
| return created[0]; | ||
| } | ||
| /** | ||
| * Create many values in database. | ||
@@ -356,402 +117,153 @@ */ | ||
| } | ||
| return this.qb.insert(data, '*'); | ||
| const preparedData = data.map(data => this.prepareInsert(data)); | ||
| return this.qb.insert(preparedData, '*'); | ||
| } | ||
| /** | ||
| * Create data or update if already exists. | ||
| * Find a value in database. | ||
| */ | ||
| async createOrUpdate(data = {}) { | ||
| const query = this.qb.clone(); | ||
| const hasValue = await query.first(); | ||
| if (hasValue) { | ||
| await this.qb | ||
| .where(this.primaryKey, hasValue[this.primaryKey]) | ||
| .update(data); | ||
| return this.where(this.primaryKey, hasValue[this.primaryKey]).find(); | ||
| } | ||
| return this.create(data); | ||
| async find() { | ||
| const data = await super.find(); | ||
| return this.normalizeRow(data); | ||
| } | ||
| /** | ||
| * Update a value in database. | ||
| * Find many values in database. | ||
| */ | ||
| async update(data) { | ||
| await this.qb.clone().update(data); | ||
| const result = await this.findMany(); | ||
| if (result.length === 1) { | ||
| return result[0]; | ||
| } | ||
| return result; | ||
| async findMany() { | ||
| const data = await super.findMany(); | ||
| return data.map(row => this.normalizeRow(row)); | ||
| } | ||
| /** | ||
| * Delete one value in database. | ||
| * Set a where json statement in your query. | ||
| */ | ||
| async delete() { | ||
| await this.qb.delete(); | ||
| } | ||
| /** | ||
| * Set the table that this query will be executed. | ||
| */ | ||
| table(table) { | ||
| if (!this.isConnected) { | ||
| throw new NotConnectedDatabaseException(); | ||
| whereJson(column, operator, value) { | ||
| if (Is.Undefined(column) || !Is.String(column)) { | ||
| throw new EmptyColumnException('whereJson'); | ||
| } | ||
| this.tableName = table; | ||
| this.qb = this.query(); | ||
| return this; | ||
| } | ||
| /** | ||
| * Log in console the actual query built. | ||
| */ | ||
| dump() { | ||
| console.log(this.qb.toSQL().toNative()); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set the columns that should be selected on query. | ||
| */ | ||
| select(...columns) { | ||
| this.qb.select(...columns); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set the columns that should be selected on query raw. | ||
| */ | ||
| selectRaw(sql, bindings) { | ||
| return this.select(this.raw(sql, bindings)); | ||
| } | ||
| /** | ||
| * Set the table that should be used on query. | ||
| * Different from `table()` method, this method | ||
| * doesn't change the driver table. | ||
| */ | ||
| from(table) { | ||
| this.qb.from(table); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set the table that should be used on query raw. | ||
| * Different from `table()` method, this method | ||
| * doesn't change the driver table. | ||
| */ | ||
| fromRaw(sql, bindings) { | ||
| return this.from(this.raw(sql, bindings)); | ||
| } | ||
| /** | ||
| * Set a join statement in your query. | ||
| */ | ||
| join(table, column1, operation, column2) { | ||
| return this.joinByType('join', table, column1, operation, column2); | ||
| } | ||
| /** | ||
| * Set a left join statement in your query. | ||
| */ | ||
| leftJoin(table, column1, operation, column2) { | ||
| return this.joinByType('leftJoin', table, column1, operation, column2); | ||
| } | ||
| /** | ||
| * Set a right join statement in your query. | ||
| */ | ||
| rightJoin(table, column1, operation, column2) { | ||
| return this.joinByType('rightJoin', table, column1, operation, column2); | ||
| } | ||
| /** | ||
| * Set a cross join statement in your query. | ||
| */ | ||
| crossJoin(table, column1, operation, column2) { | ||
| return this.joinByType('crossJoin', table, column1, operation, column2); | ||
| } | ||
| /** | ||
| * Set a full outer join statement in your query. | ||
| */ | ||
| fullOuterJoin(table, column1, operation, column2) { | ||
| return this.joinByType('fullOuterJoin', table, column1, operation, column2); | ||
| } | ||
| /** | ||
| * Set a left outer join statement in your query. | ||
| */ | ||
| leftOuterJoin(table, column1, operation, column2) { | ||
| return this.joinByType('leftOuterJoin', table, column1, operation, column2); | ||
| } | ||
| /** | ||
| * Set a right outer join statement in your query. | ||
| */ | ||
| rightOuterJoin(table, column1, operation, column2) { | ||
| return this.joinByType('rightOuterJoin', table, column1, operation, column2); | ||
| } | ||
| /** | ||
| * Set a join raw statement in your query. | ||
| */ | ||
| joinRaw(sql, bindings) { | ||
| this.qb.joinRaw(sql, bindings); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a group by statement in your query. | ||
| */ | ||
| groupBy(...columns) { | ||
| this.qb.groupBy(...columns); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a group by raw statement in your query. | ||
| */ | ||
| groupByRaw(sql, bindings) { | ||
| this.qb.groupByRaw(sql, bindings); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a having statement in your query. | ||
| */ | ||
| having(column, operation, value) { | ||
| if (operation === undefined) { | ||
| this.qb.having(column); | ||
| return this; | ||
| const parsed = this.parseJsonSelector(column); | ||
| if (!parsed) { | ||
| throw new Error(`Invalid JSON selector: ${column}`); | ||
| } | ||
| if (value === undefined) { | ||
| this.qb.having(column, '=', operation); | ||
| const normalized = this.normalizeJsonOperation(operator, value); | ||
| if (!parsed.path.includes('*')) { | ||
| this.qb.whereRaw('json_extract(??, ?) ' + normalized.operator + ' ?', [ | ||
| parsed.column, | ||
| this.parseJsonSelectorToSqlitePath(parsed.path), | ||
| normalized.value | ||
| ]); | ||
| return this; | ||
| } | ||
| this.qb.having(column, operation, value); | ||
| const wildcard = this.parseJsonSelectorToWildcardParts(parsed.path); | ||
| this.qb.whereRaw('exists (select 1 from json_each(??, ?) where json_extract(json_each.value, ?) ' + | ||
| normalized.operator + | ||
| ' ?)', [parsed.column, wildcard.arrayPath, wildcard.valuePath, normalized.value]); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a having raw statement in your query. | ||
| * Set an or where json statement in your query. | ||
| */ | ||
| havingRaw(sql, bindings) { | ||
| this.qb.havingRaw(sql, bindings); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a having exists statement in your query. | ||
| */ | ||
| havingExists(closure) { | ||
| const driver = this.clone(); | ||
| // @ts-ignore | ||
| this.qb.havingExists(function () { | ||
| closure(driver.setQueryBuilder(this, { useSetQB: true })); | ||
| }); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a having not exists statement in your query. | ||
| */ | ||
| havingNotExists(closure) { | ||
| const driver = this.clone(); | ||
| // @ts-ignore | ||
| this.qb.havingNotExists(function () { | ||
| closure(driver.setQueryBuilder(this, { useSetQB: true })); | ||
| }); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a having in statement in your query. | ||
| */ | ||
| havingIn(column, values) { | ||
| this.qb.havingIn(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a having not in statement in your query. | ||
| */ | ||
| havingNotIn(column, values) { | ||
| this.qb.havingNotIn(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a having between statement in your query. | ||
| */ | ||
| havingBetween(column, values) { | ||
| this.qb.havingBetween(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a having not between statement in your query. | ||
| */ | ||
| havingNotBetween(column, values) { | ||
| this.qb.havingNotBetween(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a having null statement in your query. | ||
| */ | ||
| havingNull(column) { | ||
| this.qb.havingNull(column); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a having not null statement in your query. | ||
| */ | ||
| havingNotNull(column) { | ||
| this.qb.havingNotNull(column); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or having statement in your query. | ||
| */ | ||
| orHaving(column, operation, value) { | ||
| if (operation === undefined) { | ||
| this.qb.orHaving(column); | ||
| return this; | ||
| orWhereJson(column, operator, value) { | ||
| if (Is.Undefined(column) || !Is.String(column)) { | ||
| throw new EmptyColumnException('orWhereJson'); | ||
| } | ||
| if (value === undefined) { | ||
| this.qb.orHaving(column, '=', operation); | ||
| const parsed = this.parseJsonSelector(column); | ||
| if (!parsed) { | ||
| throw new Error(`Invalid JSON selector: ${column}`); | ||
| } | ||
| const normalized = this.normalizeJsonOperation(operator, value); | ||
| if (!parsed.path.includes('*')) { | ||
| this.qb.orWhereRaw('json_extract(??, ?) ' + normalized.operator + ' ?', [ | ||
| parsed.column, | ||
| this.parseJsonSelectorToSqlitePath(parsed.path), | ||
| normalized.value | ||
| ]); | ||
| return this; | ||
| } | ||
| this.qb.orHaving(column, operation, value); | ||
| const wildcard = this.parseJsonSelectorToWildcardParts(parsed.path); | ||
| this.qb.orWhereRaw('exists (select 1 from json_each(??, ?) where json_extract(json_each.value, ?) ' + | ||
| normalized.operator + | ||
| ' ?)', [parsed.column, wildcard.arrayPath, wildcard.valuePath, normalized.value]); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or having raw statement in your query. | ||
| * Convert a json selector path to sqlite json path. | ||
| */ | ||
| orHavingRaw(sql, bindings) { | ||
| this.qb.orHavingRaw(sql, bindings); | ||
| return this; | ||
| parseJsonSelectorToSqlitePath(path) { | ||
| const parts = path | ||
| .split('->') | ||
| .map(part => part.trim()) | ||
| .filter(Boolean); | ||
| return this.toJsonPath(parts); | ||
| } | ||
| /** | ||
| * Set an or having exists statement in your query. | ||
| * Split a json selector around the wildcard. | ||
| */ | ||
| orHavingExists(closure) { | ||
| const driver = this.clone(); | ||
| // @ts-ignore | ||
| this.qb.orHavingExists(function () { | ||
| closure(driver.setQueryBuilder(this, { useSetQB: true })); | ||
| }); | ||
| return this; | ||
| parseJsonSelectorToWildcardParts(path) { | ||
| const parts = path | ||
| .split('->') | ||
| .map(part => part.trim()) | ||
| .filter(Boolean); | ||
| const wildcardIndex = parts.indexOf('*'); | ||
| return { | ||
| arrayPath: this.toJsonPath(parts.slice(0, wildcardIndex)), | ||
| valuePath: this.toJsonPath(parts.slice(wildcardIndex + 1)) | ||
| }; | ||
| } | ||
| /** | ||
| * Set an or having not exists statement in your query. | ||
| * Convert path parts to a valid json path. | ||
| */ | ||
| orHavingNotExists(closure) { | ||
| const driver = this.clone(); | ||
| // @ts-ignore | ||
| this.qb.orHavingNotExists(function () { | ||
| closure(driver.setQueryBuilder(this, { useSetQB: true })); | ||
| }); | ||
| return this; | ||
| toJsonPath(parts) { | ||
| return parts.reduce((jsonPath, part) => { | ||
| if (/^\d+$/.test(part)) { | ||
| return `${jsonPath}[${part}]`; | ||
| } | ||
| return `${jsonPath}.${part}`; | ||
| }, '$'); | ||
| } | ||
| /** | ||
| * Set an or having in statement in your query. | ||
| * Normalize operator/value pairs from the whereJson overloads. | ||
| */ | ||
| orHavingIn(column, values) { | ||
| // @ts-ignore | ||
| this.qb.orHavingIn(column, values); | ||
| return this; | ||
| normalizeJsonOperation(operator, value) { | ||
| if (Is.Undefined(value)) { | ||
| return { | ||
| operator: '=', | ||
| value: operator | ||
| }; | ||
| } | ||
| return { | ||
| operator, | ||
| value | ||
| }; | ||
| } | ||
| /** | ||
| * Set an or having not in statement in your query. | ||
| * Normalize json strings returned by sqlite into arrays/objects. | ||
| */ | ||
| orHavingNotIn(column, values) { | ||
| this.qb.orHavingNotIn(column, values); | ||
| return this; | ||
| normalizeRow(row) { | ||
| if (!row || !Is.Object(row)) { | ||
| return row; | ||
| } | ||
| return Object.entries(row).reduce((normalized, [key, value]) => { | ||
| normalized[key] = this.normalizeJsonValue(value); | ||
| return normalized; | ||
| }, {}); | ||
| } | ||
| /** | ||
| * Set an or having between statement in your query. | ||
| * Parse stringified json objects/arrays returned by sqlite. | ||
| */ | ||
| orHavingBetween(column, values) { | ||
| this.qb.orHavingBetween(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or having not between statement in your query. | ||
| */ | ||
| orHavingNotBetween(column, values) { | ||
| this.qb.orHavingNotBetween(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or having null statement in your query. | ||
| */ | ||
| orHavingNull(column) { | ||
| // @ts-ignore | ||
| this.qb.orHavingNull(column); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or having not null statement in your query. | ||
| */ | ||
| orHavingNotNull(column) { | ||
| // @ts-ignore | ||
| this.qb.orHavingNotNull(column); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a where statement in your query. | ||
| */ | ||
| where(statement, operation, value) { | ||
| if (Is.Function(statement)) { | ||
| const driver = this.clone(); | ||
| this.qb.where(function () { | ||
| statement(driver.setQueryBuilder(this, { useSetQB: true })); | ||
| }); | ||
| return this; | ||
| normalizeJsonValue(value) { | ||
| if (!Is.String(value)) { | ||
| return value; | ||
| } | ||
| if (operation === undefined) { | ||
| if (Is.Array(statement)) { | ||
| throw new Error('Arrays as statement are not supported.'); | ||
| } | ||
| if (Is.String(statement)) { | ||
| throw new Error(`The value for the "${statement}" column is undefined and where will not work.`); | ||
| } | ||
| this.qb.where(statement); | ||
| return this; | ||
| const trimmed = value.trim(); | ||
| if (!(trimmed.startsWith('{') && trimmed.endsWith('}')) && | ||
| !(trimmed.startsWith('[') && trimmed.endsWith(']'))) { | ||
| return value; | ||
| } | ||
| if (value === undefined) { | ||
| this.qb.where(statement, operation); | ||
| return this; | ||
| try { | ||
| return JSON.parse(trimmed); | ||
| } | ||
| this.qb.where(statement, operation, value); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a where not statement in your query. | ||
| */ | ||
| whereNot(statement, value) { | ||
| if (Is.Function(statement)) { | ||
| const driver = this.clone(); | ||
| this.qb.whereNot(function () { | ||
| statement(driver.setQueryBuilder(this, { useSetQB: true })); | ||
| }); | ||
| return this; | ||
| catch { | ||
| return value; | ||
| } | ||
| if (value === undefined) { | ||
| this.qb.whereNot(statement); | ||
| return this; | ||
| } | ||
| this.qb.whereNot(statement, value); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a where raw statement in your query. | ||
| */ | ||
| whereRaw(sql, bindings) { | ||
| this.qb.whereRaw(sql, bindings); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a where exists statement in your query. | ||
| */ | ||
| whereExists(closure) { | ||
| const driver = this.clone(); | ||
| this.qb.whereExists(function () { | ||
| closure(driver.setQueryBuilder(this, { useSetQB: true })); | ||
| }); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a where not exists statement in your query. | ||
| */ | ||
| whereNotExists(closure) { | ||
| const driver = this.clone(); | ||
| this.qb.whereNotExists(function () { | ||
| closure(driver.setQueryBuilder(this, { useSetQB: true })); | ||
| }); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a where like statement in your query. | ||
| */ | ||
| whereLike(column, value) { | ||
| this.qb.whereLike(column, value); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a where ILike statement in your query. | ||
@@ -764,120 +276,4 @@ */ | ||
| /** | ||
| * Set a where in statement in your query. | ||
| * Set a where ILike statement in your query. | ||
| */ | ||
| whereIn(column, values) { | ||
| this.qb.whereIn(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a where not in statement in your query. | ||
| */ | ||
| whereNotIn(column, values) { | ||
| this.qb.whereNotIn(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a where between statement in your query. | ||
| */ | ||
| whereBetween(column, values) { | ||
| this.qb.whereBetween(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a where not between statement in your query. | ||
| */ | ||
| whereNotBetween(column, values) { | ||
| this.qb.whereNotBetween(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a where null statement in your query. | ||
| */ | ||
| whereNull(column) { | ||
| this.qb.whereNull(column); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a where not null statement in your query. | ||
| */ | ||
| whereNotNull(column) { | ||
| this.qb.whereNotNull(column); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a or where statement in your query. | ||
| */ | ||
| orWhere(statement, operation, value) { | ||
| if (Is.Function(statement)) { | ||
| const driver = this.clone(); | ||
| this.qb.orWhere(function () { | ||
| statement(driver.setQueryBuilder(this, { useSetQB: true })); | ||
| }); | ||
| return this; | ||
| } | ||
| if (operation === undefined) { | ||
| this.qb.orWhere(statement); | ||
| return this; | ||
| } | ||
| if (value === undefined) { | ||
| this.qb.orWhere(statement, operation); | ||
| return this; | ||
| } | ||
| this.qb.orWhere(statement, operation, value); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or where not statement in your query. | ||
| */ | ||
| orWhereNot(statement, value) { | ||
| if (Is.Function(statement)) { | ||
| const driver = this.clone(); | ||
| this.qb.orWhereNot(function () { | ||
| statement(driver.setQueryBuilder(this, { useSetQB: true })); | ||
| }); | ||
| return this; | ||
| } | ||
| if (value === undefined) { | ||
| this.qb.orWhereNot(statement); | ||
| return this; | ||
| } | ||
| this.qb.orWhereNot(statement, value); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a or where raw statement in your query. | ||
| */ | ||
| orWhereRaw(sql, bindings) { | ||
| this.qb.orWhereRaw(sql, bindings); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or where exists statement in your query. | ||
| */ | ||
| orWhereExists(closure) { | ||
| const driver = this.clone(); | ||
| this.qb.orWhereExists(function () { | ||
| closure(driver.setQueryBuilder(this, { useSetQB: true })); | ||
| }); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or where not exists statement in your query. | ||
| */ | ||
| orWhereNotExists(closure) { | ||
| const driver = this.clone(); | ||
| this.qb.orWhereNotExists(function () { | ||
| closure(driver.setQueryBuilder(this, { useSetQB: true })); | ||
| }); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or where like statement in your query. | ||
| */ | ||
| orWhereLike(column, value) { | ||
| this.qb.orWhereLike(column, value); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or where ILike statement in your query. | ||
| */ | ||
| orWhereILike(column, value) { | ||
@@ -887,86 +283,2 @@ this.qb.orWhereLike(column, value); | ||
| } | ||
| /** | ||
| * Set an or where in statement in your query. | ||
| */ | ||
| orWhereIn(column, values) { | ||
| this.qb.orWhereIn(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or where not in statement in your query. | ||
| */ | ||
| orWhereNotIn(column, values) { | ||
| this.qb.orWhereNotIn(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or where between statement in your query. | ||
| */ | ||
| orWhereBetween(column, values) { | ||
| this.qb.orWhereBetween(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or where not between statement in your query. | ||
| */ | ||
| orWhereNotBetween(column, values) { | ||
| this.qb.orWhereNotBetween(column, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or where null statement in your query. | ||
| */ | ||
| orWhereNull(column) { | ||
| this.qb.orWhereNull(column); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an or where not null statement in your query. | ||
| */ | ||
| orWhereNotNull(column) { | ||
| this.qb.orWhereNotNull(column); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an order by statement in your query. | ||
| */ | ||
| orderBy(column, direction = 'ASC') { | ||
| this.qb.orderBy(column, direction.toUpperCase()); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an order by raw statement in your query. | ||
| */ | ||
| orderByRaw(sql, bindings) { | ||
| this.qb.orderByRaw(sql, bindings); | ||
| return this; | ||
| } | ||
| /** | ||
| * Order the results easily by the latest date. By default, the result will | ||
| * be ordered by the table's "createdAt" column. | ||
| */ | ||
| latest(column = 'createdAt') { | ||
| return this.orderBy(column, 'DESC'); | ||
| } | ||
| /** | ||
| * Order the results easily by the oldest date. By default, the result will | ||
| * be ordered by the table's "createdAt" column. | ||
| */ | ||
| oldest(column = 'createdAt') { | ||
| return this.orderBy(column, 'ASC'); | ||
| } | ||
| /** | ||
| * Set the skip number in your query. | ||
| */ | ||
| offset(number) { | ||
| this.qb.offset(number); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set the limit number in your query. | ||
| */ | ||
| limit(number) { | ||
| this.qb.limit(number); | ||
| return this; | ||
| } | ||
| } |
@@ -10,5 +10,5 @@ /** | ||
| import { Collection, type PaginatedResponse, type PaginationOptions } from '@athenna/common'; | ||
| import type { ModelRelations } from '#src/types'; | ||
| import { type Faker } from '@faker-js/faker'; | ||
| import { ModelSchema } from '#src/models/schemas/ModelSchema'; | ||
| import type { ModelColumns, ModelRelations } from '#src/types'; | ||
| import { ORIGINAL_SYMBOL } from '#src/constants/OriginalSymbol'; | ||
@@ -99,3 +99,4 @@ import { ModelFactory } from '#src/models/factories/ModelFactory'; | ||
| */ | ||
| static pluck<T extends typeof BaseModel, K extends keyof InstanceType<T> = keyof InstanceType<T>>(this: T, key: K, where?: Partial<InstanceType<T>>): Promise<InstanceType<T>[K]>; | ||
| static pluck<T extends typeof BaseModel, K extends Extract<ModelColumns<InstanceType<T>>, keyof InstanceType<T>>>(this: T, key: K, where?: Partial<InstanceType<T>>): Promise<InstanceType<T>[K]>; | ||
| static pluck<T extends typeof BaseModel>(this: T, key: ModelColumns<InstanceType<T>>, where?: Partial<InstanceType<T>>): Promise<any>; | ||
| /** | ||
@@ -105,3 +106,4 @@ * Find many values in database but returns only the | ||
| */ | ||
| static pluckMany<T extends typeof BaseModel, K extends keyof InstanceType<T> = keyof InstanceType<T>>(this: T, key: K, where?: Partial<InstanceType<T>>): Promise<InstanceType<T>[K][]>; | ||
| static pluckMany<T extends typeof BaseModel, K extends Extract<ModelColumns<InstanceType<T>>, keyof InstanceType<T>>>(this: T, key: K, where?: Partial<InstanceType<T>>): Promise<InstanceType<T>[K][]>; | ||
| static pluckMany<T extends typeof BaseModel>(this: T, key: ModelColumns<InstanceType<T>>, where?: Partial<InstanceType<T>>): Promise<any[]>; | ||
| /** | ||
@@ -108,0 +110,0 @@ * Find a value in database. |
@@ -139,6 +139,2 @@ /** | ||
| } | ||
| /** | ||
| * Find value in database but returns only the value of | ||
| * selected column directly. | ||
| */ | ||
| static async pluck(key, where) { | ||
@@ -151,6 +147,2 @@ const query = this.query(); | ||
| } | ||
| /** | ||
| * Find many values in database but returns only the | ||
| * values of selected column directly. | ||
| */ | ||
| static async pluckMany(key, where) { | ||
@@ -293,3 +285,4 @@ const query = this.query(); | ||
| this[ORIGINAL_SYMBOL] = {}; | ||
| Object.keys(Json.copy(this)).forEach(key => { | ||
| const copied = Json.copy(this); | ||
| Object.keys(copied).forEach(key => { | ||
| const value = this[key]; | ||
@@ -302,3 +295,3 @@ if (Is.Array(value) && value[0] && ORIGINAL_SYMBOL in value[0]) { | ||
| } | ||
| this[ORIGINAL_SYMBOL][key] = value; | ||
| this[ORIGINAL_SYMBOL][key] = copied[key]; | ||
| }); | ||
@@ -305,0 +298,0 @@ return this; |
@@ -73,7 +73,7 @@ /** | ||
| */ | ||
| count(column?: ModelColumns<M>): Promise<string>; | ||
| count(column?: ModelColumns<M>): Promise<number>; | ||
| /** | ||
| * Calculate the average of a given column using distinct. | ||
| */ | ||
| countDistinct(column: ModelColumns<M>): Promise<string>; | ||
| countDistinct(column: ModelColumns<M>): Promise<number>; | ||
| /** | ||
@@ -83,3 +83,4 @@ * Find value in database but returns only the value of | ||
| */ | ||
| pluck<K extends keyof M = ModelColumns<M>>(column: K): Promise<M[K]>; | ||
| pluck<K extends Extract<ModelColumns<M>, keyof M>>(column: K): Promise<M[K]>; | ||
| pluck(column: ModelColumns<M>): Promise<any>; | ||
| /** | ||
@@ -89,3 +90,4 @@ * Find many values in database but returns only the | ||
| */ | ||
| pluckMany<K extends keyof M = ModelColumns<M>>(column: K): Promise<M[K][]>; | ||
| pluckMany<K extends Extract<ModelColumns<M>, keyof M>>(column: K): Promise<M[K][]>; | ||
| pluckMany(column: ModelColumns<M>): Promise<any[]>; | ||
| /** | ||
@@ -218,6 +220,2 @@ * Find a value in database. | ||
| /** | ||
| * Set a orHaving in statement in your query. | ||
| */ | ||
| orHavingIn(column: ModelColumns<M>, values: any[]): this; | ||
| /** | ||
| * Set a orHaving not in statement in your query. | ||
@@ -283,2 +281,4 @@ */ | ||
| whereNotNull(column: ModelColumns<M>): this; | ||
| whereJson(column: ModelColumns<M>, value: any): this; | ||
| whereJson(column: ModelColumns<M>, operation: Operations, value: any): this; | ||
| orWhere(statement: (query: this) => void): this; | ||
@@ -323,2 +323,4 @@ orWhere(statement: Partial<M>): this; | ||
| orWhereNotNull(column: ModelColumns<M>): this; | ||
| orWhereJson(column: ModelColumns<M>, value: any): this; | ||
| orWhereJson(column: ModelColumns<M>, operation: Operations, value: any): this; | ||
| /** | ||
@@ -325,0 +327,0 @@ * Set an order by statement in your query. |
@@ -9,3 +9,3 @@ /** | ||
| */ | ||
| import { Collection, Is, Options } from '@athenna/common'; | ||
| import { Is, Options, Collection } from '@athenna/common'; | ||
| import { QueryBuilder } from '#src/database/builders/QueryBuilder'; | ||
@@ -141,6 +141,2 @@ import { ModelGenerator } from '#src/models/factories/ModelGenerator'; | ||
| } | ||
| /** | ||
| * Find value in database but returns only the value of | ||
| * selected column directly. | ||
| */ | ||
| async pluck(column) { | ||
@@ -151,6 +147,2 @@ this.setInternalQueries(); | ||
| } | ||
| /** | ||
| * Find many values in database but returns only the | ||
| * values of selected column directly. | ||
| */ | ||
| async pluckMany(column) { | ||
@@ -501,10 +493,2 @@ this.setInternalQueries(); | ||
| /** | ||
| * Set a orHaving in statement in your query. | ||
| */ | ||
| orHavingIn(column, values) { | ||
| const name = this.schema.getColumnNameByProperty(column); | ||
| super.orHavingIn(name, values); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a orHaving not in statement in your query. | ||
@@ -656,2 +640,10 @@ */ | ||
| /** | ||
| * Set a where json statement in your query. | ||
| */ | ||
| whereJson(column, operation, value) { | ||
| const name = this.schema.getColumnNameByProperty(column); | ||
| super.whereJson(name, operation, value); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set a orWhere statement in your query. | ||
@@ -773,2 +765,10 @@ */ | ||
| /** | ||
| * Set an orWhereJson statement in your query. | ||
| */ | ||
| orWhereJson(column, operation, value) { | ||
| const name = this.schema.getColumnNameByProperty(column); | ||
| super.orWhereJson(name, operation, value); | ||
| return this; | ||
| } | ||
| /** | ||
| * Set an order by statement in your query. | ||
@@ -775,0 +775,0 @@ */ |
@@ -10,5 +10,7 @@ /** | ||
| import type { BaseModel } from '#src/models/BaseModel'; | ||
| type UnsafeColumnSelector = `${string}.${string}` | `${string}->${string}`; | ||
| export type ColumnKeys<T> = { | ||
| [K in keyof T]: T[K] extends BaseModel | BaseModel[] ? never : K; | ||
| }[keyof Omit<T, 'save' | 'fresh' | 'refresh' | 'dirty' | 'delete' | 'restore' | 'isDirty' | 'isTrashed' | 'isPersisted' | 'setOriginal' | 'load' | 'toJSON'>]; | ||
| export type ModelColumns<T> = Extract<ColumnKeys<T>, string>; | ||
| export type ModelColumns<T> = Extract<ColumnKeys<T>, string> | UnsafeColumnSelector; | ||
| export {}; |
176
3.53%10
-44.44%512388
-5.55%15314
-8.41%