@athenna/database
Version:
The Athenna database handler for SQL/NoSQL.
975 lines (974 loc) • 32.2 kB
JavaScript
/**
* @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 { Is, Options, Collection } from '@athenna/common';
import { QueryBuilder } from '#src/database/builders/QueryBuilder';
import { ModelGenerator } from '#src/models/factories/ModelGenerator';
import { UniqueValueException } from '#src/exceptions/UniqueValueException';
import { HasOneRelation } from '#src/models/relations/HasOne/HasOneRelation';
import { NotFoundDataException } from '#src/exceptions/NotFoundDataException';
import { HasManyRelation } from '#src/models/relations/HasMany/HasManyRelation';
import { NullableValueException } from '#src/exceptions/NullableValueException';
import { BelongsToRelation } from '#src/models/relations/BelongsTo/BelongsToRelation';
import { BelongsToManyRelation } from '#src/models/relations/BelongsToMany/BelongsToManyRelation';
import { HasOneThroughRelation } from '#src/models/relations/HasOneThrough/HasOneThroughRelation';
import { HasManyThroughRelation } from '#src/models/relations/HasManyThrough/HasManyThroughRelation';
export class ModelQueryBuilder extends QueryBuilder {
constructor(model, driver) {
super(driver, model.table());
this.isToSetAttributes = true;
this.isToValidateUnique = true;
this.isToValidateNullable = true;
this.selectColumns = [];
this.DELETED_AT_PROP = null;
this.DELETED_AT_NAME = null;
this.isSoftDelete = false;
this.hasCustomSelect = false;
this.Model = model;
this.schema = model.schema();
this.generator = new ModelGenerator(this.Model, this.schema);
this.primaryKeyName = this.schema.getMainPrimaryKeyName();
this.primaryKeyProperty = this.schema.getMainPrimaryKeyProperty();
const deletedAtColumn = this.schema.getDeletedAtColumn();
if (deletedAtColumn) {
this.isSoftDelete = true;
this.DELETED_AT_NAME = deletedAtColumn.name;
this.DELETED_AT_PROP = deletedAtColumn.property;
}
this.selectColumns = this.schema.getAllColumnNames();
this.setPrimaryKey(this.primaryKeyName);
}
/**
* Define a transaction to be used by the model query builder.
*/
setTransaction(trx) {
return this.setDriver(trx.driver, this.Model.table());
}
/**
* Set a different driver to the model query builder.
*/
setDriver(driver, tableName) {
super.setDriver(driver, tableName);
return this;
}
/**
* Calculate the average of a given column.
*/
async avg(column) {
this.setInternalQueries();
const name = this.schema.getColumnNameByProperty(column);
return super.avg(name);
}
/**
* Calculate the average of a given column.
*/
async avgDistinct(column) {
this.setInternalQueries();
const name = this.schema.getColumnNameByProperty(column);
return super.avgDistinct(name);
}
/**
* Get the max number of a given column.
*/
async max(column) {
this.setInternalQueries();
const name = this.schema.getColumnNameByProperty(column);
return super.max(name);
}
/**
* Get the min number of a given column.
*/
async min(column) {
this.setInternalQueries();
const name = this.schema.getColumnNameByProperty(column);
return super.min(name);
}
/**
* Sum all numbers of a given column.
*/
async sum(column) {
this.setInternalQueries();
const name = this.schema.getColumnNameByProperty(column);
return super.sum(name);
}
/**
* Sum all numbers of a given column.
*/
async sumDistinct(column) {
this.setInternalQueries();
const name = this.schema.getColumnNameByProperty(column);
return super.sumDistinct(name);
}
/**
* Increment a value of a given column.
*/
async increment(column) {
this.setInternalQueries();
const name = this.schema.getColumnNameByProperty(column);
return super.increment(name);
}
/**
* Decrement a value of a given column.
*/
async decrement(column) {
this.setInternalQueries();
const name = this.schema.getColumnNameByProperty(column);
await super.decrement(name);
}
/**
* Calculate the average of a given column using distinct.
*/
async count(column) {
this.setInternalQueries();
if (!column) {
return super.count();
}
const name = this.schema.getColumnNameByProperty(column);
return super.count(name);
}
/**
* Calculate the average of a given column using distinct.
*/
async countDistinct(column) {
this.setInternalQueries();
const name = this.schema.getColumnNameByProperty(column);
return super.countDistinct(name);
}
async pluck(column) {
this.setInternalQueries();
const columnName = this.schema.getColumnNameByProperty(column);
return super.pluck(columnName);
}
async pluckMany(column) {
this.setInternalQueries();
const columnName = this.schema.getColumnNameByProperty(column);
return super.pluckMany(columnName);
}
/**
* Find a value in database.
*/
async find() {
this.setInternalQueries();
const data = await super.find();
this.resetCustomSelect();
if (this.hasCustomSelect) {
return data;
}
return this.generator.generateOne(data);
}
/**
* Find a value in database or throw exception if undefined.
*/
async findOrFail() {
const data = await this.find();
if (!data) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
throw new NotFoundDataException(this.Model.connection());
}
return data;
}
/**
* Find a value in database or create a new one if it doesn't exist.
*/
async findOrCreate(data = {}) {
const hasValue = await this.find();
if (hasValue) {
return hasValue;
}
return this.create(data);
}
/**
* Return a single data or, if no results are found,
* execute the given closure.
*/
async findOr(closure) {
const data = (await this.find());
if (!data) {
return closure();
}
return data;
}
/**
* Find a value in database and return as boolean.
*/
async exists() {
this.setInternalQueries();
return super.exists();
}
/**
* Find many values in database.
*/
async findMany() {
this.setInternalQueries();
const data = await super.findMany();
this.resetCustomSelect();
if (this.hasCustomSelect) {
return data;
}
return this.generator.generateMany(data);
}
/**
* Find many values in database and return paginated.
*/
async paginate(page = { page: 0, limit: 10, resourceUrl: '/' }, limit = 10, resourceUrl = '/') {
this.setInternalQueries();
const data = await super.paginate(page, limit, resourceUrl);
this.resetCustomSelect();
if (this.hasCustomSelect) {
return data;
}
data.data = await this.generator.generateMany(data.data);
return data;
}
/**
* Find many values in database and return
* as a collection instance.
*/
async collection() {
const models = await this.findMany();
return new Collection(models);
}
/**
* Create a value in database.
*/
async create(data = {}, cleanPersist = true) {
const created = await this.createMany([data], cleanPersist);
return created[0];
}
/**
* Create many values in database.
*/
async createMany(data, cleanPersist = true) {
data = await Promise.all(data.map(async (d) => {
const date = new Date();
const createdAt = this.schema.getCreatedAtColumn();
const updatedAt = this.schema.getUpdatedAtColumn();
const deletedAt = this.schema.getDeletedAtColumn();
const attributes = this.isToSetAttributes ? this.Model.attributes() : {};
const parsed = this.schema.propertiesToColumnNames(d, {
attributes,
cleanPersist
});
if (createdAt && parsed[createdAt.name] === undefined) {
parsed[createdAt.name] = date;
}
if (updatedAt && parsed[updatedAt.name] === undefined) {
parsed[updatedAt.name] = date;
}
if (deletedAt && parsed[deletedAt.name] === undefined) {
parsed[deletedAt.name] = null;
}
this.validateNullable(parsed);
await this.validateUnique(parsed);
return parsed;
}));
const created = await super.createMany(data);
return this.generator.generateMany(created);
}
/**
* Create or update a value in database.
*/
async createOrUpdate(data, cleanPersist = true) {
const hasValue = await this.find();
if (hasValue) {
const pk = this.primaryKeyProperty;
return this.where(pk, hasValue[pk]).update(data, cleanPersist);
}
return this.create(data, cleanPersist);
}
/**
* Update a value in database.
*/
async update(data, cleanPersist = true) {
this.setInternalQueries();
const date = new Date();
const updatedAt = this.schema.getUpdatedAtColumn();
const attributes = this.isToSetAttributes ? this.Model.attributes() : {};
const parsed = this.schema.propertiesToColumnNames(data, {
attributes,
cleanPersist
});
if (updatedAt && parsed[updatedAt.name] === undefined) {
parsed[updatedAt.name] = date;
}
await this.validateUnique(parsed, true);
const updated = await super.update(parsed);
if (Is.Array(updated)) {
return this.generator.generateMany(updated);
}
return this.generator.generateOne(updated);
}
/**
* Delete or soft delete a value in database.
*/
async delete(force = false) {
this.setInternalQueries({ addSelect: false });
if (!this.DELETED_AT_NAME || force) {
await super.delete();
return;
}
await this.update({ [this.DELETED_AT_PROP]: new Date() });
}
/**
* Restore one or multiple soft deleted models.
*/
async restore(data) {
this.setInternalQueries({ addSoftDelete: false });
if (!this.DELETED_AT_PROP) {
return;
}
const date = new Date();
const updatedAt = this.schema.getUpdatedAtColumn();
const attributes = this.isToSetAttributes ? this.Model.attributes() : {};
const parsed = this.schema.propertiesToColumnNames({ ...data, [this.DELETED_AT_PROP]: null }, {
attributes
});
if (updatedAt && parsed[updatedAt.name] === undefined) {
parsed[updatedAt.name] = date;
}
const updated = await super.update(parsed);
if (Is.Array(updated)) {
return this.generator.generateMany(updated);
}
return this.generator.generateOne(updated);
}
/**
* Retrieve only the values that are soft deleted in
* database.
*/
onlyTrashed() {
this.isSoftDelete = false;
if (!this.DELETED_AT_PROP) {
return this;
}
return this.whereNotNull(this.DELETED_AT_PROP);
}
/**
* Retrieve active and soft deleted values from database.
*/
withTrashed() {
this.isSoftDelete = false;
return this;
}
/**
* Enable/disable setting the default attributes properties
* when creating/updating models.
*/
setAttributes(value) {
this.isToSetAttributes = value;
return this;
}
/**
* Enable/disable the `isUnique` property validation of
* models columns.
*/
uniqueValidation(value) {
this.isToValidateUnique = value;
return this;
}
/**
* Enable/disable the `isNullable` property validation of
* models columns.
*/
nullableValidation(value) {
this.isToValidateNullable = value;
return this;
}
/**
* Eager load a relation in your query.
*/
with(relation, closure) {
this.schema.includeRelation(relation, closure);
return this;
}
/**
* Only returns the data if the closure returns a result.
*/
whereHas(relation, closure) {
const options = this.schema.includeWhereHasRelation(relation, closure);
/**
* Snapshot the full options object immediately at call time, before any
* subsequent `with(sameRelation)` call can mutate the shared `options`
* object (e.g. overwriting `closure` or `withClosure`). Because this
* spread happens here — outside the Knex callback — the snapshot is
* frozen regardless of what happens to `options` afterwards.
*/
const snapshot = { ...options };
super.whereExists(query => {
switch (snapshot.type) {
case 'hasOne':
return HasOneRelation.whereHas(this.Model, query, snapshot);
case 'hasMany':
return HasManyRelation.whereHas(this.Model, query, snapshot);
case 'hasOneThrough':
return HasOneThroughRelation.whereHas(this.Model, query, snapshot);
case 'hasManyThrough':
return HasManyThroughRelation.whereHas(this.Model, query, snapshot);
case 'belongsTo':
return BelongsToRelation.whereHas(this.Model, query, snapshot);
case 'belongsToMany':
return BelongsToManyRelation.whereHas(this.Model, query, snapshot);
}
});
return this;
}
/**
* Same as {@link ModelQueryBuilder.whereHas}, but joins the resulting
* `EXISTS (...)` clause to the surrounding WHERE with `OR` instead of `AND`.
*
* Useful inside a grouped `where(qb => ...)` closure to build expressions
* like `(directCol ILIKE x OR relation.col ILIKE x)` without resorting to
* raw SQL.
*/
orWhereHas(relation, closure) {
const options = this.schema.includeWhereHasRelation(relation, closure);
/**
* Snapshot the full options object immediately at call time, before any
* subsequent `with(sameRelation)` call can mutate the shared `options`
* object (e.g. overwriting `closure` or `withClosure`). Because this
* spread happens here — outside the Knex callback — the snapshot is
* frozen regardless of what happens to `options` afterwards.
*/
const snapshot = { ...options };
super.orWhereExists(query => {
switch (snapshot.type) {
case 'hasOne':
return HasOneRelation.whereHas(this.Model, query, snapshot);
case 'hasMany':
return HasManyRelation.whereHas(this.Model, query, snapshot);
case 'hasOneThrough':
return HasOneThroughRelation.whereHas(this.Model, query, snapshot);
case 'hasManyThrough':
return HasManyThroughRelation.whereHas(this.Model, query, snapshot);
case 'belongsTo':
return BelongsToRelation.whereHas(this.Model, query, snapshot);
case 'belongsToMany':
return BelongsToManyRelation.whereHas(this.Model, query, snapshot);
}
});
return this;
}
/**
* Build a grouped OR search across any mix of direct columns and
* relation columns in a single `WHERE (...)` clause.
*
* Each entry in `fields` is either a direct column property (e.g. `name`)
* or a `relation.column` path (e.g. `profile.bio`). The resulting SQL is a
* single parenthesized group joined exclusively by `OR`. Passing a falsy
* `term` short-circuits and the query is left untouched.
*
* @example
* ```ts
* User.query().search(['name', 'email', 'profile.bio'], 'john')
* ```
*/
search(fields, term) {
if (!term) {
return this;
}
const value = `%${term}%`;
this.where(qb => {
fields.forEach((field, i) => {
const isRelation = field.includes('.');
if (isRelation) {
const [relation, column] = field.split('.');
const relOp = i === 0 ? 'whereHas' : 'orWhereHas';
qb[relOp](relation, (q) => q.whereILike(column, value));
return;
}
const op = i === 0 ? 'whereILike' : 'orWhereILike';
qb[op](field, value);
});
});
return this;
}
/**
* Executes the given closure when the first argument is true.
*/
when(criteria, closure) {
if (criteria) {
closure(this, criteria);
return this;
}
return this;
}
/**
* Set the columns that should be selected on query.
*/
select(...columns) {
const selectColumns = this.schema.getColumnNamesByProperties(columns);
super.select(...selectColumns);
this.hasCustomSelect = true;
return this;
}
/**
* Set the columns that should be selected on query raw.
*/
selectRaw(sql, bindings) {
super.selectRaw(sql, bindings);
this.hasCustomSelect = true;
return this;
}
/**
* Set a group by statement in your query.
*/
groupBy(...columns) {
super.groupBy(...this.schema.getColumnNamesByProperties(columns));
return this;
}
/**
* Set a having statement in your query.
*/
having(column, operation, value) {
const name = this.schema.getColumnNameByProperty(column);
super.having(name, operation, value);
return this;
}
/**
* Set a having in statement in your query.
*/
havingIn(column, values) {
const name = this.schema.getColumnNameByProperty(column);
super.havingIn(name, values);
return this;
}
/**
* Set a having not in statement in your query.
*/
havingNotIn(column, values) {
const name = this.schema.getColumnNameByProperty(column);
super.havingNotIn(name, values);
return this;
}
/**
* Set a having between statement in your query.
*/
havingBetween(column, values) {
const name = this.schema.getColumnNameByProperty(column);
super.havingBetween(name, values);
return this;
}
/**
* Set a having not between statement in your query.
*/
havingNotBetween(column, values) {
const name = this.schema.getColumnNameByProperty(column);
super.havingNotBetween(name, values);
return this;
}
/**
* Set a having null statement in your query.
*/
havingNull(column) {
const name = this.schema.getColumnNameByProperty(column);
super.havingNull(name);
return this;
}
/**
* Set a having not null statement in your query.
*/
havingNotNull(column) {
const name = this.schema.getColumnNameByProperty(column);
super.havingNotNull(name);
return this;
}
/**
* Set a orHaving statement in your query.
*/
orHaving(column, operation, value) {
const name = this.schema.getColumnNameByProperty(column);
super.orHaving(name, operation, value);
return this;
}
/**
* Set a orHaving not in statement in your query.
*/
orHavingNotIn(column, values) {
const name = this.schema.getColumnNameByProperty(column);
super.orHavingNotIn(name, values);
return this;
}
/**
* Set a orHaving between statement in your query.
*/
orHavingBetween(column, values) {
const name = this.schema.getColumnNameByProperty(column);
super.orHavingBetween(name, values);
return this;
}
/**
* Set a orHaving not between statement in your query.
*/
orHavingNotBetween(column, values) {
const name = this.schema.getColumnNameByProperty(column);
super.orHavingNotBetween(name, values);
return this;
}
/**
* Set a orHaving null statement in your query.
*/
orHavingNull(column) {
const name = this.schema.getColumnNameByProperty(column);
super.orHavingNull(name);
return this;
}
/**
* Set a orHaving not null statement in your query.
*/
orHavingNotNull(column) {
const name = this.schema.getColumnNameByProperty(column);
super.orHavingNotNull(name);
return this;
}
/**
* Set a where statement in your query.
*/
where(statement, operation, value) {
if (Is.Function(statement)) {
super.where(query => {
statement(new ModelQueryBuilder(this.Model, query));
});
return this;
}
if (!Is.String(statement) && Is.Undefined(operation)) {
const parsed = this.schema.propertiesToColumnNames(statement);
super.where(parsed);
return this;
}
const name = this.schema.getColumnNameByProperty(statement);
super.where(name, operation, value);
return this;
}
/**
* Set a where not statement in your query.
*/
whereNot(statement, value) {
if (Is.Function(statement)) {
super.whereNot(query => {
statement(new ModelQueryBuilder(this.Model, query));
});
return this;
}
if (!Is.String(statement) && Is.Undefined(value)) {
const parsed = this.schema.propertiesToColumnNames(statement);
super.whereNot(parsed);
return this;
}
const name = this.schema.getColumnNameByProperty(statement);
super.whereNot(name, value);
return this;
}
/**
* Set a where like statement in your query.
*/
whereLike(column, value) {
const name = this.schema.getColumnNameByProperty(column);
super.whereLike(name, value);
return this;
}
/**
* Set a where ILike statement in your query.
*/
whereILike(column, value) {
const name = this.schema.getColumnNameByProperty(column);
super.whereILike(name, value);
return this;
}
/**
* Set a where in statement in your query.
*/
whereIn(column, values) {
const name = this.schema.getColumnNameByProperty(column);
super.whereIn(name, values);
return this;
}
/**
* Set a where not in statement in your query.
*/
whereNotIn(column, values) {
const name = this.schema.getColumnNameByProperty(column);
super.whereNotIn(name, values);
return this;
}
/**
* Set a where between statement in your query.
*/
whereBetween(column, values) {
const name = this.schema.getColumnNameByProperty(column);
super.whereBetween(name, values);
return this;
}
/**
* Set a where not between statement in your query.
*/
whereNotBetween(column, values) {
const name = this.schema.getColumnNameByProperty(column);
super.whereNotBetween(name, values);
return this;
}
/**
* Set a where null statement in your query.
*/
whereNull(column) {
const name = this.schema.getColumnNameByProperty(column);
super.whereNull(name);
return this;
}
/**
* Set a where not null statement in your query.
*/
whereNotNull(column) {
const name = this.schema.getColumnNameByProperty(column);
super.whereNotNull(name);
return this;
}
/**
* 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.
*/
orWhere(statement, operation, value) {
if (Is.Function(statement)) {
super.orWhere(query => {
statement(new ModelQueryBuilder(this.Model, query));
});
return this;
}
if (!Is.String(statement) && Is.Undefined(operation)) {
const parsed = this.schema.propertiesToColumnNames(statement);
super.orWhere(parsed);
return this;
}
const name = this.schema.getColumnNameByProperty(statement);
super.orWhere(name, operation, value);
return this;
}
/**
* Set a orWhere not statement in your query.
*/
orWhereNot(statement, value) {
if (Is.Function(statement)) {
super.orWhereNot(query => {
statement(new ModelQueryBuilder(this.Model, query));
});
return this;
}
if (!Is.String(statement) && Is.Undefined(value)) {
const parsed = this.schema.propertiesToColumnNames(statement);
super.orWhereNot(parsed);
return this;
}
const name = this.schema.getColumnNameByProperty(statement);
super.orWhereNot(name, value);
return this;
}
/**
* Set a orWhere like statement in your query.
*/
orWhereLike(statement, value) {
if (!Is.String(statement) && Is.Undefined(value)) {
const parsed = this.schema.propertiesToColumnNames(statement);
super.orWhereLike(parsed);
return this;
}
const name = this.schema.getColumnNameByProperty(statement);
super.orWhereLike(name, value);
return this;
}
/**
* Set a orWhere ILike statement in your query.
*/
orWhereILike(statement, value) {
if (!Is.String(statement) && Is.Undefined(value)) {
const parsed = this.schema.propertiesToColumnNames(statement);
super.orWhereILike(parsed);
return this;
}
const name = this.schema.getColumnNameByProperty(statement);
super.orWhereILike(name, value);
return this;
}
/**
* Set a orWhere in statement in your query.
*/
orWhereIn(column, values) {
const name = this.schema.getColumnNameByProperty(column);
super.orWhereIn(name, values);
return this;
}
/**
* Set a orWhere not in statement in your query.
*/
orWhereNotIn(column, values) {
const name = this.schema.getColumnNameByProperty(column);
super.orWhereNotIn(name, values);
return this;
}
/**
* Set a orWhere between statement in your query.
*/
orWhereBetween(column, values) {
const name = this.schema.getColumnNameByProperty(column);
super.orWhereBetween(name, values);
return this;
}
/**
* Set a orWhere not between statement in your query.
*/
orWhereNotBetween(column, values) {
const name = this.schema.getColumnNameByProperty(column);
super.orWhereNotBetween(name, values);
return this;
}
/**
* Set a orWhere null statement in your query.
*/
orWhereNull(column) {
const name = this.schema.getColumnNameByProperty(column);
super.orWhereNull(name);
return this;
}
/**
* Set a orWhere not null statement in your query.
*/
orWhereNotNull(column) {
const name = this.schema.getColumnNameByProperty(column);
super.orWhereNotNull(name);
return this;
}
/**
* 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.
*/
orderBy(column, direction = 'ASC') {
const name = this.schema.getColumnNameByProperty(column);
super.orderBy(name, direction);
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) {
if (!column) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
column = 'createdAt';
}
const name = this.schema.getColumnNameByProperty(column);
super.latest(name);
return this;
}
/**
* Order the results easily by the oldest date. By default, the result will
* be ordered by the table's "createdAt" column.
*/
oldest(column) {
if (!column) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
column = 'createdAt';
}
const name = this.schema.getColumnNameByProperty(column);
super.oldest(name);
return this;
}
/**
* Set the internal selected properties and soft delete
* queries.
*/
setInternalQueries(options) {
options = Options.create(options, {
addSelect: true,
addSoftDelete: true
});
if (options.addSelect && !this.hasCustomSelect) {
super.select(...this.selectColumns);
}
if (options.addSoftDelete) {
super.when(this.isSoftDelete, query => query.whereNull(this.DELETED_AT_NAME));
}
}
/**
* Reset select state after terminal custom select queries.
*/
resetCustomSelect() {
if (!this.hasCustomSelect) {
return;
}
this.hasCustomSelect = false;
this.selectColumns = this.schema.getAllColumnNames();
}
/**
* Verify that columns with `isNullable` property
* can be created in database.
*/
validateNullable(data) {
if (!this.isToValidateNullable) {
return;
}
const records = [];
for (const column of this.schema.getAllNotNullableColumns()) {
const value = data[column.name];
if (value === undefined || value === null) {
records.push(column.property);
}
}
if (!Is.Empty(records)) {
throw new NullableValueException(records);
}
}
/**
* Verify that columns with isUnique property
* can be created in database.
*/
async validateUnique(data, isUpdate = false) {
if (!this.isToValidateUnique) {
return;
}
const records = {};
for (const column of this.schema.getAllUniqueColumns()) {
const value = data[column.name];
if (value === undefined) {
continue;
}
if (isUpdate) {
const data = await this.Model.query()
.where(column.name, value)
.findMany();
if (data.length > 1) {
records[column.property] = value;
continue;
}
}
const isDuplicated = await this.Model.query()
.where(column.name, value)
.exists();
if (isDuplicated) {
records[column.property] = value;
}
}
if (!Is.Empty(records)) {
throw new UniqueValueException(records);
}
}
}