Finish fleshing out Model; refactor mixin types; TRUNCATE
This commit is contained in:
parent
e4f5da7ac6
commit
b915fcedf9
@ -496,7 +496,9 @@ export class AsyncCollection<T> {
|
|||||||
|
|
||||||
if ( !fetched_indices.includes(index) ) {
|
if ( !fetched_indices.includes(index) ) {
|
||||||
fetched_indices.push(index)
|
fetched_indices.push(index)
|
||||||
random_items.push(await this._items.at_index(index))
|
const item = await this._items.at_index(index)
|
||||||
|
if ( typeof item !== 'undefined' )
|
||||||
|
random_items.push(item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ export class StopIteration extends Error {}
|
|||||||
export abstract class Iterable<T> {
|
export abstract class Iterable<T> {
|
||||||
protected index = 0
|
protected index = 0
|
||||||
|
|
||||||
abstract async at_index(i: number): Promise<T>
|
abstract async at_index(i: number): Promise<T | undefined>
|
||||||
abstract async from_range(start: number, end: number): Promise<Collection<T>>
|
abstract async from_range(start: number, end: number): Promise<Collection<T>>
|
||||||
abstract async count(): Promise<number>
|
abstract async count(): Promise<number>
|
||||||
abstract clone(): Iterable<T>
|
abstract clone(): Iterable<T>
|
||||||
|
@ -29,17 +29,17 @@ export default abstract class Logger {
|
|||||||
case LoggingLevel.Success:
|
case LoggingLevel.Success:
|
||||||
return green('success')
|
return green('success')
|
||||||
case LoggingLevel.Error:
|
case LoggingLevel.Error:
|
||||||
return red('error')
|
return red(' error')
|
||||||
case LoggingLevel.Warning:
|
case LoggingLevel.Warning:
|
||||||
return yellow('warning')
|
return yellow('warning')
|
||||||
case LoggingLevel.Info:
|
case LoggingLevel.Info:
|
||||||
return blue('info')
|
return blue(' info')
|
||||||
case LoggingLevel.Debug:
|
case LoggingLevel.Debug:
|
||||||
return cyan('debug')
|
return cyan(' debug')
|
||||||
case LoggingLevel.Verbose:
|
case LoggingLevel.Verbose:
|
||||||
return gray('verbose')
|
return gray('verbose')
|
||||||
case LoggingLevel.Silent:
|
case LoggingLevel.Silent:
|
||||||
return gray('silent')
|
return gray(' silent')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,6 +59,7 @@ class Logging {
|
|||||||
level,
|
level,
|
||||||
output,
|
output,
|
||||||
date: new Date,
|
date: new Date,
|
||||||
|
caller_name: this.get_caller_info(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,6 +74,14 @@ class Logging {
|
|||||||
public remove_logger(logger_class: typeof Logger) {
|
public remove_logger(logger_class: typeof Logger) {
|
||||||
this._loggers = this._loggers.filter(x => !(x instanceof logger_class))
|
this._loggers = this._loggers.filter(x => !(x instanceof logger_class))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected get_caller_info(level = 5): string {
|
||||||
|
let e = new Error
|
||||||
|
if ( !e.stack ) return 'Unknown'
|
||||||
|
return e.stack.split(' at ')
|
||||||
|
.slice(level)
|
||||||
|
.map((x: string): string => x.trim().split(' (')[0].split('.')[0].split(':')[0])[0]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export { Logging }
|
export { Logging }
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import AbstractLogger from './Logger.ts'
|
import AbstractLogger from './Logger.ts'
|
||||||
import { LogMessage } from './types.ts'
|
import { LogMessage } from './types.ts'
|
||||||
import { gray } from '../../external/std.ts'
|
import { gray, cyan } from '../../external/std.ts'
|
||||||
|
|
||||||
export default class StandardLogger extends AbstractLogger {
|
export default class StandardLogger extends AbstractLogger {
|
||||||
public async write(message: LogMessage): Promise<void> {
|
public async write(message: LogMessage): Promise<void> {
|
||||||
const prefix = this.level_display(message.level)
|
const prefix = this.level_display(message.level)
|
||||||
const text = `${prefix} ${gray(this.format_date(message.date))}`
|
const text = `${prefix} ${gray(this.format_date(message.date))} (${cyan(message.caller_name || 'Unknown')})`
|
||||||
console.log(text, message.output)
|
console.log(text, message.output)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,7 @@ interface LogMessage {
|
|||||||
level: LoggingLevel,
|
level: LoggingLevel,
|
||||||
date: Date,
|
date: Date,
|
||||||
output: any,
|
output: any,
|
||||||
|
caller_name: string,
|
||||||
}
|
}
|
||||||
|
|
||||||
const isLogMessage = (something: any): something is LogMessage => {
|
const isLogMessage = (something: any): something is LogMessage => {
|
||||||
|
115
lib/src/support/BehaviorSubject.ts
Normal file
115
lib/src/support/BehaviorSubject.ts
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
export class UnsubscribeError extends Error {}
|
||||||
|
export class CompletedObservableError extends Error {
|
||||||
|
constructor() {
|
||||||
|
super('This observable can no longer be pushed to, as it has been completed.')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SubscriberFunction<T> = (val: T) => any
|
||||||
|
export type SubscriberErrorFunction = (error: Error) => any
|
||||||
|
export type SubscriberCompleteFunction<T> = (val?: T) => any
|
||||||
|
|
||||||
|
export type ComplexSubscriber<T> = {
|
||||||
|
next?: SubscriberFunction<T>,
|
||||||
|
error?: SubscriberErrorFunction,
|
||||||
|
complete?: SubscriberCompleteFunction<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Subscription<T> = SubscriberFunction<T> | ComplexSubscriber<T>
|
||||||
|
export type Unsubscribe = { unsubscribe: () => void }
|
||||||
|
|
||||||
|
export class BehaviorSubject<T> {
|
||||||
|
protected subscribers: ComplexSubscriber<T>[] = []
|
||||||
|
protected _is_complete: boolean = false
|
||||||
|
protected _value?: T
|
||||||
|
protected _has_push: boolean = false
|
||||||
|
|
||||||
|
public subscribe(subscriber: Subscription<T>): Unsubscribe {
|
||||||
|
if ( typeof subscriber === 'function' ) {
|
||||||
|
this.subscribers.push({ next: subscriber })
|
||||||
|
} else {
|
||||||
|
this.subscribers.push(subscriber)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
unsubscribe: () => {
|
||||||
|
this.subscribers = this.subscribers.filter(x => x !== subscriber)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public to_promise(): Promise<T> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const { unsubscribe } = this.subscribe({
|
||||||
|
next: (val: T) => {
|
||||||
|
resolve(val)
|
||||||
|
unsubscribe()
|
||||||
|
},
|
||||||
|
error: (error: Error) => {
|
||||||
|
reject(error)
|
||||||
|
unsubscribe()
|
||||||
|
},
|
||||||
|
complete: (val?: T) => {
|
||||||
|
resolve(val)
|
||||||
|
unsubscribe()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
public async next(val: T): Promise<void> {
|
||||||
|
if ( this._is_complete ) throw new CompletedObservableError()
|
||||||
|
this._value = val
|
||||||
|
this._has_push = true
|
||||||
|
for ( const subscriber of this.subscribers ) {
|
||||||
|
if ( subscriber.next ) {
|
||||||
|
try {
|
||||||
|
await subscriber.next(val)
|
||||||
|
} catch (e) {
|
||||||
|
if ( e instanceof UnsubscribeError ) {
|
||||||
|
this.subscribers = this.subscribers.filter(x => x !== subscriber)
|
||||||
|
} else if (subscriber.error) {
|
||||||
|
await subscriber.error(e)
|
||||||
|
} else {
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async push(vals: T[]): Promise<void> {
|
||||||
|
if ( this._is_complete ) throw new CompletedObservableError()
|
||||||
|
await Promise.all(vals.map(val => this.next(val)))
|
||||||
|
}
|
||||||
|
|
||||||
|
public async complete(final_val?: T): Promise<void> {
|
||||||
|
if ( this._is_complete ) throw new CompletedObservableError()
|
||||||
|
if ( typeof final_val === 'undefined' ) final_val = this.value()
|
||||||
|
else this._value = final_val
|
||||||
|
|
||||||
|
for ( const subscriber of this.subscribers ) {
|
||||||
|
if ( subscriber.complete ) {
|
||||||
|
try {
|
||||||
|
await subscriber.complete(final_val)
|
||||||
|
} catch (e) {
|
||||||
|
if ( subscriber.error ) {
|
||||||
|
await subscriber.error(e)
|
||||||
|
} else {
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this._is_complete = true
|
||||||
|
}
|
||||||
|
|
||||||
|
public value(): T | undefined {
|
||||||
|
return this._value
|
||||||
|
}
|
||||||
|
|
||||||
|
public is_complete(): boolean {
|
||||||
|
return this._is_complete
|
||||||
|
}
|
||||||
|
}
|
@ -7,3 +7,5 @@ export function applyMixins(derivedCtor: any, baseCtors: any[]) {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type Constructor<T = {}> = new (...args: any[]) => T
|
||||||
|
@ -2,8 +2,10 @@ import {escape, EscapedValue, FieldSet, QuerySource} from './types.ts'
|
|||||||
import { Select } from './type/Select.ts'
|
import { Select } from './type/Select.ts'
|
||||||
import RawValue from './RawValue.ts'
|
import RawValue from './RawValue.ts'
|
||||||
import {Statement} from './Statement.ts'
|
import {Statement} from './Statement.ts'
|
||||||
import {Update} from "./type/Update.ts";
|
import {Update} from './type/Update.ts'
|
||||||
import {Insert} from "./type/Insert.ts";
|
import {Insert} from './type/Insert.ts'
|
||||||
|
import {Delete} from './type/Delete.ts'
|
||||||
|
import {Truncate} from "./type/Truncate.ts";
|
||||||
|
|
||||||
export function raw(value: string) {
|
export function raw(value: string) {
|
||||||
return new RawValue(value)
|
return new RawValue(value)
|
||||||
@ -15,29 +17,39 @@ export class IncorrectInterpolationError extends Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Builder {
|
export class Builder<T> {
|
||||||
// create table, insert, delete, alter table, drop table, select
|
// create table, alter table, drop table, select
|
||||||
|
|
||||||
public select(...fields: FieldSet[]) {
|
public select(...fields: FieldSet[]): Select<T> {
|
||||||
fields = fields.flat()
|
fields = fields.flat()
|
||||||
const select = new Select()
|
const select = new Select<T>()
|
||||||
return select.fields(...fields)
|
return select.fields(...fields)
|
||||||
}
|
}
|
||||||
|
|
||||||
public update(target?: QuerySource, alias?: string) {
|
public update(target?: QuerySource, alias?: string): Update<T> {
|
||||||
const update = new Update()
|
const update = new Update<T>()
|
||||||
if ( target ) update.to(target, alias)
|
if ( target ) update.to(target, alias)
|
||||||
return update
|
return update
|
||||||
}
|
}
|
||||||
|
|
||||||
public insert(target?: QuerySource, alias?: string) {
|
public delete(target?: QuerySource, alias?: string): Delete<T> {
|
||||||
const insert = new Insert()
|
const del = new Delete<T>()
|
||||||
|
if ( target ) del.from(target, alias)
|
||||||
|
return del
|
||||||
|
}
|
||||||
|
|
||||||
|
public insert(target?: QuerySource, alias?: string): Insert<T> {
|
||||||
|
const insert = new Insert<T>()
|
||||||
if ( target ) insert.into(target, alias)
|
if ( target ) insert.into(target, alias)
|
||||||
return insert
|
return insert
|
||||||
}
|
}
|
||||||
|
|
||||||
public statement(statement: string, ...interpolations: EscapedValue[]) {
|
public statement(statement: string, ...interpolations: EscapedValue[]): Statement<T> {
|
||||||
return new Statement(statement, interpolations)
|
return new Statement<T>(statement, interpolations)
|
||||||
|
}
|
||||||
|
|
||||||
|
public truncate(target?: QuerySource, alias?: string): Truncate<T> {
|
||||||
|
return new Truncate<T>(target, alias)
|
||||||
}
|
}
|
||||||
|
|
||||||
public static raw(value: string) {
|
public static raw(value: string) {
|
||||||
|
5
orm/src/builder/Scope.ts
Normal file
5
orm/src/builder/Scope.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import {WhereBuilder} from './type/WhereBuilder.ts'
|
||||||
|
|
||||||
|
export abstract class Scope {
|
||||||
|
abstract apply(query: WhereBuilder): WhereBuilder
|
||||||
|
}
|
@ -2,7 +2,7 @@ import {EscapedValue, escape} from './types.ts'
|
|||||||
import {IncorrectInterpolationError} from './Builder.ts'
|
import {IncorrectInterpolationError} from './Builder.ts'
|
||||||
import ConnectionExecutable from './type/ConnectionExecutable.ts'
|
import ConnectionExecutable from './type/ConnectionExecutable.ts'
|
||||||
|
|
||||||
export class Statement extends ConnectionExecutable {
|
export class Statement<T> extends ConnectionExecutable<T> {
|
||||||
constructor(
|
constructor(
|
||||||
public statement: string,
|
public statement: string,
|
||||||
public interpolations: EscapedValue[]
|
public interpolations: EscapedValue[]
|
||||||
|
14
orm/src/builder/scope/FunctionScope.ts
Normal file
14
orm/src/builder/scope/FunctionScope.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import {Scope} from '../Scope.ts'
|
||||||
|
import {WhereBuilder} from '../type/WhereBuilder.ts'
|
||||||
|
|
||||||
|
export type ScopeFunction = (query: WhereBuilder) => WhereBuilder
|
||||||
|
|
||||||
|
export class FunctionScope extends Scope {
|
||||||
|
constructor(protected _fn: ScopeFunction) {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
|
apply(query: WhereBuilder): WhereBuilder {
|
||||||
|
return this._fn(query)
|
||||||
|
}
|
||||||
|
}
|
@ -6,53 +6,59 @@ import {Connection} from '../../db/Connection.ts'
|
|||||||
import {ResultCollection} from './result/ResultCollection.ts'
|
import {ResultCollection} from './result/ResultCollection.ts'
|
||||||
import {ResultIterable} from './result/ResultIterable.ts'
|
import {ResultIterable} from './result/ResultIterable.ts'
|
||||||
import ResultOperator from './result/ResultOperator.ts'
|
import ResultOperator from './result/ResultOperator.ts'
|
||||||
import ObjectResultOperator from './result/ObjectResultOperator.ts'
|
import {Collection} from '../../../../lib/src/collection/Collection.ts'
|
||||||
|
import NoTargetOperatorError from '../../error/NoTargetOperatorError.ts'
|
||||||
|
|
||||||
export default abstract class ConnectionExecutable {
|
export default abstract class ConnectionExecutable<T> {
|
||||||
abstract sql(level: number): string
|
abstract sql(level: number): string
|
||||||
|
|
||||||
to_count(): string {
|
to_count(): string {
|
||||||
return `SELECT COUNT(*) AS to_count FROM (${this.sql(0)}) AS target_query`
|
return `SELECT COUNT(*) AS to_count FROM (${this.sql(0)}) AS target_query`
|
||||||
}
|
}
|
||||||
|
|
||||||
async get_row(i: number) {
|
async get_row(i: number): Promise<T | undefined> {
|
||||||
if ( !(this.__target_connection instanceof Connection) ) {
|
if ( !(this.__target_connection instanceof Connection) ) {
|
||||||
throw new Error('Unable to execute database item: no target connection.')
|
throw new Error('Unable to execute database item: no target connection.')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( !this.__target_operator ) throw new NoTargetOperatorError()
|
||||||
|
|
||||||
const query = `SELECT * FROM (${this.sql(0)}) AS target_query OFFSET ${i} LIMIT 1`
|
const query = `SELECT * FROM (${this.sql(0)}) AS target_query OFFSET ${i} LIMIT 1`
|
||||||
const result = await this.__target_connection.query(query)
|
const result = await this.__target_connection.query(query)
|
||||||
const row = result.rows.first()
|
const row = result.rows.first()
|
||||||
if ( row ) return this.__target_operator.inflate_row(row)
|
if ( row ) return this.__target_operator.inflate_row(row)
|
||||||
}
|
}
|
||||||
|
|
||||||
async get_range(start: number, end: number) {
|
async get_range(start: number, end: number): Promise<Collection<T>> {
|
||||||
if ( !(this.__target_connection instanceof Connection) ) {
|
if ( !(this.__target_connection instanceof Connection) ) {
|
||||||
throw new Error('Unable to execute database item: no target connection.')
|
throw new Error('Unable to execute database item: no target connection.')
|
||||||
}
|
}
|
||||||
|
|
||||||
const query = `SELECT * FROM (${this.sql(0)}) AS target_query OFFSET ${start} LIMIT ${(end - start) + 1}`
|
const query = `SELECT * FROM (${this.sql(0)}) AS target_query OFFSET ${start} LIMIT ${(end - start) + 1}`
|
||||||
const result = await this.__target_connection.query(query)
|
const result = await this.__target_connection.query(query)
|
||||||
return result.rows.map(row => this.__target_operator.inflate_row(row))
|
return result.rows.map(row => {
|
||||||
|
if ( !this.__target_operator ) throw new NoTargetOperatorError()
|
||||||
|
return this.__target_operator.inflate_row(row)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
iterator() {
|
iterator(): ResultIterable<T> {
|
||||||
return new ResultIterable(this)
|
return new ResultIterable<T>(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
results(chunk_size = 1000) {
|
results(chunk_size = 1000) {
|
||||||
return new ResultCollection(this.iterator(), chunk_size)
|
return new ResultCollection<T>(this.iterator(), chunk_size)
|
||||||
}
|
}
|
||||||
|
|
||||||
__target_connection?: Connection
|
__target_connection?: Connection
|
||||||
__target_operator: ResultOperator<any> = new ObjectResultOperator()
|
__target_operator?: ResultOperator<T>
|
||||||
|
|
||||||
target_connection(connection: string | Connection) {
|
target_connection(connection: string | Connection) {
|
||||||
this.__target_connection = typeof connection === 'string' ? make(Database).connection(connection) : connection
|
this.__target_connection = typeof connection === 'string' ? make(Database).connection(connection) : connection
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
target_operator(operator: ResultOperator<any>) {
|
target_operator(operator: ResultOperator<T>) {
|
||||||
this.__target_operator = operator
|
this.__target_operator = operator
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
@ -1,21 +1,26 @@
|
|||||||
import ConnectionExecutable from './ConnectionExecutable.ts'
|
import ConnectionExecutable from './ConnectionExecutable.ts'
|
||||||
import {QueryResult, QueryRow} from "../../db/types.ts";
|
import {QueryResult, QueryRow} from '../../db/types.ts'
|
||||||
import {Connection} from "../../db/Connection.ts";
|
import {Connection} from '../../db/Connection.ts'
|
||||||
import {Collection} from "../../../../lib/src/collection/Collection.ts";
|
import {Collection} from '../../../../lib/src/collection/Collection.ts'
|
||||||
|
import NoTargetOperatorError from '../../error/NoTargetOperatorError.ts'
|
||||||
|
|
||||||
export default abstract class ConnectionMutable extends ConnectionExecutable {
|
export default abstract class ConnectionMutable<T> extends ConnectionExecutable<T> {
|
||||||
__execution_result?: QueryResult
|
__execution_result?: QueryResult
|
||||||
|
|
||||||
async get_row(i: number) {
|
async get_row(i: number): Promise<T | undefined> {
|
||||||
const result = await this.get_execution_result()
|
const result = await this.get_execution_result()
|
||||||
const row = result.rows.at(i)
|
const row = result.rows.at(i)
|
||||||
|
if ( !this.__target_operator ) throw new NoTargetOperatorError()
|
||||||
if ( row ) return this.__target_operator.inflate_row(row)
|
if ( row ) return this.__target_operator.inflate_row(row)
|
||||||
}
|
}
|
||||||
|
|
||||||
async get_range(start: number, end: number) {
|
async get_range(start: number, end: number): Promise<Collection<T>> {
|
||||||
const result = await this.get_execution_result()
|
const result = await this.get_execution_result()
|
||||||
const rows: Collection<QueryRow> = result.rows.slice(start, end + 1) as Collection<QueryRow>
|
const rows: Collection<QueryRow> = result.rows.slice(start, end + 1) as Collection<QueryRow>
|
||||||
return rows.map(row => this.__target_operator.inflate_row(row))
|
return rows.map(row => {
|
||||||
|
if ( !this.__target_operator ) throw new NoTargetOperatorError()
|
||||||
|
return this.__target_operator.inflate_row(row)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async count() {
|
async count() {
|
||||||
|
60
orm/src/builder/type/Delete.ts
Normal file
60
orm/src/builder/type/Delete.ts
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import ConnectionMutable from './ConnectionMutable.ts'
|
||||||
|
import {WhereBuilder} from './WhereBuilder.ts'
|
||||||
|
import {applyMixins} from '../../../../lib/src/support/mixins.ts'
|
||||||
|
import {QuerySource, WhereStatement, FieldSet} from '../types.ts'
|
||||||
|
import {TableRefBuilder} from './TableRefBuilder.ts'
|
||||||
|
import {MalformedSQLGrammarError} from './Select.ts'
|
||||||
|
import {Scope} from '../Scope.ts'
|
||||||
|
|
||||||
|
export class Delete<T> extends ConnectionMutable<T> {
|
||||||
|
protected _target?: QuerySource = undefined
|
||||||
|
protected _wheres: WhereStatement[] = []
|
||||||
|
protected _scopes: Scope[] = []
|
||||||
|
protected _fields: string[] = []
|
||||||
|
protected _only: boolean = false
|
||||||
|
|
||||||
|
sql(level = 0): string {
|
||||||
|
const indent = Array(level * 2).fill(' ').join('')
|
||||||
|
if ( typeof this._target === 'undefined' )
|
||||||
|
throw new MalformedSQLGrammarError('No table reference has been provided.')
|
||||||
|
|
||||||
|
const table_ref = this.source_alias_to_table_ref(this._target)
|
||||||
|
const wheres = this.wheres_to_sql(this._wheres, level + 1)
|
||||||
|
const returning_fields = this._fields.join(', ')
|
||||||
|
|
||||||
|
return [
|
||||||
|
`DELETE FROM ${this._only ? 'ONLY ' : ''}${this.serialize_table_ref(table_ref)}`,
|
||||||
|
...(wheres.trim() ? ['WHERE', wheres] : []),
|
||||||
|
...(returning_fields.trim() ? [`RETURNING ${returning_fields}`] : []),
|
||||||
|
].filter(x => String(x).trim()).join(`\n${indent}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
only() {
|
||||||
|
this._only = true
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
from(source: QuerySource, alias?: string) {
|
||||||
|
if ( !alias ) this._target = source
|
||||||
|
else this._target = { ref: source, alias }
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
returning(...fields: FieldSet[]) {
|
||||||
|
for ( const field_set of fields ) {
|
||||||
|
if ( typeof field_set === 'string' ) {
|
||||||
|
if ( !this._fields.includes(field_set) )
|
||||||
|
this._fields.push(field_set)
|
||||||
|
} else {
|
||||||
|
for ( const field of field_set ) {
|
||||||
|
if ( !this._fields.includes(field) )
|
||||||
|
this._fields.push(field)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Delete<T> extends WhereBuilder, TableRefBuilder {}
|
||||||
|
applyMixins(Delete, [WhereBuilder, TableRefBuilder])
|
@ -8,7 +8,7 @@ import {raw} from '../Builder.ts'
|
|||||||
|
|
||||||
// TODO support DEFAULT VALUES
|
// TODO support DEFAULT VALUES
|
||||||
// TODO support ON CONFLICT
|
// TODO support ON CONFLICT
|
||||||
export class Insert extends ConnectionMutable {
|
export class Insert<T> extends ConnectionMutable<T> {
|
||||||
protected _target?: QuerySource = undefined
|
protected _target?: QuerySource = undefined
|
||||||
protected _columns: string[] = []
|
protected _columns: string[] = []
|
||||||
protected _rows: string[] = []
|
protected _rows: string[] = []
|
||||||
@ -114,5 +114,5 @@ export class Insert extends ConnectionMutable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Insert extends TableRefBuilder {}
|
export interface Insert<T> extends TableRefBuilder {}
|
||||||
applyMixins(Insert, [TableRefBuilder])
|
applyMixins(Insert, [TableRefBuilder])
|
||||||
|
@ -19,16 +19,18 @@ import {RightOuterJoin} from './join/RightOuterJoin.ts'
|
|||||||
import {FullOuterJoin} from './join/FullOuterJoin.ts'
|
import {FullOuterJoin} from './join/FullOuterJoin.ts'
|
||||||
import {HavingBuilder} from './HavingBuilder.ts'
|
import {HavingBuilder} from './HavingBuilder.ts'
|
||||||
import ConnectionExecutable from './ConnectionExecutable.ts'
|
import ConnectionExecutable from './ConnectionExecutable.ts'
|
||||||
|
import {Scope} from '../Scope.ts'
|
||||||
|
|
||||||
export type WhereBuilderFunction = (group: WhereBuilder) => any
|
export type WhereBuilderFunction = (group: WhereBuilder) => any
|
||||||
export type HavingBuilderFunction = (group: HavingBuilder) => any
|
export type HavingBuilderFunction = (group: HavingBuilder) => any
|
||||||
export type JoinFunction = (join: Join) => any
|
export type JoinFunction = (join: Join) => any
|
||||||
export class MalformedSQLGrammarError extends Error {}
|
export class MalformedSQLGrammarError extends Error {}
|
||||||
|
|
||||||
export class Select extends ConnectionExecutable {
|
export class Select<T> extends ConnectionExecutable<T> {
|
||||||
protected _fields: string[] = []
|
protected _fields: string[] = []
|
||||||
protected _source?: QuerySource = undefined
|
protected _source?: QuerySource = undefined
|
||||||
protected _wheres: WhereStatement[] = []
|
protected _wheres: WhereStatement[] = []
|
||||||
|
protected _scopes: Scope[] = []
|
||||||
protected _havings: HavingStatement[] = []
|
protected _havings: HavingStatement[] = []
|
||||||
protected _limit?: number
|
protected _limit?: number
|
||||||
protected _offset?: number
|
protected _offset?: number
|
||||||
@ -200,5 +202,5 @@ export class Select extends ConnectionExecutable {
|
|||||||
// TODO raw()
|
// TODO raw()
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Select extends WhereBuilder, TableRefBuilder, HavingBuilder {}
|
export interface Select<T> extends WhereBuilder, TableRefBuilder, HavingBuilder {}
|
||||||
applyMixins(Select, [WhereBuilder, TableRefBuilder, HavingBuilder])
|
applyMixins(Select, [WhereBuilder, TableRefBuilder, HavingBuilder])
|
||||||
|
72
orm/src/builder/type/Truncate.ts
Normal file
72
orm/src/builder/type/Truncate.ts
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
import ConnectionMutable from "./ConnectionMutable.ts";
|
||||||
|
import {MalformedSQLGrammarError} from "./Select.ts";
|
||||||
|
import {TableRefBuilder} from "./TableRefBuilder.ts";
|
||||||
|
import {applyMixins} from "../../../../lib/src/support/mixins.ts";
|
||||||
|
import {QuerySource} from "../types.ts";
|
||||||
|
|
||||||
|
export class Truncate<T> extends ConnectionMutable<T> {
|
||||||
|
protected _source?: QuerySource
|
||||||
|
protected _only: boolean = false
|
||||||
|
protected _restart: boolean = false
|
||||||
|
protected _continue: boolean = false
|
||||||
|
protected _cascade: boolean = false
|
||||||
|
protected _restrict: boolean = false
|
||||||
|
|
||||||
|
constructor(table?: QuerySource, alias?: string) {
|
||||||
|
super()
|
||||||
|
if ( table ) this.table(table, alias)
|
||||||
|
}
|
||||||
|
|
||||||
|
sql(level: number = 0): string {
|
||||||
|
const indent = Array(level).fill(' ').join('')
|
||||||
|
if ( typeof this._source === 'undefined' )
|
||||||
|
throw new MalformedSQLGrammarError(`No table reference has been provided.`)
|
||||||
|
const table_ref = this.source_alias_to_table_ref(this._source)
|
||||||
|
|
||||||
|
let identity = ''
|
||||||
|
if ( this._continue ) identity = 'CONTINUE IDENTITY '
|
||||||
|
else if ( this._restart ) identity = 'RESTART IDENTITY '
|
||||||
|
|
||||||
|
let cascade_redirect = ''
|
||||||
|
if ( this._cascade ) cascade_redirect = 'CASCADE '
|
||||||
|
else if ( this._restrict ) cascade_redirect = 'RESTRICT'
|
||||||
|
|
||||||
|
return [
|
||||||
|
`TRUNCATE TABLE ${this._only ? 'ONLY ' : ''}${this.serialize_table_ref(table_ref)}`,
|
||||||
|
`${identity}${cascade_redirect}`,
|
||||||
|
].filter(x => String(x).trim()).join(`\n${indent}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
table(source: QuerySource, alias?: string) {
|
||||||
|
if ( !alias ) this._source = source
|
||||||
|
else this._source = { ref: source, alias }
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
restart_identity() {
|
||||||
|
this._continue = false
|
||||||
|
this._restart = true
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
continue_identity() {
|
||||||
|
this._continue = true
|
||||||
|
this._restart = false
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
cascade() {
|
||||||
|
this._cascade = true
|
||||||
|
this._restrict = false
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
restrict() {
|
||||||
|
this._cascade = false
|
||||||
|
this._restrict = true
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Truncate<T> extends TableRefBuilder {}
|
||||||
|
applyMixins(Truncate, [TableRefBuilder])
|
@ -6,14 +6,16 @@ import {applyMixins} from '../../../../lib/src/support/mixins.ts'
|
|||||||
import {TableRefBuilder} from './TableRefBuilder.ts'
|
import {TableRefBuilder} from './TableRefBuilder.ts'
|
||||||
import {MalformedSQLGrammarError} from './Select.ts'
|
import {MalformedSQLGrammarError} from './Select.ts'
|
||||||
import ConnectionMutable from './ConnectionMutable.ts'
|
import ConnectionMutable from './ConnectionMutable.ts'
|
||||||
|
import {Scope} from '../Scope.ts'
|
||||||
|
|
||||||
// TODO FROM
|
// TODO FROM
|
||||||
// TODO WHERE CURRENT OF
|
// TODO WHERE CURRENT OF
|
||||||
export class Update extends ConnectionMutable {
|
export class Update<T> extends ConnectionMutable<T> {
|
||||||
protected _target?: QuerySource = undefined
|
protected _target?: QuerySource = undefined
|
||||||
protected _only = false
|
protected _only = false
|
||||||
protected _sets: Collection<FieldValue> = new Collection<FieldValue>()
|
protected _sets: Collection<FieldValue> = new Collection<FieldValue>()
|
||||||
protected _wheres: WhereStatement[] = []
|
protected _wheres: WhereStatement[] = []
|
||||||
|
protected _scopes: Scope[] = []
|
||||||
protected _fields: string[] = []
|
protected _fields: string[] = []
|
||||||
|
|
||||||
sql(level = 0): string {
|
sql(level = 0): string {
|
||||||
@ -84,5 +86,5 @@ export class Update extends ConnectionMutable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Update extends WhereBuilder, TableRefBuilder {}
|
export interface Update<T> extends WhereBuilder, TableRefBuilder {}
|
||||||
applyMixins(Update, [WhereBuilder, TableRefBuilder])
|
applyMixins(Update, [WhereBuilder, TableRefBuilder])
|
||||||
|
@ -1,15 +1,40 @@
|
|||||||
import {EscapedValue, isWhereClause, isWhereGroup, WhereStatement} from '../types.ts'
|
import {EscapedValue, isWhereClause, isWhereGroup, WhereStatement} from '../types.ts'
|
||||||
import {escape, SQLWhereOperator, WherePreOperator} from '../types.ts'
|
import {escape, SQLWhereOperator, WherePreOperator} from '../types.ts'
|
||||||
import {WhereBuilderFunction} from "./Select.ts";
|
import {WhereBuilderFunction} from './Select.ts'
|
||||||
|
import {apply_filter_to_where, QueryFilter} from '../../model/filter.ts'
|
||||||
|
import {Scope} from '../Scope.ts'
|
||||||
|
import {FunctionScope, ScopeFunction} from '../scope/FunctionScope.ts'
|
||||||
|
import {make} from '../../../../di/src/global.ts'
|
||||||
|
|
||||||
export class WhereBuilder {
|
export class WhereBuilder {
|
||||||
protected _wheres: WhereStatement[] = []
|
protected _wheres: WhereStatement[] = []
|
||||||
|
protected _scopes: Scope[] = []
|
||||||
|
|
||||||
get where_items() {
|
get where_items() {
|
||||||
return this._wheres
|
return this._wheres
|
||||||
}
|
}
|
||||||
|
|
||||||
|
without_scope(scope: typeof Scope) {
|
||||||
|
this._scopes = this._scopes.filter(x => !(x instanceof Scope))
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
with_scope(scope: Scope | ScopeFunction) {
|
||||||
|
if ( scope instanceof Scope ) {
|
||||||
|
this._scopes.push(scope)
|
||||||
|
} else {
|
||||||
|
this._scopes.push(new FunctionScope(scope))
|
||||||
|
}
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
with_scopes(scopes: (Scope | ScopeFunction)[]) {
|
||||||
|
scopes.forEach(scope => this.with_scope(scope))
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
wheres_to_sql(wheres?: WhereStatement[], level = 0): string {
|
wheres_to_sql(wheres?: WhereStatement[], level = 0): string {
|
||||||
|
this._scopes.forEach(scope => scope.apply(this))
|
||||||
const indent = Array(level * 2).fill(' ').join('')
|
const indent = Array(level * 2).fill(' ').join('')
|
||||||
let statements = []
|
let statements = []
|
||||||
for ( const where of wheres || this._wheres ) {
|
for ( const where of wheres || this._wheres ) {
|
||||||
@ -97,4 +122,48 @@ export class WhereBuilder {
|
|||||||
})
|
})
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
whereBetween(field: string, lower_bound: EscapedValue, upper_bound: EscapedValue) {
|
||||||
|
this._wheres.push({
|
||||||
|
field,
|
||||||
|
operator: 'BETWEEN',
|
||||||
|
operand: `${escape(lower_bound)} AND ${escape(upper_bound)}`,
|
||||||
|
preop: 'AND',
|
||||||
|
})
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
orWhereBetween(field: string, lower_bound: EscapedValue, upper_bound: EscapedValue) {
|
||||||
|
this._wheres.push({
|
||||||
|
field,
|
||||||
|
operator: 'BETWEEN',
|
||||||
|
operand: `${escape(lower_bound)} AND ${escape(upper_bound)}`,
|
||||||
|
preop: 'OR',
|
||||||
|
})
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
whereNotBetween(field: string, lower_bound: EscapedValue, upper_bound: EscapedValue) {
|
||||||
|
this._wheres.push({
|
||||||
|
field,
|
||||||
|
operator: 'NOT BETWEEN',
|
||||||
|
operand: `${escape(lower_bound)} AND ${escape(upper_bound)}`,
|
||||||
|
preop: 'AND',
|
||||||
|
})
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
orWhereNotBetween(field: string, lower_bound: EscapedValue, upper_bound: EscapedValue) {
|
||||||
|
this._wheres.push({
|
||||||
|
field,
|
||||||
|
operator: 'NOT BETWEEN',
|
||||||
|
operand: `${escape(lower_bound)} AND ${escape(upper_bound)}`,
|
||||||
|
preop: 'OR',
|
||||||
|
})
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
filter(filter: QueryFilter) {
|
||||||
|
return apply_filter_to_where(filter, this)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,12 @@ import {JoinOperator, TableRef, WhereStatement} from '../../types.ts'
|
|||||||
import {TableRefBuilder} from '../TableRefBuilder.ts'
|
import {TableRefBuilder} from '../TableRefBuilder.ts'
|
||||||
import {applyMixins} from '../../../../../lib/src/support/mixins.ts'
|
import {applyMixins} from '../../../../../lib/src/support/mixins.ts'
|
||||||
import {WhereBuilder} from '../WhereBuilder.ts'
|
import {WhereBuilder} from '../WhereBuilder.ts'
|
||||||
|
import {Scope} from '../../Scope.ts'
|
||||||
|
|
||||||
export class Join {
|
export class Join {
|
||||||
public readonly operator: JoinOperator = 'JOIN'
|
public readonly operator: JoinOperator = 'JOIN'
|
||||||
protected _wheres: WhereStatement[] = []
|
protected _wheres: WhereStatement[] = []
|
||||||
|
protected _scopes: Scope[] = []
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public readonly table_ref: TableRef
|
public readonly table_ref: TableRef
|
||||||
|
@ -1,21 +1,21 @@
|
|||||||
import {AsyncCollection} from '../../../../../lib/src/collection/AsyncCollection.ts'
|
import {AsyncCollection} from '../../../../../lib/src/collection/AsyncCollection.ts'
|
||||||
import {ResultIterable} from './ResultIterable.ts'
|
import {ResultIterable} from './ResultIterable.ts'
|
||||||
import {Collection} from "../../../../../lib/src/collection/Collection.ts";
|
import {Collection} from '../../../../../lib/src/collection/Collection.ts'
|
||||||
|
|
||||||
export class ResultCollection extends AsyncCollection<any> {
|
export class ResultCollection<T> extends AsyncCollection<T> {
|
||||||
constructor(
|
constructor(
|
||||||
executable: ResultIterable,
|
executable: ResultIterable<T>,
|
||||||
chunk_size: number = 1000
|
chunk_size: number = 1000
|
||||||
) {
|
) {
|
||||||
super(executable, chunk_size)
|
super(executable, chunk_size)
|
||||||
}
|
}
|
||||||
|
|
||||||
then(func?: (items: Collection<any>) => any) {
|
then(func?: (items: Collection<T>) => any) {
|
||||||
if ( func ) {
|
if ( func ) {
|
||||||
this.collect().then((items: Collection<any>) => func(items))
|
this.collect().then((items: Collection<T>) => func(items))
|
||||||
} else {
|
} else {
|
||||||
return new Promise(res => [
|
return new Promise(res => [
|
||||||
this.collect().then((items: Collection<any>) => res(items))
|
this.collect().then((items: Collection<T>) => res(items))
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,19 @@
|
|||||||
import {Iterable} from '../../../../../lib/src/collection/Iterable.ts'
|
import {Iterable} from '../../../../../lib/src/collection/Iterable.ts'
|
||||||
import ConnectionExecutable from '../ConnectionExecutable.ts'
|
import ConnectionExecutable from '../ConnectionExecutable.ts'
|
||||||
import {Collection} from '../../../../../lib/src/collection/Collection.ts'
|
import {Collection} from '../../../../../lib/src/collection/Collection.ts'
|
||||||
|
import {QueryRow} from '../../../db/types.ts'
|
||||||
|
|
||||||
export class ResultIterable extends Iterable<any> {
|
export class ResultIterable<T> extends Iterable<T> {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected executable: ConnectionExecutable
|
protected executable: ConnectionExecutable<T>
|
||||||
) { super() }
|
) { super() }
|
||||||
|
|
||||||
async at_index(i: number): Promise<any> {
|
async at_index(i: number): Promise<T | undefined> {
|
||||||
return this.executable.get_row(i)
|
return this.executable.get_row(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
async from_range(start: number, end: number): Promise<Collection<any>> {
|
async from_range(start: number, end: number): Promise<Collection<T>> {
|
||||||
return this.executable.get_range(start, end)
|
return this.executable.get_range(start, end)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ export type WherePreOperator = 'AND' | 'OR' | 'AND NOT' | 'OR NOT'
|
|||||||
export type WhereClause = { field: string, operator: SQLWhereOperator, operand: string, preop: WherePreOperator }
|
export type WhereClause = { field: string, operator: SQLWhereOperator, operand: string, preop: WherePreOperator }
|
||||||
export type WhereGroup = { items: WhereStatement[], preop: WherePreOperator }
|
export type WhereGroup = { items: WhereStatement[], preop: WherePreOperator }
|
||||||
export type WhereStatement = WhereClause | WhereGroup
|
export type WhereStatement = WhereClause | WhereGroup
|
||||||
export type SQLWhereOperator = WhereOperator | 'IN' | 'NOT IN' | 'LIKE'
|
export type SQLWhereOperator = WhereOperator | 'IN' | 'NOT IN' | 'LIKE' | 'BETWEEN' | 'NOT BETWEEN'
|
||||||
export type OrderDirection = 'ASC' | 'DESC'
|
export type OrderDirection = 'ASC' | 'DESC'
|
||||||
export type OrderStatement = { direction: OrderDirection, field: string }
|
export type OrderStatement = { direction: OrderDirection, field: string }
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { Collection } from '../../../lib/src/collection/Collection.ts'
|
import { Collection } from '../../../lib/src/collection/Collection.ts'
|
||||||
|
|
||||||
export type QueryRow = { [key: string]: any }
|
export type QueryRow = { [key: string]: any }
|
||||||
|
export type ModelKey = string | number
|
||||||
|
|
||||||
export interface QueryResult {
|
export interface QueryResult {
|
||||||
rows: Collection<QueryRow>,
|
rows: Collection<QueryRow>,
|
||||||
|
5
orm/src/error/NoTargetOperatorError.ts
Normal file
5
orm/src/error/NoTargetOperatorError.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export default class NoTargetOperatorError extends Error {
|
||||||
|
constructor(msg = 'This query has no defined target operator.') {
|
||||||
|
super(msg)
|
||||||
|
}
|
||||||
|
}
|
@ -2,26 +2,43 @@ import { Builder } from '../builder/Builder.ts'
|
|||||||
import {FieldSet, FieldValueObject, QuerySource} from '../builder/types.ts'
|
import {FieldSet, FieldValueObject, QuerySource} from '../builder/types.ts'
|
||||||
import {make} from '../../../di/src/global.ts'
|
import {make} from '../../../di/src/global.ts'
|
||||||
import Database from '../service/Database.ts'
|
import Database from '../service/Database.ts'
|
||||||
import {QueryRow} from '../db/types.ts'
|
import {QueryRow, ModelKey} from '../db/types.ts'
|
||||||
import ModelResultOperator from './ModelResultOperator.ts'
|
import ModelResultOperator from './ModelResultOperator.ts'
|
||||||
import {get_fields_meta, ModelField, set_model_fields_meta} from './Field.ts'
|
import {get_fields_meta, ModelField, set_model_fields_meta} from './Field.ts'
|
||||||
import {Collection} from '../../../lib/src/collection/Collection.ts'
|
import {Collection} from '../../../lib/src/collection/Collection.ts'
|
||||||
import {logger} from '../../../lib/src/service/logging/global.ts'
|
import {logger} from '../../../lib/src/service/logging/global.ts'
|
||||||
import ObjectResultOperator from '../builder/type/result/ObjectResultOperator.ts'
|
import ObjectResultOperator from '../builder/type/result/ObjectResultOperator.ts'
|
||||||
|
import {QueryFilter} from './filter.ts'
|
||||||
|
import {BehaviorSubject} from '../../../lib/src/support/BehaviorSubject.ts'
|
||||||
|
import {Scope} from '../builder/Scope.ts'
|
||||||
|
|
||||||
// TODO separate read/write connections
|
// TODO separate read/write connections
|
||||||
// TODO manual dirty flags
|
// TODO manual dirty flags
|
||||||
export abstract class Model extends Builder {
|
export abstract class Model<T extends Model<T>> extends Builder<T> {
|
||||||
protected static connection: string
|
protected static connection: string
|
||||||
protected static table: string
|
protected static table: string
|
||||||
protected static key: string
|
protected static key: string
|
||||||
|
|
||||||
protected static readonly CREATED_AT = 'created_at'
|
protected static readonly CREATED_AT = 'created_at'
|
||||||
protected static readonly UPDATED_AT = 'updated_at'
|
protected static readonly UPDATED_AT = 'updated_at'
|
||||||
protected static timestamps = false
|
protected static timestamps = true
|
||||||
|
|
||||||
|
protected static global_scopes: Scope[] = []
|
||||||
|
protected static appends: string[] = []
|
||||||
|
protected static masks: string[] = []
|
||||||
|
|
||||||
protected _original?: QueryRow
|
protected _original?: QueryRow
|
||||||
|
|
||||||
|
protected retrieved$ = new BehaviorSubject<Model<T>>()
|
||||||
|
protected saving$ = new BehaviorSubject<Model<T>>()
|
||||||
|
protected saved$ = new BehaviorSubject<Model<T>>()
|
||||||
|
protected updating$ = new BehaviorSubject<Model<T>>()
|
||||||
|
protected updated$ = new BehaviorSubject<Model<T>>()
|
||||||
|
protected creating$ = new BehaviorSubject<Model<T>>()
|
||||||
|
protected created$ = new BehaviorSubject<Model<T>>()
|
||||||
|
protected deleting$ = new BehaviorSubject<Model<T>>()
|
||||||
|
protected deleted$ = new BehaviorSubject<Model<T>>()
|
||||||
|
|
||||||
public static table_name() {
|
public static table_name() {
|
||||||
return this.table
|
return this.table
|
||||||
}
|
}
|
||||||
@ -46,10 +63,19 @@ export abstract class Model extends Builder {
|
|||||||
return this.prototype.insert()
|
return this.prototype.insert()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static delete() {
|
||||||
|
return this.prototype.delete()
|
||||||
|
}
|
||||||
|
|
||||||
|
public static truncate() {
|
||||||
|
return this.prototype.truncate()
|
||||||
|
}
|
||||||
|
|
||||||
public update(target?: QuerySource, alias?: string) {
|
public update(target?: QuerySource, alias?: string) {
|
||||||
const constructor = (this.constructor as typeof Model)
|
const constructor = (this.constructor as typeof Model)
|
||||||
return super.update()
|
return super.update()
|
||||||
.to(constructor.table_name())
|
.to(constructor.table_name())
|
||||||
|
.with_scopes(constructor.global_scopes)
|
||||||
.target_connection(constructor.get_connection())
|
.target_connection(constructor.get_connection())
|
||||||
.target_operator(make(ModelResultOperator, constructor))
|
.target_operator(make(ModelResultOperator, constructor))
|
||||||
}
|
}
|
||||||
@ -58,6 +84,7 @@ export abstract class Model extends Builder {
|
|||||||
const constructor = (this.constructor as typeof Model)
|
const constructor = (this.constructor as typeof Model)
|
||||||
return super.select(...fields)
|
return super.select(...fields)
|
||||||
.from(constructor.table_name())
|
.from(constructor.table_name())
|
||||||
|
.with_scopes(constructor.global_scopes)
|
||||||
.target_connection(constructor.get_connection())
|
.target_connection(constructor.get_connection())
|
||||||
.target_operator(make(ModelResultOperator, constructor))
|
.target_operator(make(ModelResultOperator, constructor))
|
||||||
}
|
}
|
||||||
@ -70,6 +97,23 @@ export abstract class Model extends Builder {
|
|||||||
.target_operator(make(ModelResultOperator, constructor))
|
.target_operator(make(ModelResultOperator, constructor))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public delete(target?: QuerySource, alias?: string) {
|
||||||
|
const constructor = (this.constructor as typeof Model)
|
||||||
|
return super.delete()
|
||||||
|
.from(constructor.table_name())
|
||||||
|
.with_scopes(constructor.global_scopes)
|
||||||
|
.target_connection(constructor.get_connection())
|
||||||
|
.target_operator(make(ModelResultOperator, constructor))
|
||||||
|
}
|
||||||
|
|
||||||
|
public truncate(target?: QuerySource, alias?: string) {
|
||||||
|
const constructor = (this.constructor as typeof Model)
|
||||||
|
return super.truncate()
|
||||||
|
.table(constructor.table_name())
|
||||||
|
.target_connection(constructor.get_connection())
|
||||||
|
.target_operator(make(ObjectResultOperator))
|
||||||
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
values?: any
|
values?: any
|
||||||
) {
|
) {
|
||||||
@ -94,22 +138,52 @@ export abstract class Model extends Builder {
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
this[field_def.model_key] = row[field_def.database_key]
|
this[field_def.model_key] = row[field_def.database_key]
|
||||||
})
|
})
|
||||||
|
this.retrieved$.next(this)
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
// changes
|
public timestamps(): { updated_at?: Date, created_at?: Date } {
|
||||||
// date format
|
const constructor = (this.constructor as typeof Model)
|
||||||
// appends
|
const timestamps: { updated_at?: Date, created_at?: Date } = {}
|
||||||
// caching
|
if ( constructor.timestamps ) {
|
||||||
// relations
|
// @ts-ignore
|
||||||
// timestamps
|
if ( constructor.CREATED_AT ) timestamps.created_at = this[constructor.CREATED_AT]
|
||||||
// hidden
|
|
||||||
// fillable
|
// @ts-ignore
|
||||||
// guarded
|
if ( constructor.UPDATED_AT ) timestamps.updated_at = this[constructor.UPDATED_AT]
|
||||||
// key type
|
}
|
||||||
// with
|
return timestamps
|
||||||
// per page
|
}
|
||||||
// exists
|
|
||||||
|
public static model_select(...other_fields: FieldSet[]) {
|
||||||
|
const fields = get_fields_meta(this.prototype).pluck('database_key').toArray()
|
||||||
|
return this.select(...[...fields, ...other_fields])
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async find(filter: QueryFilter = {}) {
|
||||||
|
// @ts-ignore
|
||||||
|
return await this.model_select().filter(filter).results()
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async find_one(filter: QueryFilter = {}) {
|
||||||
|
// @ts-ignore
|
||||||
|
const result = await this.model_select().filter(filter).limit(1).results()
|
||||||
|
return result.first()
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async find_by_key(key: ModelKey): Promise<undefined | typeof Model> {
|
||||||
|
const result = await this.model_select()
|
||||||
|
.where(this.qualified_key_name(), '=', key)
|
||||||
|
.limit(1)
|
||||||
|
.results()
|
||||||
|
|
||||||
|
return result.first()
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async count(): Promise<number> {
|
||||||
|
const result = this.model_select().results()
|
||||||
|
return await result.count()
|
||||||
|
}
|
||||||
|
|
||||||
protected get _is_dirty() {
|
protected get _is_dirty() {
|
||||||
return (field_def: ModelField) => {
|
return (field_def: ModelField) => {
|
||||||
@ -118,18 +192,16 @@ export abstract class Model extends Builder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// to_row
|
|
||||||
public to_row(): QueryRow {
|
public to_row(): QueryRow {
|
||||||
const data = {}
|
const row = {}
|
||||||
const meta = (this.constructor as typeof Model).fields()
|
this.field_defs()
|
||||||
meta.each(field => {
|
.each(field_def => {
|
||||||
|
// @ts-ignore
|
||||||
|
row[field_def.database_key] = this[field_def.model_key]
|
||||||
})
|
})
|
||||||
return {}
|
return row
|
||||||
}
|
}
|
||||||
|
|
||||||
// relations_to_row
|
|
||||||
// dirty_to_row
|
|
||||||
public dirty_to_row(): QueryRow {
|
public dirty_to_row(): QueryRow {
|
||||||
const row = {}
|
const row = {}
|
||||||
this.field_defs()
|
this.field_defs()
|
||||||
@ -142,8 +214,6 @@ export abstract class Model extends Builder {
|
|||||||
return row
|
return row
|
||||||
}
|
}
|
||||||
|
|
||||||
// attributes
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a collection of field definitions that contains information
|
* Get a collection of field definitions that contains information
|
||||||
* on which database fields correspond to which model fields, and
|
* on which database fields correspond to which model fields, and
|
||||||
@ -229,10 +299,10 @@ export abstract class Model extends Builder {
|
|||||||
return !this.is_dirty()
|
return !this.is_dirty()
|
||||||
}
|
}
|
||||||
|
|
||||||
// was changed - pass attribute(s)
|
public was_changed(field: string) {
|
||||||
// observe/observers - retrieved, saving, saved, updating, updated, creating, created, deleting, deleted
|
// @ts-ignore
|
||||||
// global scopes
|
return this.field_defs().pluck('model_key').includes(field) && this[field] !== this._original[field]
|
||||||
// non-global scopes
|
}
|
||||||
|
|
||||||
// has one
|
// has one
|
||||||
// morph one
|
// morph one
|
||||||
@ -244,28 +314,48 @@ export abstract class Model extends Builder {
|
|||||||
// morph to many
|
// morph to many
|
||||||
// morphed by many
|
// morphed by many
|
||||||
// is relation loaded
|
// is relation loaded
|
||||||
// touch - update update timestamp, created if necessary
|
|
||||||
// touch created - update update and created timestamp
|
|
||||||
// set created at/set updated at
|
|
||||||
// is fillable
|
|
||||||
// is guarded
|
|
||||||
// without touching
|
|
||||||
|
|
||||||
// all
|
|
||||||
// load relations
|
// load relations
|
||||||
// load missing relations
|
// load missing relations
|
||||||
// increment column
|
// relations
|
||||||
// decrement column
|
// with
|
||||||
|
// relations_to_row
|
||||||
|
|
||||||
// update - bulk
|
// support for soft deletes!
|
||||||
// push - update single
|
// force delete - for soft deleted models
|
||||||
|
|
||||||
// save - update or create instance
|
public touch() {
|
||||||
public async save(): Promise<Model> {
|
const constructor = (this.constructor as typeof Model)
|
||||||
|
if ( constructor.timestamps ) {
|
||||||
|
if ( this.exists() && constructor.UPDATED_AT ) {
|
||||||
|
// @ts-ignore
|
||||||
|
this[constructor.UPDATED_AT] = new Date()
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( constructor.CREATED_AT ) {
|
||||||
|
// @ts-ignore
|
||||||
|
this[constructor.CREATED_AT] = new Date()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
public static all() {
|
||||||
|
return this.model_select().results()
|
||||||
|
}
|
||||||
|
|
||||||
|
public async save({ without_timestamps = false }): Promise<Model<T>> {
|
||||||
|
await this.saving$.next(this)
|
||||||
const constructor = (this.constructor as typeof Model)
|
const constructor = (this.constructor as typeof Model)
|
||||||
|
|
||||||
// TODO timestamps
|
// TODO timestamps
|
||||||
if ( this.exists() && this.is_dirty() ) { // We're updating an existing record
|
if ( this.exists() && this.is_dirty() ) { // We're updating an existing record
|
||||||
|
await this.updating$.next(this)
|
||||||
|
|
||||||
|
if ( !without_timestamps && constructor.timestamps && constructor.UPDATED_AT ) {
|
||||||
|
// @ts-ignore
|
||||||
|
this[constructor.UPDATED_AT] = new Date()
|
||||||
|
}
|
||||||
|
|
||||||
const mutable = this.update()
|
const mutable = this.update()
|
||||||
.data(this.dirty_to_row())
|
.data(this.dirty_to_row())
|
||||||
.where(constructor.qualified_key_name(), '=', this.key())
|
.where(constructor.qualified_key_name(), '=', this.key())
|
||||||
@ -280,8 +370,22 @@ export abstract class Model extends Builder {
|
|||||||
logger.warn(`Model update modified ${modified_rows} rows! (Key: ${constructor.qualified_key_name()})`)
|
logger.warn(`Model update modified ${modified_rows} rows! (Key: ${constructor.qualified_key_name()})`)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.assume_from_source(result.firstWhere(this.key_name(), '=', this.key()))
|
const model = result.firstWhere(this.key_name(), '=', this.key())
|
||||||
|
if ( model ) this.assume_from_source(model)
|
||||||
|
await this.updated$.next(this)
|
||||||
} else if ( !this.exists() ) { // We're inserting a new record
|
} else if ( !this.exists() ) { // We're inserting a new record
|
||||||
|
await this.creating$.next(this)
|
||||||
|
|
||||||
|
if ( !without_timestamps && constructor.timestamps && constructor.CREATED_AT ) {
|
||||||
|
// @ts-ignore
|
||||||
|
this[constructor.CREATED_AT] = new Date()
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( constructor.timestamps && constructor.UPDATED_AT ) {
|
||||||
|
// @ts-ignore
|
||||||
|
this[constructor.UPDATED_AT] = new Date()
|
||||||
|
}
|
||||||
|
|
||||||
const insert_object: FieldValueObject = this._build_insert_field_object()
|
const insert_object: FieldValueObject = this._build_insert_field_object()
|
||||||
const mutable = this.insert()
|
const mutable = this.insert()
|
||||||
.row(insert_object)
|
.row(insert_object)
|
||||||
@ -296,18 +400,21 @@ export abstract class Model extends Builder {
|
|||||||
logger.warn(`Model insert created ${inserted_rows} rows! (Key: ${constructor.qualified_key_name()})`)
|
logger.warn(`Model insert created ${inserted_rows} rows! (Key: ${constructor.qualified_key_name()})`)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.assume_from_source(result.first())
|
const model = result.first()
|
||||||
|
if ( model ) this.assume_from_source(model)
|
||||||
|
await this.created$.next(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await this.saved$.next(this)
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
protected _build_insert_field_object(): FieldValueObject {
|
protected _build_insert_field_object(): FieldValueObject {
|
||||||
const fields = this.field_defs()
|
const fields = this.field_defs().whereNot('model_key', '=', this.key_name())
|
||||||
const values = {}
|
const values = {}
|
||||||
fields.each(field_def => {
|
fields.each(field_def => {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
values[field_def.database_key] = this[field_def.model_key]
|
values[field_def.database_key] = this[field_def.model_key] ?? null
|
||||||
})
|
})
|
||||||
return values
|
return values
|
||||||
}
|
}
|
||||||
@ -317,20 +424,112 @@ export abstract class Model extends Builder {
|
|||||||
return Object.keys(this._original).map(String)
|
return Object.keys(this._original).map(String)
|
||||||
}
|
}
|
||||||
|
|
||||||
// destroy - bulk
|
public static async destroy(id_or_ids: ModelKey | ModelKey[]) {
|
||||||
// delete single
|
const ids = Array.isArray(id_or_ids) ? id_or_ids : [id_or_ids]
|
||||||
// force delete - for soft deleted models
|
const constructor = (this.constructor as typeof Model)
|
||||||
// without scope
|
const mutable = this.delete()
|
||||||
// without global scope
|
.whereIn(constructor.qualified_key_name(), ids)
|
||||||
// without global scopes
|
.target_operator(make(ObjectResultOperator))
|
||||||
|
.results()
|
||||||
|
|
||||||
// to object
|
const result = await mutable
|
||||||
// to json
|
|
||||||
// fresh - get new instance of this model
|
const modified_rows = await mutable.count()
|
||||||
// refresh - reload this instance
|
if ( modified_rows !== ids.length ) {
|
||||||
// replicate to new instance
|
logger.warn(`Model bulk destroy modified ${modified_rows} when ${ids.length} keys were provided. (Key: ${constructor.qualified_key_name()})`)
|
||||||
// is - check if two models are the same
|
}
|
||||||
// isNot
|
}
|
||||||
|
|
||||||
|
public async destroy(): Promise<void> {
|
||||||
|
await this.deleting$.next(this)
|
||||||
|
|
||||||
|
const constructor = (this.constructor as typeof Model)
|
||||||
|
const mutable = this.delete()
|
||||||
|
.where(constructor.qualified_key_name, '=', this.key())
|
||||||
|
.target_operator(make(ObjectResultOperator))
|
||||||
|
.results()
|
||||||
|
|
||||||
|
const result = await mutable
|
||||||
|
|
||||||
|
const modified_rows = await mutable.count()
|
||||||
|
if ( modified_rows !== 1 ) {
|
||||||
|
logger.warn(`Model delete modified ${modified_rows} rows! (Key: ${constructor.qualified_key_name()})`)
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.deleted$.next(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
public to_object(): { [key: string]: any } {
|
||||||
|
const constructor = (this.constructor as typeof Model)
|
||||||
|
const obj = {}
|
||||||
|
this.field_defs()
|
||||||
|
.each(field_def => {
|
||||||
|
// @ts-ignore
|
||||||
|
obj[field_def.model_key] = this[field_def.model_key]
|
||||||
|
})
|
||||||
|
|
||||||
|
constructor.appends.forEach(appended_field => {
|
||||||
|
// @ts-ignore
|
||||||
|
obj[appended_field] = this[appended_field]
|
||||||
|
})
|
||||||
|
|
||||||
|
constructor.masks.forEach(masked_field => {
|
||||||
|
// @ts-ignore
|
||||||
|
delete obj[masked_field]
|
||||||
|
})
|
||||||
|
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
|
||||||
|
public to_json(): string {
|
||||||
|
return JSON.stringify(this.to_object())
|
||||||
|
}
|
||||||
|
|
||||||
|
public async fresh(): Promise<Model<T>> {
|
||||||
|
const constructor = (this.constructor as typeof Model)
|
||||||
|
const fields = this.field_defs()
|
||||||
|
.whereNot('model_key', '=', this.key_name())
|
||||||
|
.pluck('database_key')
|
||||||
|
.toArray()
|
||||||
|
|
||||||
|
const result = await constructor.select(...fields)
|
||||||
|
.where(constructor.qualified_key_name(), '=', this.key())
|
||||||
|
.limit(1)
|
||||||
|
.results()
|
||||||
|
return result.first()
|
||||||
|
}
|
||||||
|
|
||||||
|
public async refresh() {
|
||||||
|
const constructor = (this.constructor as typeof Model)
|
||||||
|
const values = await this.select(this._loaded_database_fields())
|
||||||
|
.where(constructor.qualified_key_name(), '=', this.key())
|
||||||
|
.limit(1)
|
||||||
|
.target_operator(make(ObjectResultOperator))
|
||||||
|
.results()
|
||||||
|
|
||||||
|
const row = values.first()
|
||||||
|
if ( row ) this.assume_from_source(row)
|
||||||
|
}
|
||||||
|
|
||||||
|
public populate(other: Model<T>): Model<T> {
|
||||||
|
const row = this.to_row()
|
||||||
|
delete row[this.key_name()]
|
||||||
|
other.assume_from_source(row)
|
||||||
|
return other
|
||||||
|
}
|
||||||
|
|
||||||
|
public is(other_instance: Model<any>): boolean {
|
||||||
|
const this_constructor = (this.constructor as typeof Model)
|
||||||
|
const other_constructor = (other_instance.constructor as typeof Model)
|
||||||
|
return (
|
||||||
|
other_instance.key() === this.key()
|
||||||
|
&& this_constructor.qualified_key_name() === other_constructor.qualified_key_name()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
public is_not(other_instance: Model<any>): boolean {
|
||||||
|
return !this.is(other_instance)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the field name of the primary key for this model.
|
* Returns the field name of the primary key for this model.
|
||||||
@ -343,7 +542,7 @@ export abstract class Model extends Builder {
|
|||||||
/**
|
/**
|
||||||
* If defined, returns the value of the primary key for this model.
|
* If defined, returns the value of the primary key for this model.
|
||||||
*/
|
*/
|
||||||
public key() {
|
public key(): ModelKey {
|
||||||
return this?._original?.[(this.constructor as typeof Model).key]
|
return this?._original?.[(this.constructor as typeof Model).key]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,25 +1,23 @@
|
|||||||
import ResultOperator from '../builder/type/result/ResultOperator.ts'
|
import ResultOperator from '../builder/type/result/ResultOperator.ts'
|
||||||
import {Model} from './Model.ts'
|
import {Model} from './Model.ts'
|
||||||
import {Injectable} from '../../../di/src/decorator/Injection.ts'
|
import {Injectable} from '../../../di/src/decorator/Injection.ts'
|
||||||
import {Container} from '../../../di/src/Container.ts'
|
|
||||||
import {QueryRow} from '../db/types.ts'
|
import {QueryRow} from '../db/types.ts'
|
||||||
import Instantiable from '../../../di/src/type/Instantiable.ts'
|
import Instantiable from '../../../di/src/type/Instantiable.ts'
|
||||||
|
import {make} from '../../../di/src/global.ts'
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export default class ModelResultOperator extends ResultOperator<Model> {
|
export default class ModelResultOperator<T extends Model<T>> extends ResultOperator<T> {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected injector: Container,
|
protected ModelClass: Instantiable<T>,
|
||||||
protected ModelClass: Instantiable<Model>,
|
|
||||||
) {
|
) {
|
||||||
super();
|
super()
|
||||||
}
|
}
|
||||||
|
|
||||||
inflate_row(row: QueryRow): Model {
|
inflate_row(row: QueryRow): T {
|
||||||
return this.injector.make(this.ModelClass).assume_from_source(row)
|
return make(this.ModelClass).assume_from_source(row)
|
||||||
}
|
}
|
||||||
|
|
||||||
deflate_row(item: Model): QueryRow {
|
deflate_row(item: T): QueryRow {
|
||||||
return item.to_row()
|
return item.to_row()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
54
orm/src/model/filter.ts
Normal file
54
orm/src/model/filter.ts
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import {WhereBuilder} from '../builder/type/WhereBuilder.ts'
|
||||||
|
import {logger} from '../../../lib/src/service/logging/global.ts'
|
||||||
|
import {EscapedValue} from '../builder/types.ts'
|
||||||
|
|
||||||
|
export enum FilterOp {
|
||||||
|
eq = '$eq',
|
||||||
|
in = '$in',
|
||||||
|
lt = '$lt',
|
||||||
|
lte = '$lte',
|
||||||
|
gt = '$gt',
|
||||||
|
gte = '$gte',
|
||||||
|
}
|
||||||
|
|
||||||
|
export type QueryFilter = { [key: string]: any }
|
||||||
|
|
||||||
|
export function apply_filter_to_where(filter: QueryFilter, where: WhereBuilder): WhereBuilder {
|
||||||
|
for ( const field in filter ) {
|
||||||
|
if ( !filter.hasOwnProperty(field) ) continue
|
||||||
|
const filter_val = filter[field]
|
||||||
|
if ( Array.isArray(filter_val) ) {
|
||||||
|
where = where.whereIn(field, filter_val)
|
||||||
|
} else if ( typeof filter_val === 'object' ) {
|
||||||
|
for ( const op in filter_val ) {
|
||||||
|
if ( !filter_val.hasOwnProperty(op) ) continue
|
||||||
|
switch (op) {
|
||||||
|
case FilterOp.eq:
|
||||||
|
where = where.where(field, '=', filter_val[op])
|
||||||
|
break
|
||||||
|
case FilterOp.in:
|
||||||
|
where = where.whereIn(field, filter_val[op])
|
||||||
|
break
|
||||||
|
case FilterOp.lt:
|
||||||
|
where = where.where(field, '<', filter_val[op])
|
||||||
|
break
|
||||||
|
case FilterOp.lte:
|
||||||
|
where = where.where(field, '<=', filter_val[op])
|
||||||
|
break
|
||||||
|
case FilterOp.gt:
|
||||||
|
where = where.where(field, '>', filter_val[op])
|
||||||
|
break
|
||||||
|
case FilterOp.gte:
|
||||||
|
where = where.where(field, '>=', filter_val[op])
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
logger.warn(`Invalid filter operator attempted: ${op}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
where = where.where(field, '=', filter_val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return where
|
||||||
|
}
|
71
test.ts
71
test.ts
@ -1,5 +1,70 @@
|
|||||||
import { make, container } from "./di/src/global.ts";
|
import {make} from "./di/src/global.ts";
|
||||||
|
import Application from "./lib/src/lifecycle/Application.ts";
|
||||||
import Scaffolding from "./lib/src/unit/Scaffolding.ts";
|
import Scaffolding from "./lib/src/unit/Scaffolding.ts";
|
||||||
|
import {Logging} from "./lib/src/service/logging/Logging.ts";
|
||||||
|
import Database from "./orm/src/service/Database.ts";
|
||||||
|
import { Model } from './orm/src/model/Model.ts'
|
||||||
|
import {Field} from "./orm/src/model/Field.ts";
|
||||||
|
import {QueryRow, Type} from './orm/src/db/types.ts';
|
||||||
|
import {Builder} from "./orm/src/builder/Builder.ts";
|
||||||
|
import ObjectResultOperator from "./orm/src/builder/type/result/ObjectResultOperator.ts";
|
||||||
|
import {BehaviorSubject} from "./lib/src/support/BehaviorSubject.ts";
|
||||||
|
|
||||||
const scaf = make(Scaffolding)
|
// TODO enum bit fields
|
||||||
scaf.up()
|
// TODO JSON field support
|
||||||
|
|
||||||
|
;(async () => {
|
||||||
|
const scaf = make(Scaffolding)
|
||||||
|
await scaf.up()
|
||||||
|
|
||||||
|
const logger = make(Logging)
|
||||||
|
|
||||||
|
const app = make(Application)
|
||||||
|
await app.run()
|
||||||
|
|
||||||
|
const db = make(Database)
|
||||||
|
|
||||||
|
await db.postgres('garrettmills', {
|
||||||
|
user: 'garrettmills',
|
||||||
|
password: 'garrettmills',
|
||||||
|
database: 'garrettmills',
|
||||||
|
hostname: 'localhost',
|
||||||
|
port: 5432,
|
||||||
|
})
|
||||||
|
|
||||||
|
class User extends Model<User> {
|
||||||
|
protected static connection = 'garrettmills'
|
||||||
|
protected static table = 'daton_users'
|
||||||
|
protected static key = 'user_id'
|
||||||
|
|
||||||
|
protected static appends: string[] = ['display_name']
|
||||||
|
protected static masks: string[] = ['active']
|
||||||
|
|
||||||
|
@Field(Type.int)
|
||||||
|
public user_id!: number
|
||||||
|
|
||||||
|
@Field(Type.varchar)
|
||||||
|
public username!: string
|
||||||
|
|
||||||
|
@Field(Type.varchar)
|
||||||
|
public first_name!: string
|
||||||
|
|
||||||
|
@Field(Type.varchar)
|
||||||
|
public last_name!: string
|
||||||
|
|
||||||
|
@Field(Type.boolean)
|
||||||
|
public active!: boolean
|
||||||
|
|
||||||
|
@Field(Type.timestamp)
|
||||||
|
public updated_at!: Date
|
||||||
|
|
||||||
|
@Field(Type.timestamp)
|
||||||
|
public created_at!: Date
|
||||||
|
|
||||||
|
get display_name(): string {
|
||||||
|
return `${this.last_name}, ${this.first_name}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const u = await User.find_one({ username: 'garrettmills' })
|
||||||
|
})()
|
||||||
|
Loading…
Reference in New Issue
Block a user