import { ModelData, ModelDataWithRelationData, PartialArray, PickRelations } from './types.js'; import { IModelDescriptor, InsertBehaviour, ISelectQueryBuilder, IWhereBuilder, QueryScope, IHistoricalModel, IModelBase, IRelationDescriptor, IDehydrateOptions, ISaveOptions, ISaveResult, ITransactionOptions } from './interfaces.js'; import { WhereFunction } from './types.js'; import { RawQuery, UpdateQueryBuilder, TruncateTableQueryBuilder, SelectQueryBuilder, DeleteQueryBuilder, InsertQueryBuilder } from './builders.js'; import { Op } from './enums.js'; import { Wrap } from './statements.js'; import { OrmDriver } from './driver.js'; import { IModelChange, IModelSnapshot } from './snapshot.js'; import { IContainer, Constructor } from '@spinajs/di'; import { DateTime } from 'luxon'; /** * * Updates model descriptor * * @param targetOrForward * @param descriptor * @returns */ export declare function updateModelDescriptor(targetOrForward: any, callback: (descriptor: IModelDescriptor) => void): void; export declare class ModelBase implements IModelBase { private _container; /** * Diff baseline: a value copy of every persisted column, taken when the row was last read * from or written to the database. `null` means the row has never been in the database, which * is what classifies it as an INSERT - not the presence of a primary key, because * `setDefaults()` pre-fills @Uuid keys on construction. Written only by `takeSnapshot()` and * `clearSnapshot()`. */ private __snapshot__; static readonly _queryScopes: QueryScope; /** * Gets descriptor for this model. It contains information about relations, orm driver, connection properties, * db table attached, column information and others. */ get ModelDescriptor(): IModelDescriptor | null; /** * Gets di container associated with this model ( via connection object eg. different drivers have their own implementation of things) */ get Container(): IContainer; /** * Primary key column names of this model. One element for the common single-column case. */ get PrimaryKeyName(): string[]; /** * Primary key value: a scalar for a single-column key, a tuple in key order for a composite key. */ get PrimaryKeyValue(): any; /** * Accepts a scalar for a single-column key, and an array in key order or an object keyed by * column name for a composite key. Cascades the new value into loaded relations exactly as * before, using the single-column relation key ( relations join on one column pair ). */ set PrimaryKeyValue(newVal: any); /** * The diff baseline for this instance, or `null` when it has never been hydrated from * the database. Read-only from the outside: mutate it only through `takeSnapshot()`, * `snapshotRelation()` and `clearSnapshot()`. */ get Snapshot(): IModelSnapshot | null; /** * The columns the diff baseline covers: the real, persisted ones. * * `Virtual` columns are excluded, which is what every other value-handling path already does — * `StandardModelToSqlConverter` ( converters.ts ) drops them from every INSERT/UPDATE payload, * and the builders skip them when resolving a column statement. A virtual column has no database * column behind it, so a baseline for one can never produce a write; snapshotting it is pure cost. * * It is also the difference between working and throwing. `@Filterable` ( orm-http ) mints a * `{ Virtual: true }` column descriptor for ANY decorated property that has no column of its own, * RELATION properties included — the documented way to declare `exists` / `n-exists` filters. * Reading such a "column" off the model returns a `Relation`, not a column value, and a Many * relation is an `Array` subclass, so `snapshotValue` used to hand it to `_.cloneDeep`, which * rebuilds an array subclass with `new value.constructor()` and no arguments. The `Relation` * constructor then dereferences an undefined descriptor and every SELECT that hydrates the model * dies. A relation's baseline is `snapshotRelation()`'s job, and it stores primary keys. */ private snapshotColumns; /** * The value this model writes for every foreign key a @BelongsTo currently manages, keyed by * column name. A relation holding a target owns its foreign key: `toSql()` writes the target's * join-column value ( `Relation.PrimaryKey` — the target's primary key unless `@BelongsTo` * names another column ), so a direct write to the column never reaches the database. A * relation holding null or undefined owns nothing and is absent here, leaving the column to * speak for itself: NULL after a `detach()`, untouched after a `populate()` that found no row. * * A @Recursive relation is excluded whatever it holds: `toSql()` writes its foreign key from * the raw column, overriding the relation, so the column is what reaches the database. * * The single place that answers "what does this model persist for this column", so the * snapshot baseline and the diff can never read the question differently. */ private effectiveForeignKeys; /** * Captures the current value of every column as the diff baseline, discarding any * previous baseline and any relation keys recorded against it. * * Reads the raw columns, deliberately: a snapshot is taken after an INSERT too, and an INSERT * may omit a foreign key on purpose ( a deferred self-reference, whose target had no key yet ). * Recording what the relation now holds would claim that key had been written and suppress the * follow-up UPDATE that actually writes it. `writeBackRelationKeys()` is what brings a column * in line with its relation, and it runs only where the key really was written. * * Values are copied, never aliased — see `snapshotValue`. An aliased snapshot makes * every diff empty and `save()` a silent no-op. */ takeSnapshot(): void; /** * Copies the value each @BelongsTo relation just wrote back onto its foreign-key column, so * the model agrees with the row. Call it after a statement that wrote those keys, before * `takeSnapshot()`. * * Without it a foreign key written directly and then overridden by its relation keeps the * overridden value in the column, the snapshot records that value as the baseline, and the * model reads dirty forever - every `save()` minting an UPDATE that changes nothing. */ private writeBackRelationKeys; /** * Records the primary keys of the members currently in relation `name` as that * relation's baseline. A no-op when the model has no snapshot — an unhydrated model * has nothing to diff against and its relations are all "new". * * @param name - relation property name, as declared on the model descriptor */ snapshotRelation(name: string): void; /** * Discards the diff baseline. After this the model is treated as brand new by `save()`. */ clearSnapshot(): void; /** * `true` until the row has been in the database: the diff baseline is `null`. This is what * classifies the model as an INSERT - not the presence of a primary key, because * `setDefaults()` pre-fills @Uuid keys on construction. */ get IsNew(): boolean; /** * Whether `save()` would write anything: a model that has never been in the database, or one * with at least one column ( or re-pointed foreign key ) differing from its baseline. * * Derived from the snapshot on every read - there is no write observer - so it costs one * comparison per column until the first difference. There is deliberately no setter: the only * way to make a model clean is to persist it or to re-baseline it with `takeSnapshot()`. */ get IsDirty(): boolean; /** * Every persisted column whose current value differs from the baseline, in descriptor column * order, followed by any @BelongsTo foreign key the descriptor declares no column for. On a * model with no baseline every column is reported with `OldValue: undefined`. * * Computed on demand - nothing observes writes - so an in-place mutation of a JSON column is * seen exactly like an assignment. Call it once and reuse the result rather than polling it * in a loop. * * Named `changeSet` - not `changes` - because model members and columns share a namespace and * `changes` is a real column name in downstream schemas. */ changeSet(): IModelChange[]; /** * The single diff. `stopAtFirst` lets a boolean question return as soon as one change is found. * * Every foreign key is resolved to ONE effective value before anything is compared, so * `IsDirty` and `changeSet()` can never disagree and the short-circuit can never skip a * decision the full diff would have made. * * The rule: a @BelongsTo holding a target decides its foreign key, because that is what * `toSql()` writes - the target's join-column value, `Relation.PrimaryKey`, which is the * target's own primary key unless `@BelongsTo` names another column. A direct write to the * column is therefore overridden and never reaches the database, and the diff has to say so. * A relation holding null or undefined decides nothing and the column stands: NULL after a * `detach()`, untouched after a `populate()` that found no row. */ private diff; valueOf(): any; driver(): OrmDriver; /** * Recursivelly takes all relation data and returns as single array */ getFlattenRelationModels(recursive?: boolean): ModelBase[]; static getModelDescriptor(): IModelDescriptor; static getRelationDescriptor(_relation: string): IRelationDescriptor; /** * Clears all data in table */ static truncate(): void; /** * Get all data from db */ static all(this: T, _page?: number, _perPage?: number): SelectQueryBuilder>>; /** * Inserts data to DB. * * @param _data - data to insert */ static insert(this: T, _data: InstanceType | Partial> | PickRelations | Array> | Array>>, _insertBehaviour?: InsertBehaviour): InsertQueryBuilder; /** * Search entities in db * * @param column - column to search or function * @param operator - boolean operator * @param value - value to compare */ static where(this: T, val: boolean): ISelectQueryBuilder>> & T['_queryScopes']; static where(this: T, val: PartialArray> | PickRelations): ISelectQueryBuilder>> & T['_queryScopes']; static where(this: T, func: WhereFunction>): ISelectQueryBuilder>> & T['_queryScopes']; static where(this: T, column: string, operator: Op, value: any): ISelectQueryBuilder>> & T['_queryScopes']; static where(this: T, column: string, value: any): ISelectQueryBuilder>> & T['_queryScopes']; static where(this: T, statement: Wrap): ISelectQueryBuilder>> & T['_queryScopes']; static where(this: T, column: string | boolean | WhereFunction> | RawQuery | PartialArray> | Wrap | PickRelations, operator?: Op | any, value?: any): ISelectQueryBuilder>> & T['_queryScopes']; /** * Updates single or multiple records at once with provided value based on condition * * @param _data - data to set */ static update(this: T, _data: Partial>): UpdateQueryBuilder> & T['_queryScopes']; /** * Tries to find all models with given primary keys */ static find(this: T, _pks: any[]): Promise>>; /** * Tries to get first result from db * * Orders by Primary key, if pk not exists then by unique constraints and lastly by CreateAt if no unique columns exists. */ static first(this: T, callback?: (builder: IWhereBuilder & T['_queryScopes']) => void): Promise>; /** * Tries to get first result from db * * Orders by Primary key, if pk not exists then by unique constraints and lastly by CreateAt if no unique columns exists. */ static last(this: T, callback?: (builder: IWhereBuilder & T['_queryScopes']) => void): Promise>; /** * Tries to get newest result from db. It throws if model dont have CreatedAt decorated property */ static newest(this: T, callback?: (builder: IWhereBuilder & T['_queryScopes']) => void): Promise>; /** * Tries to get oldest result from db. It throws if model dont have CreatedAt decorated property */ static oldest(this: T, callback?: (builder: IWhereBuilder & T['_queryScopes']) => void): Promise>; /** * Returns total count of entries in db for this model */ static count(this: T, callback?: (builder: IWhereBuilder & T['_queryScopes']) => void): Promise; /** * Tries to find all models in db. If not all exists, throws exception */ static findOrFail(this: T, _pks: any[]): Promise>>; /** * gets model by specified pk, if not exists, returns null * */ static get(this: T, _pk: any): Promise>; /** * Finds model by specified pk. If model not exists in db throws exception * */ static getOrFail(this: T, _pk: any): Promise>; /** * * Checks if model with pk key or unique fields exists and if not creates one AND NOT save in db * NOTE: it checks for unique fields constraint */ static getOrNew(this: T, _data?: Partial>): Promise>; /** * Creates query on this model. used for quering db for partial data, to perform some kind of operations * that dont need full ORM model to involve, or other non standard operations eg. joins or raw data queries based on this model */ static query(this: T): ISelectQueryBuilder>> & T['_queryScopes']; /** * Populates relation data. It returns query builder that can be used to fetch data from db * * @param _relation - relation name * @param _owner - owner model */ static populate(_relation: string, _owner: ModelBase | number | string): ISelectQueryBuilder>> & R['_queryScopes']; /** * Selects data from db. It returns query builder that can be used to fetch data from db * */ static select(this: T): ISelectQueryBuilder>> & T['_queryScopes']; /** * * Checks if model with pk key / unique fields exists and if not creates one and saves to db * NOTE: it checks for unique fields too. * * @param data - model width data to check */ static getOrCreate(this: T, _pk: string | number | null, _data?: Partial>): Promise>; /** * Creates new model & saves is to db * * @param data - initial model data */ static create(this: T, _data: Partial>): Promise>; /** * Deletes model from db * * @param pk - primary key */ static destroy(this: T, _pk?: any | any[]): DeleteQueryBuilder> & T['_queryScopes']; /** * Checks if model exists in db */ static exists(): Promise; static whereExists(this: T, _qOrR: string | ISelectQueryBuilder, _func?: WhereFunction>): ISelectQueryBuilder>>; static whereNotExists(this: T, _qOrR: string | ISelectQueryBuilder, _func?: WhereFunction>): ISelectQueryBuilder>>; /** * Runs `_callback` inside a transaction on this model's connection. The transaction commits * when the callback resolves and rolls back when it throws — see `OrmDriver.transaction`. * Resolves with whatever the callback returned. */ static transaction(this: T, _callback: (trx: OrmDriver) => Promise, _options?: ITransactionOptions): Promise; constructor(data?: Partial); /** * Fills model with data. It only fills properties that exists in database * * @param data - data to fill */ hydrate(data: Partial): void; /** * * Attachess model to proper relation an sets foreign key * * @param data - model to attach */ attach(data: ModelBase): void; /** * Extracts all data from model. It takes only properties that exists in DB * * Properties declared with `@Hidden()` are always omitted - see `descriptor.Hidden`. */ dehydrate(options?: IDehydrateOptions): ModelData; /** * * Extracts all data from model with relation data. Relation data are dehydrated recursively. * * Properties declared with `@Hidden()` are always omitted, relations included. * * @param omit - fields to omit */ dehydrateWithRelations(options?: IDehydrateOptions): ModelDataWithRelationData; toSql(onlyDirty?: boolean): Partial; /** * deletes enitt from db. If model have SoftDelete decorator, model is marked as deleted */ destroy(): Promise; /** * If model can be in achived state - sets archived at date and saves it to db */ archive(): Promise; /** * Writes the columns that differ from the snapshot. * * The change set is `changeSet()` - the snapshot diff, which also covers a foreign key that was * re-pointed through its relation - so re-assigning a column its current value produces no * UPDATE, and a column written A -> B -> A is not written back. * * A model with no snapshot ( never hydrated ) reports every column as changed, which is the * right answer: there is no baseline to be more precise than. * * @param data - optional patch hydrated onto the model first */ update(data?: Partial): Promise; /** * Save all changes to db. It creates new entry id db or updates existing one if * primary key exists */ insert(insertBehaviour?: InsertBehaviour): Promise; /** * * Shorthand for inserting model when no primary key exists, or update * its value in db if primary key is set * * @param insertBehaviour - insert mode */ insertOrUpdate(): Promise; /** * Persists this model and everything reachable from it in one transaction. * * The graph is diffed against the snapshots taken when it was loaded, sorted so that a * parent is inserted before any child that references it, and executed as inserts, then * updates restricted to the columns that actually changed, then junction rows, then the * orphan policy of every relation that lost a member. * * A relation that was never populated is invisible: `Items: OrderItem[] = []` on a freshly * constructed model deletes nothing. That is the deliberate divergence from TypeORM. * * @param options - `{ reload: true }` to diff against current database state instead of the * hydration snapshot; `{ chunk: n }` to bound batched statement size. */ save(options?: ISaveOptions): Promise; /** * Gets model data from database and returns as fresh instance. * * If primary key is not fetched, tries to load by columns with unique constraint. * If there is no unique columns or primary key, throws error */ fresh(): Promise; /** * Refresh model from database. * * If no primary key is set, tries to fetch data base on columns * with unique constraints. If none exists, throws exception */ refresh(): Promise; toJSON(): ModelData; /** * sets default values for model. values are taken from DB default column prop */ protected setDefaults(): void; protected createSelectQuery(): { query: SelectQueryBuilder; description: IModelDescriptor; model: import("@spinajs/di").Class; container: IContainer; }; protected createUpdateQuery(): { query: UpdateQueryBuilder; description: IModelDescriptor; model: import("@spinajs/di").Class; container: IContainer; }; protected createInsertQuery(): { query: InsertQueryBuilder; description: IModelDescriptor; model: import("@spinajs/di").Class; container: IContainer; }; } export declare abstract class HistoricalModel implements IHistoricalModel { readonly __action__: 'update' | 'insert' | 'delete'; readonly __revision__: number; readonly __start__: DateTime; readonly __end__: DateTime; } export declare const MODEL_STATIC_MIXINS: { getModelDescriptor(): IModelDescriptor; getRelationDescriptor(relation: string): IRelationDescriptor; truncate(): TruncateTableQueryBuilder; driver(): OrmDriver; populate(this: ModelBase, relation: string, owner: ModelBase | number | string): SelectQueryBuilder; query(): SelectQueryBuilder; select(): SelectQueryBuilder; where(column: string | boolean | WhereFunction | RawQuery | Wrap | {}, operator?: Op | any, value?: any): SelectQueryBuilder; update(data: Partial>): UpdateQueryBuilder; all(page?: number, perPage?: number): SelectQueryBuilder; /** * Try to insert new value */ insert(this: T, data: InstanceType | Partial> | Array> | Array>>, insertBehaviour?: InsertBehaviour): Promise; find(this: T, pks: any[]): Promise>>; findOrFail(this: T, pks: any[]): Promise>>; get(this: T, pk: any): Promise>; getOrFail(this: T, pk: any): Promise>; destroy(pks?: any | any[]): IWhereBuilder>; create(this: T, data: Partial>): Promise>; getOrCreate(this: T, pk: string | number | null, data: Partial>): Promise>; getOrNew(this: T, data?: Partial>): Promise>; exists(this: T, pk: any): Promise; whereExists | ModelBase[]>(this: T, qOrRel: ISelectQueryBuilder | string, callback: WhereFunction>): SelectQueryBuilder; whereNotExists | ModelBase[]>(this: T, qOrRel: ISelectQueryBuilder | string, callback: WhereFunction>): SelectQueryBuilder; first(this: T, callback?: (builder: IWhereBuilder) => void): Promise>; last(this: T, callback?: (builder: IWhereBuilder) => void): Promise>; newest(this: T, callback?: (builder: IWhereBuilder) => void): Promise>; oldest(this: T, callback?: (builder: IWhereBuilder) => void): Promise>; count(this: T, callback?: (builder: IWhereBuilder) => void): Promise; transaction(this: T, callback: (trx: OrmDriver) => Promise, options?: ITransactionOptions): Promise; }; export declare const _modelProxyFactory: (_c: IContainer, model: Constructor) => ModelBase; //# sourceMappingURL=model.d.ts.map