2021-06-17 16:17:15 +02:00
|
|
|
'use strict';
|
|
|
|
|
2021-06-22 17:13:11 +02:00
|
|
|
const _ = require('lodash/fp');
|
|
|
|
const types = require('./types');
|
2021-06-28 12:34:29 +02:00
|
|
|
const { createField } = require('./fields');
|
2021-06-17 16:17:15 +02:00
|
|
|
const { createQueryBuilder } = require('./query');
|
|
|
|
const { createRepository } = require('./entity-repository');
|
2021-08-12 16:12:40 +02:00
|
|
|
const { isBidirectional, isOneToAny } = require('./metadata/relations');
|
2021-06-17 16:17:15 +02:00
|
|
|
|
2021-07-30 21:15:58 +02:00
|
|
|
const toId = value => value.id || value;
|
|
|
|
const toIds = value => _.castArray(value || []).map(toId);
|
|
|
|
|
2021-08-12 16:12:40 +02:00
|
|
|
const isValidId = value => _.isString(value) || _.isInteger(value);
|
|
|
|
const toAssocs = data => {
|
|
|
|
return _.castArray(data)
|
|
|
|
.filter(datum => !_.isNil(datum))
|
|
|
|
.map(datum => {
|
|
|
|
// if it is a string or an integer return an obj with id = to datum
|
|
|
|
if (isValidId(datum)) {
|
|
|
|
return { id: datum, __pivot: {} };
|
|
|
|
}
|
|
|
|
|
|
|
|
// if it is an object check it has at least a valid id
|
|
|
|
if (!_.has('id', datum) || !isValidId(datum.id)) {
|
|
|
|
throw new Error(`Invalid id, expected a string or integer, got ${datum}`);
|
|
|
|
}
|
|
|
|
|
|
|
|
return datum;
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2021-08-03 11:22:57 +02:00
|
|
|
// TODO: handle programmatic defaults
|
2021-08-12 16:12:40 +02:00
|
|
|
const toRow = (metadata, data = {}, { withDefaults = false } = {}) => {
|
2021-06-22 17:13:11 +02:00
|
|
|
const { attributes } = metadata;
|
|
|
|
|
2021-06-23 15:37:20 +02:00
|
|
|
const obj = {};
|
2021-06-22 17:13:11 +02:00
|
|
|
|
2021-06-23 15:37:20 +02:00
|
|
|
for (const attributeName in attributes) {
|
|
|
|
const attribute = attributes[attributeName];
|
|
|
|
|
2021-08-12 16:12:40 +02:00
|
|
|
// TODO: convert to column name
|
|
|
|
if (types.isScalar(attribute.type)) {
|
|
|
|
const field = createField(attribute);
|
2021-06-28 12:34:29 +02:00
|
|
|
|
2021-08-12 16:12:40 +02:00
|
|
|
if (_.isUndefined(data[attributeName])) {
|
|
|
|
if (!_.isUndefined(attribute.default) && withDefaults) {
|
|
|
|
if (typeof attribute.default === 'function') {
|
|
|
|
obj[attributeName] = attribute.default();
|
|
|
|
} else {
|
|
|
|
obj[attributeName] = attribute.default;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
2021-06-28 12:34:29 +02:00
|
|
|
|
2021-08-12 16:12:40 +02:00
|
|
|
if (typeof field.validate === 'function' && data[attributeName] !== null) {
|
|
|
|
field.validate(data[attributeName]);
|
|
|
|
}
|
2021-06-30 20:00:03 +02:00
|
|
|
|
2021-06-28 21:37:44 +02:00
|
|
|
const val = data[attributeName] === null ? null : field.toDB(data[attributeName]);
|
2021-06-28 12:34:29 +02:00
|
|
|
|
2021-06-28 21:37:44 +02:00
|
|
|
obj[attributeName] = val;
|
2021-06-23 15:37:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (types.isRelation(attribute.type)) {
|
|
|
|
// oneToOne & manyToOne
|
|
|
|
if (attribute.joinColumn && attribute.owner) {
|
|
|
|
// TODO: ensure joinColumn name respect convention ?
|
|
|
|
const joinColumnName = attribute.joinColumn.name;
|
|
|
|
|
2021-06-25 12:07:32 +02:00
|
|
|
// allow setting to null
|
|
|
|
const attrValue = !_.isUndefined(data[attributeName])
|
|
|
|
? data[attributeName]
|
|
|
|
: data[joinColumnName];
|
2021-06-23 15:37:20 +02:00
|
|
|
|
|
|
|
if (!_.isUndefined(attrValue)) {
|
|
|
|
obj[joinColumnName] = attrValue;
|
|
|
|
}
|
2021-07-26 17:52:59 +02:00
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (attribute.morphColumn && attribute.owner) {
|
2021-07-26 22:02:48 +02:00
|
|
|
const { idColumn, typeColumn, typeField = '__type' } = attribute.morphColumn;
|
2021-07-26 17:52:59 +02:00
|
|
|
|
|
|
|
const value = data[attributeName];
|
|
|
|
|
2021-07-30 20:45:51 +02:00
|
|
|
if (value === null) {
|
|
|
|
Object.assign(obj, {
|
|
|
|
[idColumn.name]: null,
|
|
|
|
[typeColumn.name]: null,
|
|
|
|
});
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2021-07-26 17:52:59 +02:00
|
|
|
if (!_.isUndefined(value)) {
|
2021-07-26 22:02:48 +02:00
|
|
|
if (!_.has('id', value) || !_.has(typeField, value)) {
|
|
|
|
throw new Error(`Expects properties ${typeField} an id to make a morph association`);
|
2021-07-26 17:52:59 +02:00
|
|
|
}
|
|
|
|
|
2021-07-30 20:45:51 +02:00
|
|
|
Object.assign(obj, {
|
|
|
|
[idColumn.name]: value.id,
|
|
|
|
[typeColumn.name]: value[typeField],
|
|
|
|
});
|
2021-07-26 17:52:59 +02:00
|
|
|
}
|
2021-06-23 15:37:20 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return obj;
|
|
|
|
};
|
|
|
|
|
2021-06-17 16:17:15 +02:00
|
|
|
const createEntityManager = db => {
|
|
|
|
const repoMap = {};
|
|
|
|
|
|
|
|
return {
|
2021-08-03 11:22:57 +02:00
|
|
|
async findOne(uid, params) {
|
2021-08-04 17:47:38 +02:00
|
|
|
await db.lifecycles.run('beforeFindOne', uid, { params });
|
2021-08-03 11:22:57 +02:00
|
|
|
|
|
|
|
const result = await this.createQueryBuilder(uid)
|
2021-06-17 16:17:15 +02:00
|
|
|
.init(params)
|
2021-08-03 11:22:57 +02:00
|
|
|
.first()
|
|
|
|
.execute();
|
2021-06-17 16:17:15 +02:00
|
|
|
|
2021-08-04 17:47:38 +02:00
|
|
|
await db.lifecycles.run('afterFindOne', uid, { params, result });
|
2021-08-03 11:22:57 +02:00
|
|
|
|
|
|
|
return result;
|
2021-06-17 16:17:15 +02:00
|
|
|
},
|
|
|
|
|
|
|
|
// should we name it findOne because people are used to it ?
|
2021-08-03 11:22:57 +02:00
|
|
|
async findMany(uid, params) {
|
2021-08-04 17:47:38 +02:00
|
|
|
await db.lifecycles.run('beforeFindMany', uid, { params });
|
2021-08-03 11:22:57 +02:00
|
|
|
|
|
|
|
const result = await this.createQueryBuilder(uid)
|
|
|
|
.init(params)
|
|
|
|
.execute();
|
|
|
|
|
2021-08-04 17:47:38 +02:00
|
|
|
await db.lifecycles.run('afterFindMany', uid, { params, result });
|
2021-06-17 16:17:15 +02:00
|
|
|
|
2021-08-03 11:22:57 +02:00
|
|
|
return result;
|
2021-06-17 16:17:15 +02:00
|
|
|
},
|
|
|
|
|
2021-06-22 17:13:11 +02:00
|
|
|
async count(uid, params = {}) {
|
2021-08-04 17:47:38 +02:00
|
|
|
await db.lifecycles.run('beforeCount', uid, { params });
|
2021-06-17 16:17:15 +02:00
|
|
|
|
2021-08-03 11:22:57 +02:00
|
|
|
const res = await this.createQueryBuilder(uid)
|
2021-08-10 08:42:42 +02:00
|
|
|
.init(_.pick(['_q', 'where'], params))
|
2021-06-17 16:17:15 +02:00
|
|
|
.count()
|
|
|
|
.first()
|
|
|
|
.execute();
|
|
|
|
|
2021-08-03 11:22:57 +02:00
|
|
|
const result = Number(res.count);
|
|
|
|
|
2021-08-04 17:47:38 +02:00
|
|
|
await db.lifecycles.run('afterCount', uid, { params, result });
|
2021-08-03 11:22:57 +02:00
|
|
|
|
|
|
|
return result;
|
2021-06-17 16:17:15 +02:00
|
|
|
},
|
|
|
|
|
2021-06-25 12:07:32 +02:00
|
|
|
async create(uid, params = {}) {
|
2021-08-04 17:47:38 +02:00
|
|
|
await db.lifecycles.run('beforeCreate', uid, { params });
|
2021-06-23 15:37:20 +02:00
|
|
|
|
2021-08-03 11:22:57 +02:00
|
|
|
const metadata = db.metadata.get(uid);
|
2021-06-17 16:17:15 +02:00
|
|
|
const { data } = params;
|
|
|
|
|
2021-06-25 12:07:32 +02:00
|
|
|
if (!_.isPlainObject(data)) {
|
|
|
|
throw new Error('Create expects a data object');
|
|
|
|
}
|
|
|
|
|
2021-08-12 16:12:40 +02:00
|
|
|
const dataToInsert = toRow(metadata, data, { withDefaults: true });
|
2021-06-17 16:17:15 +02:00
|
|
|
|
|
|
|
const [id] = await this.createQueryBuilder(uid)
|
2021-06-22 17:13:11 +02:00
|
|
|
.insert(dataToInsert)
|
2021-06-17 16:17:15 +02:00
|
|
|
.execute();
|
|
|
|
|
2021-07-06 14:18:03 +02:00
|
|
|
await this.attachRelations(uid, id, data);
|
2021-06-17 16:17:15 +02:00
|
|
|
|
2021-06-25 12:07:32 +02:00
|
|
|
// TODO: in case there is not select or populate specified return the inserted data ?
|
2021-08-03 11:22:57 +02:00
|
|
|
// TODO: do not trigger the findOne lifecycles ?
|
2021-08-04 17:47:38 +02:00
|
|
|
const result = await this.findOne(uid, {
|
2021-08-03 11:22:57 +02:00
|
|
|
where: { id },
|
|
|
|
select: params.select,
|
|
|
|
populate: params.populate,
|
|
|
|
});
|
2021-06-25 12:07:32 +02:00
|
|
|
|
2021-08-04 17:47:38 +02:00
|
|
|
await db.lifecycles.run('afterCreate', uid, { params, result });
|
2021-08-03 11:22:57 +02:00
|
|
|
|
|
|
|
return result;
|
2021-06-17 16:17:15 +02:00
|
|
|
},
|
|
|
|
|
2021-08-12 16:12:40 +02:00
|
|
|
// TODO: where do we handle relation processing for many queries ?
|
2021-06-25 12:07:32 +02:00
|
|
|
async createMany(uid, params = {}) {
|
2021-08-04 17:47:38 +02:00
|
|
|
await db.lifecycles.run('beforeCreateMany', uid, { params });
|
2021-08-03 11:22:57 +02:00
|
|
|
|
|
|
|
const metadata = db.metadata.get(uid);
|
2021-06-17 16:17:15 +02:00
|
|
|
const { data } = params;
|
|
|
|
|
2021-06-25 12:07:32 +02:00
|
|
|
if (!_.isArray(data)) {
|
2021-08-03 11:22:57 +02:00
|
|
|
throw new Error('CreateMany expects data to be an array');
|
2021-06-25 12:07:32 +02:00
|
|
|
}
|
|
|
|
|
2021-08-12 16:12:40 +02:00
|
|
|
const dataToInsert = data.map(datum => toRow(metadata, datum, { withDefaults: true }));
|
2021-06-22 17:13:11 +02:00
|
|
|
|
2021-06-24 18:28:36 +02:00
|
|
|
if (_.isEmpty(dataToInsert)) {
|
2021-06-25 12:07:32 +02:00
|
|
|
throw new Error('Nothing to insert');
|
2021-06-24 18:28:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
await this.createQueryBuilder(uid)
|
2021-06-22 17:13:11 +02:00
|
|
|
.insert(dataToInsert)
|
2021-06-17 16:17:15 +02:00
|
|
|
.execute();
|
|
|
|
|
2021-08-03 11:22:57 +02:00
|
|
|
const result = { count: data.length };
|
|
|
|
|
2021-08-04 17:47:38 +02:00
|
|
|
await db.lifecycles.run('afterCreateMany', uid, { params, result });
|
2021-08-03 11:22:57 +02:00
|
|
|
|
|
|
|
return result;
|
2021-06-17 16:17:15 +02:00
|
|
|
},
|
|
|
|
|
2021-06-25 12:07:32 +02:00
|
|
|
async update(uid, params = {}) {
|
2021-08-04 17:47:38 +02:00
|
|
|
await db.lifecycles.run('beforeUpdate', uid, { params });
|
2021-08-03 11:22:57 +02:00
|
|
|
|
2021-06-23 15:37:20 +02:00
|
|
|
const metadata = db.metadata.get(uid);
|
2021-08-03 11:22:57 +02:00
|
|
|
const { where, data } = params;
|
2021-06-22 17:13:11 +02:00
|
|
|
|
2021-06-30 20:00:03 +02:00
|
|
|
if (!_.isPlainObject(data)) {
|
|
|
|
throw new Error('Update requires a data object');
|
|
|
|
}
|
|
|
|
|
2021-06-25 12:07:32 +02:00
|
|
|
if (_.isEmpty(where)) {
|
|
|
|
throw new Error('Update requires a where parameter');
|
2021-06-24 18:28:36 +02:00
|
|
|
}
|
|
|
|
|
2021-06-25 12:07:32 +02:00
|
|
|
const entity = await this.createQueryBuilder(uid)
|
|
|
|
.select('id')
|
2021-06-17 16:17:15 +02:00
|
|
|
.where(where)
|
2021-06-25 12:07:32 +02:00
|
|
|
.first()
|
2021-06-17 16:17:15 +02:00
|
|
|
.execute();
|
|
|
|
|
2021-06-25 12:07:32 +02:00
|
|
|
if (!entity) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
const { id } = entity;
|
|
|
|
|
2021-06-28 21:37:44 +02:00
|
|
|
const dataToUpdate = toRow(metadata, data);
|
2021-06-24 18:28:36 +02:00
|
|
|
|
2021-06-25 12:07:32 +02:00
|
|
|
if (!_.isEmpty(dataToUpdate)) {
|
|
|
|
await this.createQueryBuilder(uid)
|
|
|
|
.where({ id })
|
|
|
|
.update(dataToUpdate)
|
|
|
|
.execute();
|
|
|
|
}
|
|
|
|
|
2021-07-06 14:18:03 +02:00
|
|
|
await this.updateRelations(uid, id, data);
|
2021-06-25 12:07:32 +02:00
|
|
|
|
2021-08-03 11:22:57 +02:00
|
|
|
// TODO: do not trigger the findOne lifecycles ?
|
2021-08-04 17:47:38 +02:00
|
|
|
const result = await this.findOne(uid, {
|
2021-08-03 11:22:57 +02:00
|
|
|
where: { id },
|
|
|
|
select: params.select,
|
|
|
|
populate: params.populate,
|
|
|
|
});
|
|
|
|
|
2021-08-04 17:47:38 +02:00
|
|
|
await db.lifecycles.run('afterUpdate', uid, { params, result });
|
2021-08-03 11:22:57 +02:00
|
|
|
|
|
|
|
return result;
|
2021-06-17 16:17:15 +02:00
|
|
|
},
|
|
|
|
|
2021-08-12 16:12:40 +02:00
|
|
|
// TODO: where do we handle relation processing for many queries ?
|
2021-06-25 12:07:32 +02:00
|
|
|
async updateMany(uid, params = {}) {
|
2021-08-04 17:47:38 +02:00
|
|
|
await db.lifecycles.run('beforeUpdateMany', uid, { params });
|
2021-06-17 16:17:15 +02:00
|
|
|
|
2021-06-23 15:37:20 +02:00
|
|
|
const metadata = db.metadata.get(uid);
|
2021-08-03 11:22:57 +02:00
|
|
|
const { where, data } = params;
|
|
|
|
|
2021-06-28 21:37:44 +02:00
|
|
|
const dataToUpdate = toRow(metadata, data);
|
2021-06-22 17:13:11 +02:00
|
|
|
|
2021-06-24 18:28:36 +02:00
|
|
|
if (_.isEmpty(dataToUpdate)) {
|
|
|
|
throw new Error('Update requires data');
|
|
|
|
}
|
|
|
|
|
2021-06-25 12:07:32 +02:00
|
|
|
const updatedRows = await this.createQueryBuilder(uid)
|
2021-06-17 16:17:15 +02:00
|
|
|
.where(where)
|
2021-06-22 17:13:11 +02:00
|
|
|
.update(dataToUpdate)
|
2021-06-17 16:17:15 +02:00
|
|
|
.execute();
|
2021-06-24 18:28:36 +02:00
|
|
|
|
2021-08-03 11:22:57 +02:00
|
|
|
const result = { count: updatedRows };
|
|
|
|
|
2021-08-04 17:47:38 +02:00
|
|
|
await db.lifecycles.run('afterUpdateMany', uid, { params, result });
|
2021-08-03 11:22:57 +02:00
|
|
|
|
|
|
|
return result;
|
2021-06-25 12:07:32 +02:00
|
|
|
},
|
2021-06-24 18:28:36 +02:00
|
|
|
|
2021-06-25 12:07:32 +02:00
|
|
|
async delete(uid, params = {}) {
|
2021-08-04 17:47:38 +02:00
|
|
|
await db.lifecycles.run('beforeDelete', uid, { params });
|
2021-08-03 11:22:57 +02:00
|
|
|
|
2021-06-25 12:07:32 +02:00
|
|
|
const { where, select, populate } = params;
|
2021-06-24 18:28:36 +02:00
|
|
|
|
2021-06-25 12:07:32 +02:00
|
|
|
if (_.isEmpty(where)) {
|
|
|
|
throw new Error('Delete requires a where parameter');
|
|
|
|
}
|
2021-06-17 16:17:15 +02:00
|
|
|
|
2021-08-03 11:22:57 +02:00
|
|
|
// TODO: avoid trigger the findOne lifecycles in the case ?
|
2021-06-25 12:07:32 +02:00
|
|
|
const entity = await this.findOne(uid, {
|
|
|
|
select: select && ['id'].concat(select),
|
2021-07-07 18:04:39 +02:00
|
|
|
where,
|
2021-06-25 12:07:32 +02:00
|
|
|
populate,
|
|
|
|
});
|
|
|
|
|
|
|
|
if (!entity) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
const { id } = entity;
|
|
|
|
|
|
|
|
await this.createQueryBuilder(uid)
|
|
|
|
.where({ id })
|
2021-06-17 16:17:15 +02:00
|
|
|
.delete()
|
|
|
|
.execute();
|
2021-06-24 18:28:36 +02:00
|
|
|
|
2021-07-06 14:18:03 +02:00
|
|
|
await this.deleteRelations(uid, id);
|
2021-06-24 18:28:36 +02:00
|
|
|
|
2021-08-04 17:47:38 +02:00
|
|
|
await db.lifecycles.run('afterDelete', uid, { params, result: entity });
|
2021-08-03 11:22:57 +02:00
|
|
|
|
2021-06-25 12:07:32 +02:00
|
|
|
return entity;
|
2021-06-17 16:17:15 +02:00
|
|
|
},
|
|
|
|
|
2021-06-25 12:07:32 +02:00
|
|
|
// TODO: where do we handle relation processing for many queries ?
|
|
|
|
async deleteMany(uid, params = {}) {
|
2021-08-04 17:47:38 +02:00
|
|
|
await db.lifecycles.run('beforeDeleteMany', uid, { params });
|
2021-08-03 11:22:57 +02:00
|
|
|
|
2021-06-17 16:17:15 +02:00
|
|
|
const { where } = params;
|
|
|
|
|
2021-06-25 12:07:32 +02:00
|
|
|
const deletedRows = await this.createQueryBuilder(uid)
|
2021-06-17 16:17:15 +02:00
|
|
|
.where(where)
|
|
|
|
.delete()
|
|
|
|
.execute();
|
2021-06-24 18:28:36 +02:00
|
|
|
|
2021-08-03 11:22:57 +02:00
|
|
|
const result = { count: deletedRows };
|
|
|
|
|
2021-08-04 17:47:38 +02:00
|
|
|
await db.lifecycles.run('afterDelete', uid, { params, result });
|
2021-08-03 11:22:57 +02:00
|
|
|
|
|
|
|
return result;
|
2021-06-17 16:17:15 +02:00
|
|
|
},
|
|
|
|
|
2021-06-28 12:34:29 +02:00
|
|
|
/**
|
|
|
|
* Attach relations to a new entity
|
2021-06-30 21:17:32 +02:00
|
|
|
*
|
2021-06-28 12:34:29 +02:00
|
|
|
* @param {EntityManager} em - entity manager instance
|
|
|
|
* @param {Metadata} metadata - model metadta
|
|
|
|
* @param {ID} id - entity ID
|
|
|
|
* @param {object} data - data received for creation
|
|
|
|
*/
|
2021-07-05 18:35:16 +02:00
|
|
|
// TODO: wrap Transaction
|
2021-07-06 14:18:03 +02:00
|
|
|
async attachRelations(uid, id, data) {
|
|
|
|
const { attributes } = db.metadata.get(uid);
|
|
|
|
|
2021-06-28 12:34:29 +02:00
|
|
|
for (const attributeName in attributes) {
|
|
|
|
const attribute = attributes[attributeName];
|
|
|
|
|
2021-08-12 16:12:40 +02:00
|
|
|
const isValidLink = _.has(attributeName, data) && !_.isNil(data[attributeName]);
|
2021-07-28 15:32:21 +02:00
|
|
|
|
|
|
|
if (attribute.type !== 'relation' || !isValidLink) {
|
2021-07-26 17:52:59 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2021-07-26 19:40:30 +02:00
|
|
|
if (attribute.relation === 'morphOne' || attribute.relation === 'morphMany') {
|
|
|
|
const { target, morphBy } = attribute;
|
2021-07-26 17:52:59 +02:00
|
|
|
|
2021-07-26 19:40:30 +02:00
|
|
|
const targetAttribute = db.metadata.get(target).attributes[morphBy];
|
2021-07-26 17:52:59 +02:00
|
|
|
|
2021-07-26 19:40:30 +02:00
|
|
|
if (targetAttribute.relation === 'morphToOne') {
|
|
|
|
// set columns
|
|
|
|
const { idColumn, typeColumn } = targetAttribute.morphColumn;
|
2021-07-26 17:52:59 +02:00
|
|
|
|
2021-07-26 19:40:30 +02:00
|
|
|
await this.createQueryBuilder(target)
|
|
|
|
.update({ [idColumn.name]: id, [typeColumn.name]: uid })
|
2021-07-30 21:15:58 +02:00
|
|
|
.where({ id: toId(data[attributeName]) })
|
2021-07-26 19:40:30 +02:00
|
|
|
.execute();
|
2021-07-30 20:45:51 +02:00
|
|
|
} else if (targetAttribute.relation === 'morphToMany') {
|
2021-07-26 19:40:30 +02:00
|
|
|
const { joinTable } = targetAttribute;
|
|
|
|
const { joinColumn, morphColumn } = joinTable;
|
2021-07-26 17:52:59 +02:00
|
|
|
|
|
|
|
const { idColumn, typeColumn } = morphColumn;
|
|
|
|
|
2021-08-12 16:12:40 +02:00
|
|
|
const rows = toAssocs(data[attributeName]).map(data => {
|
|
|
|
return {
|
|
|
|
[joinColumn.name]: data.id,
|
|
|
|
[idColumn.name]: id,
|
|
|
|
[typeColumn.name]: uid,
|
|
|
|
...(joinTable.on || {}),
|
|
|
|
...(data.__pivot || {}),
|
|
|
|
};
|
|
|
|
});
|
2021-07-26 17:52:59 +02:00
|
|
|
|
|
|
|
if (_.isEmpty(rows)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2021-07-26 19:40:30 +02:00
|
|
|
await this.createQueryBuilder(joinTable.name)
|
2021-07-26 17:52:59 +02:00
|
|
|
.insert(rows)
|
|
|
|
.execute();
|
2021-07-26 19:40:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
continue;
|
|
|
|
} else if (attribute.relation === 'morphToOne') {
|
|
|
|
// handled on the entry itself
|
|
|
|
continue;
|
|
|
|
} else if (attribute.relation === 'morphToMany') {
|
|
|
|
const { joinTable } = attribute;
|
|
|
|
const { joinColumn, morphColumn } = joinTable;
|
|
|
|
|
2021-07-26 22:02:48 +02:00
|
|
|
const { idColumn, typeColumn, typeField = '__type' } = morphColumn;
|
2021-07-26 19:40:30 +02:00
|
|
|
|
2021-08-12 16:12:40 +02:00
|
|
|
const rows = toAssocs(data[attributeName]).map(data => ({
|
2021-07-26 19:40:30 +02:00
|
|
|
[joinColumn.name]: id,
|
|
|
|
[idColumn.name]: data.id,
|
2021-07-26 22:02:48 +02:00
|
|
|
[typeColumn.name]: data[typeField],
|
2021-07-26 19:40:30 +02:00
|
|
|
...(joinTable.on || {}),
|
2021-08-12 16:12:40 +02:00
|
|
|
...(data.__pivot || {}),
|
2021-07-26 19:40:30 +02:00
|
|
|
}));
|
2021-07-26 17:52:59 +02:00
|
|
|
|
2021-07-26 19:40:30 +02:00
|
|
|
if (_.isEmpty(rows)) {
|
2021-07-26 17:52:59 +02:00
|
|
|
continue;
|
|
|
|
}
|
2021-07-26 19:40:30 +02:00
|
|
|
|
|
|
|
await this.createQueryBuilder(joinTable.name)
|
|
|
|
.insert(rows)
|
|
|
|
.execute();
|
|
|
|
|
|
|
|
continue;
|
2021-07-26 17:52:59 +02:00
|
|
|
}
|
|
|
|
|
2021-06-28 12:34:29 +02:00
|
|
|
if (attribute.joinColumn && attribute.owner) {
|
2021-07-05 18:35:16 +02:00
|
|
|
if (
|
|
|
|
attribute.relation === 'oneToOne' &&
|
|
|
|
isBidirectional(attribute) &&
|
|
|
|
data[attributeName]
|
|
|
|
) {
|
2021-07-06 14:18:03 +02:00
|
|
|
await this.createQueryBuilder(uid)
|
2021-07-02 02:26:14 +02:00
|
|
|
.where({ [attribute.joinColumn.name]: data[attributeName], id: { $ne: id } })
|
|
|
|
.update({ [attribute.joinColumn.name]: null })
|
|
|
|
.execute();
|
|
|
|
}
|
|
|
|
|
2021-06-28 12:34:29 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// oneToOne oneToMany on the non owning side
|
|
|
|
if (attribute.joinColumn && !attribute.owner) {
|
|
|
|
// need to set the column on the target
|
|
|
|
const { target } = attribute;
|
|
|
|
|
|
|
|
// TODO: check it is an id & the entity exists (will throw due to FKs otherwise so not a big pbl in SQL)
|
2021-07-02 02:26:14 +02:00
|
|
|
|
2021-07-28 15:32:21 +02:00
|
|
|
await this.createQueryBuilder(target)
|
|
|
|
.where({ [attribute.joinColumn.referencedColumn]: id })
|
|
|
|
.update({ [attribute.joinColumn.referencedColumn]: null })
|
|
|
|
.execute();
|
|
|
|
|
|
|
|
await this.createQueryBuilder(target)
|
|
|
|
.update({ [attribute.joinColumn.referencedColumn]: id })
|
|
|
|
// NOTE: works if it is an array or a single id
|
|
|
|
.where({ id: data[attributeName] })
|
|
|
|
.execute();
|
2021-06-28 12:34:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (attribute.joinTable) {
|
|
|
|
// need to set the column on the target
|
|
|
|
|
|
|
|
const { joinTable } = attribute;
|
|
|
|
const { joinColumn, inverseJoinColumn } = joinTable;
|
|
|
|
|
2021-08-12 16:12:40 +02:00
|
|
|
// TODO: validate logic of delete
|
|
|
|
if (isOneToAny(attribute) && isBidirectional(attribute)) {
|
2021-06-28 12:34:29 +02:00
|
|
|
await this.createQueryBuilder(joinTable.name)
|
2021-07-28 15:32:21 +02:00
|
|
|
.delete()
|
|
|
|
.where({ [inverseJoinColumn.name]: _.castArray(data[attributeName]) })
|
|
|
|
.where(joinTable.on || {})
|
2021-06-28 12:34:29 +02:00
|
|
|
.execute();
|
|
|
|
}
|
2021-07-28 15:32:21 +02:00
|
|
|
|
2021-08-12 16:12:40 +02:00
|
|
|
const insert = toAssocs(data[attributeName]).map(data => {
|
2021-07-28 15:32:21 +02:00
|
|
|
return {
|
|
|
|
[joinColumn.name]: id,
|
2021-08-12 16:12:40 +02:00
|
|
|
[inverseJoinColumn.name]: data.id,
|
2021-07-28 15:32:21 +02:00
|
|
|
...(joinTable.on || {}),
|
2021-08-12 16:12:40 +02:00
|
|
|
...(data.__pivot || {}),
|
2021-07-28 15:32:21 +02:00
|
|
|
};
|
|
|
|
});
|
|
|
|
|
|
|
|
// if there is nothing to insert
|
|
|
|
if (insert.length === 0) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
await this.createQueryBuilder(joinTable.name)
|
|
|
|
.insert(insert)
|
|
|
|
.execute();
|
2021-06-28 12:34:29 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Updates relations of an existing entity
|
2021-06-30 21:17:32 +02:00
|
|
|
*
|
2021-06-28 12:34:29 +02:00
|
|
|
* @param {EntityManager} em - entity manager instance
|
|
|
|
* @param {Metadata} metadata - model metadta
|
|
|
|
* @param {ID} id - entity ID
|
|
|
|
* @param {object} data - data received for creation
|
|
|
|
*/
|
|
|
|
// TODO: check relation exists (handled by FKs except for polymorphics)
|
2021-07-05 18:35:16 +02:00
|
|
|
// TODO: wrap Transaction
|
2021-07-06 14:18:03 +02:00
|
|
|
async updateRelations(uid, id, data) {
|
|
|
|
const { attributes } = db.metadata.get(uid);
|
2021-06-28 12:34:29 +02:00
|
|
|
|
|
|
|
for (const attributeName in attributes) {
|
|
|
|
const attribute = attributes[attributeName];
|
|
|
|
|
2021-07-26 19:40:30 +02:00
|
|
|
if (attribute.type !== 'relation' || !_.has(attributeName, data)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (attribute.relation === 'morphOne' || attribute.relation === 'morphMany') {
|
|
|
|
const { target, morphBy } = attribute;
|
|
|
|
|
|
|
|
const targetAttribute = db.metadata.get(target).attributes[morphBy];
|
|
|
|
|
|
|
|
if (targetAttribute.relation === 'morphToOne') {
|
|
|
|
// set columns
|
|
|
|
const { idColumn, typeColumn } = targetAttribute.morphColumn;
|
|
|
|
|
|
|
|
await this.createQueryBuilder(target)
|
|
|
|
.update({ [idColumn.name]: null, [typeColumn.name]: null })
|
|
|
|
.where({ [idColumn.name]: id, [typeColumn.name]: uid })
|
|
|
|
.execute();
|
|
|
|
|
2021-07-30 20:45:51 +02:00
|
|
|
if (!_.isNull(data[attributeName])) {
|
|
|
|
await this.createQueryBuilder(target)
|
|
|
|
.update({ [idColumn.name]: id, [typeColumn.name]: uid })
|
2021-07-30 21:15:58 +02:00
|
|
|
.where({ id: toId(data[attributeName]) })
|
2021-07-30 20:45:51 +02:00
|
|
|
.execute();
|
|
|
|
}
|
|
|
|
} else if (targetAttribute.relation === 'morphToMany') {
|
2021-07-26 19:40:30 +02:00
|
|
|
const { joinTable } = targetAttribute;
|
|
|
|
const { joinColumn, morphColumn } = joinTable;
|
|
|
|
|
|
|
|
const { idColumn, typeColumn } = morphColumn;
|
|
|
|
|
|
|
|
await this.createQueryBuilder(joinTable.name)
|
|
|
|
.delete()
|
|
|
|
.where({
|
|
|
|
[idColumn.name]: id,
|
|
|
|
[typeColumn.name]: uid,
|
|
|
|
...(joinTable.on || {}),
|
|
|
|
})
|
|
|
|
.execute();
|
|
|
|
|
2021-08-12 16:12:40 +02:00
|
|
|
const rows = toAssocs(data[attributeName]).map(data => ({
|
|
|
|
[joinColumn.name]: data.id,
|
2021-07-26 19:40:30 +02:00
|
|
|
[idColumn.name]: id,
|
|
|
|
[typeColumn.name]: uid,
|
|
|
|
...(joinTable.on || {}),
|
2021-08-12 16:12:40 +02:00
|
|
|
...(data.__pivot || {}),
|
2021-07-26 19:40:30 +02:00
|
|
|
}));
|
|
|
|
|
|
|
|
if (_.isEmpty(rows)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
await this.createQueryBuilder(joinTable.name)
|
|
|
|
.insert(rows)
|
|
|
|
.execute();
|
|
|
|
}
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (attribute.relation === 'morphToOne') {
|
2021-07-30 20:45:51 +02:00
|
|
|
// handled on the entry itself
|
|
|
|
continue;
|
2021-07-26 19:40:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (attribute.relation === 'morphToMany') {
|
|
|
|
const { joinTable } = attribute;
|
|
|
|
const { joinColumn, morphColumn } = joinTable;
|
|
|
|
|
2021-07-26 22:02:48 +02:00
|
|
|
const { idColumn, typeColumn, typeField = '__type' } = morphColumn;
|
2021-07-26 19:40:30 +02:00
|
|
|
|
|
|
|
await this.createQueryBuilder(joinTable.name)
|
|
|
|
.delete()
|
|
|
|
.where({
|
|
|
|
[joinColumn.name]: id,
|
|
|
|
...(joinTable.on || {}),
|
|
|
|
})
|
|
|
|
.execute();
|
|
|
|
|
2021-08-12 16:12:40 +02:00
|
|
|
const rows = toAssocs(data[attributeName]).map(data => ({
|
2021-07-26 19:40:30 +02:00
|
|
|
[joinColumn.name]: id,
|
|
|
|
[idColumn.name]: data.id,
|
2021-07-26 22:02:48 +02:00
|
|
|
[typeColumn.name]: data[typeField],
|
2021-07-26 19:40:30 +02:00
|
|
|
...(joinTable.on || {}),
|
2021-08-12 16:12:40 +02:00
|
|
|
...(data.__pivot || {}),
|
2021-07-26 19:40:30 +02:00
|
|
|
}));
|
|
|
|
|
|
|
|
if (_.isEmpty(rows)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
await this.createQueryBuilder(joinTable.name)
|
|
|
|
.insert(rows)
|
|
|
|
.execute();
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
2021-07-26 17:52:59 +02:00
|
|
|
|
2021-06-28 12:34:29 +02:00
|
|
|
if (attribute.joinColumn && attribute.owner) {
|
2021-07-28 15:32:21 +02:00
|
|
|
// handled in the row itslef
|
2021-06-28 12:34:29 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// oneToOne oneToMany on the non owning side.
|
|
|
|
// Since it is a join column no need to remove previous relations
|
|
|
|
if (attribute.joinColumn && !attribute.owner) {
|
|
|
|
// need to set the column on the target
|
|
|
|
const { target } = attribute;
|
|
|
|
|
2021-07-28 15:32:21 +02:00
|
|
|
await this.createQueryBuilder(target)
|
|
|
|
.where({ [attribute.joinColumn.referencedColumn]: id })
|
|
|
|
.update({ [attribute.joinColumn.referencedColumn]: null })
|
|
|
|
.execute();
|
|
|
|
|
|
|
|
if (!_.isNull(data[attributeName])) {
|
2021-06-28 12:34:29 +02:00
|
|
|
await this.createQueryBuilder(target)
|
2021-07-28 15:32:21 +02:00
|
|
|
// NOTE: works if it is an array or a single id
|
|
|
|
.where({ id: data[attributeName] })
|
|
|
|
.update({ [attribute.joinColumn.referencedColumn]: id })
|
2021-06-28 12:34:29 +02:00
|
|
|
.execute();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (attribute.joinTable) {
|
|
|
|
const { joinTable } = attribute;
|
|
|
|
const { joinColumn, inverseJoinColumn } = joinTable;
|
|
|
|
|
2021-07-28 15:32:21 +02:00
|
|
|
// clear previous associations in the joinTable
|
|
|
|
await this.createQueryBuilder(joinTable.name)
|
|
|
|
.delete()
|
|
|
|
.where({ [joinColumn.name]: id })
|
|
|
|
.where(joinTable.on || {})
|
|
|
|
.execute();
|
|
|
|
|
|
|
|
if (['oneToOne', 'oneToMany'].includes(attribute.relation)) {
|
2021-06-28 12:34:29 +02:00
|
|
|
await this.createQueryBuilder(joinTable.name)
|
|
|
|
.delete()
|
2021-08-12 16:12:40 +02:00
|
|
|
.where({ [inverseJoinColumn.name]: toIds(data[attributeName]) })
|
2021-07-28 15:32:21 +02:00
|
|
|
.where(joinTable.on || {})
|
2021-06-28 12:34:29 +02:00
|
|
|
.execute();
|
2021-07-28 15:32:21 +02:00
|
|
|
}
|
2021-06-28 12:34:29 +02:00
|
|
|
|
2021-07-28 15:32:21 +02:00
|
|
|
if (!_.isNull(data[attributeName])) {
|
2021-08-12 16:12:40 +02:00
|
|
|
const insert = toAssocs(data[attributeName]).map(data => {
|
2021-07-28 15:32:21 +02:00
|
|
|
return {
|
|
|
|
[joinColumn.name]: id,
|
2021-08-12 16:12:40 +02:00
|
|
|
[inverseJoinColumn.name]: data.id,
|
2021-07-28 15:32:21 +02:00
|
|
|
...(joinTable.on || {}),
|
2021-08-12 16:12:40 +02:00
|
|
|
...(data.__pivot || {}),
|
2021-07-28 15:32:21 +02:00
|
|
|
};
|
|
|
|
});
|
2021-06-28 12:34:29 +02:00
|
|
|
|
2021-07-28 15:32:21 +02:00
|
|
|
// if there is nothing to insert
|
|
|
|
if (insert.length === 0) {
|
|
|
|
continue;
|
2021-07-02 02:26:14 +02:00
|
|
|
}
|
2021-07-28 15:32:21 +02:00
|
|
|
|
|
|
|
await this.createQueryBuilder(joinTable.name)
|
|
|
|
.insert(insert)
|
|
|
|
.execute();
|
2021-06-28 12:34:29 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
2021-08-12 16:12:40 +02:00
|
|
|
* Delete relational associations of an existing entity
|
2021-06-28 12:34:29 +02:00
|
|
|
* This removes associations but doesn't do cascade deletions for components for example. This will be handled on the entity service layer instead
|
2021-08-12 16:12:40 +02:00
|
|
|
* NOTE: Most of the deletion should be handled by ON DELETE CASCADE for dialects that have FKs
|
2021-06-30 21:17:32 +02:00
|
|
|
*
|
2021-06-28 12:34:29 +02:00
|
|
|
* @param {EntityManager} em - entity manager instance
|
|
|
|
* @param {Metadata} metadata - model metadta
|
|
|
|
* @param {ID} id - entity ID
|
|
|
|
*/
|
2021-07-05 18:35:16 +02:00
|
|
|
// TODO: wrap Transaction
|
2021-07-06 14:18:03 +02:00
|
|
|
async deleteRelations(uid, id) {
|
|
|
|
const { attributes } = db.metadata.get(uid);
|
2021-06-28 12:34:29 +02:00
|
|
|
|
|
|
|
for (const attributeName in attributes) {
|
|
|
|
const attribute = attributes[attributeName];
|
|
|
|
|
2021-07-26 19:40:30 +02:00
|
|
|
if (attribute.type !== 'relation') {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
if morphOne | morphMany
|
|
|
|
if morphBy is morphToOne
|
|
|
|
set null
|
|
|
|
if morphBy is morphToOne
|
|
|
|
delete links
|
|
|
|
*/
|
|
|
|
if (attribute.relation === 'morphOne' || attribute.relation === 'morphMany') {
|
|
|
|
const { target, morphBy } = attribute;
|
|
|
|
|
|
|
|
const targetAttribute = db.metadata.get(target).attributes[morphBy];
|
|
|
|
|
|
|
|
if (targetAttribute.relation === 'morphToOne') {
|
|
|
|
// set columns
|
|
|
|
const { idColumn, typeColumn } = targetAttribute.morphColumn;
|
|
|
|
|
|
|
|
await this.createQueryBuilder(target)
|
|
|
|
.update({ [idColumn.name]: null, [typeColumn.name]: null })
|
|
|
|
.where({ [idColumn.name]: id, [typeColumn.name]: uid })
|
|
|
|
.execute();
|
2021-07-30 20:45:51 +02:00
|
|
|
} else if (targetAttribute.relation === 'morphToMany') {
|
2021-07-26 19:40:30 +02:00
|
|
|
const { joinTable } = targetAttribute;
|
|
|
|
const { morphColumn } = joinTable;
|
|
|
|
|
|
|
|
const { idColumn, typeColumn } = morphColumn;
|
|
|
|
|
|
|
|
await this.createQueryBuilder(joinTable.name)
|
|
|
|
.delete()
|
|
|
|
.where({
|
|
|
|
[idColumn.name]: id,
|
|
|
|
[typeColumn.name]: uid,
|
|
|
|
...(joinTable.on || {}),
|
|
|
|
})
|
|
|
|
.execute();
|
|
|
|
}
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
if morphToOne
|
|
|
|
nothing to do
|
|
|
|
*/
|
|
|
|
if (attribute.relation === 'morphToOne') {
|
|
|
|
// do nothing
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
if morphToMany
|
|
|
|
delete links
|
|
|
|
*/
|
|
|
|
if (attribute.relation === 'morphToMany') {
|
|
|
|
const { joinTable } = attribute;
|
|
|
|
const { joinColumn } = joinTable;
|
|
|
|
|
|
|
|
await this.createQueryBuilder(joinTable.name)
|
|
|
|
.delete()
|
|
|
|
.where({
|
|
|
|
[joinColumn.name]: id,
|
|
|
|
...(joinTable.on || {}),
|
|
|
|
})
|
|
|
|
.execute();
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (db.dialect.usesForeignKeys()) {
|
|
|
|
return;
|
|
|
|
}
|
2021-07-26 17:52:59 +02:00
|
|
|
|
2021-06-28 12:34:29 +02:00
|
|
|
// NOTE: we do not remove existing associations with the target as it should handled by unique FKs instead
|
|
|
|
if (attribute.joinColumn && attribute.owner) {
|
|
|
|
// nothing to do => relation already added on the table
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// oneToOne oneToMany on the non owning side.
|
|
|
|
if (attribute.joinColumn && !attribute.owner) {
|
|
|
|
// need to set the column on the target
|
|
|
|
const { target } = attribute;
|
|
|
|
|
|
|
|
await this.createQueryBuilder(target)
|
|
|
|
.where({ [attribute.joinColumn.referencedColumn]: id })
|
2021-07-02 02:26:14 +02:00
|
|
|
.update({ [attribute.joinColumn.referencedColumn]: null })
|
2021-06-28 12:34:29 +02:00
|
|
|
.execute();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (attribute.joinTable) {
|
|
|
|
const { joinTable } = attribute;
|
|
|
|
const { joinColumn } = joinTable;
|
|
|
|
|
|
|
|
await this.createQueryBuilder(joinTable.name)
|
|
|
|
.delete()
|
|
|
|
.where({ [joinColumn.name]: id })
|
2021-07-28 15:32:21 +02:00
|
|
|
.where(joinTable.on || {})
|
2021-06-28 12:34:29 +02:00
|
|
|
.execute();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
2021-06-17 16:17:15 +02:00
|
|
|
|
2021-07-08 18:15:32 +02:00
|
|
|
// TODO: support multiple relations at once with the populate syntax
|
2021-08-03 11:22:57 +02:00
|
|
|
// TODO: add lifecycle events
|
2021-07-08 18:15:32 +02:00
|
|
|
async populate(uid, entity, populate) {
|
|
|
|
const entry = await this.findOne(uid, {
|
|
|
|
select: ['id'],
|
|
|
|
where: { id: entity.id },
|
|
|
|
populate: populate,
|
|
|
|
});
|
|
|
|
|
|
|
|
return Object.assign({}, entity, entry);
|
2021-07-07 18:04:39 +02:00
|
|
|
},
|
|
|
|
|
2021-07-08 18:15:32 +02:00
|
|
|
// TODO: support multiple relations at once with the populate syntax
|
2021-08-03 11:22:57 +02:00
|
|
|
// TODO: add lifecycle events
|
2021-07-08 18:15:32 +02:00
|
|
|
async load(uid, entity, field, params) {
|
2021-07-07 18:04:39 +02:00
|
|
|
const { attributes } = db.metadata.get(uid);
|
|
|
|
|
|
|
|
const attribute = attributes[field];
|
|
|
|
|
|
|
|
if (!attribute || attribute.type !== 'relation') {
|
2021-07-08 18:15:32 +02:00
|
|
|
throw new Error('Invalid load. Expected a relational attribute');
|
2021-07-07 18:04:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
const entry = await this.findOne(uid, {
|
|
|
|
select: ['id'],
|
2021-07-08 18:15:32 +02:00
|
|
|
where: { id: entity.id },
|
2021-07-07 18:04:39 +02:00
|
|
|
populate: {
|
|
|
|
[field]: params || true,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
return entry[field];
|
|
|
|
},
|
|
|
|
|
2021-06-17 16:17:15 +02:00
|
|
|
// cascading
|
|
|
|
// aggregations
|
|
|
|
// -> avg
|
|
|
|
// -> min
|
|
|
|
// -> max
|
|
|
|
// -> grouping
|
|
|
|
|
|
|
|
// formulas
|
|
|
|
// custom queries
|
|
|
|
|
|
|
|
// utilities
|
|
|
|
// -> map result
|
|
|
|
// -> map input
|
|
|
|
|
|
|
|
// extra features
|
|
|
|
// -> virtuals
|
|
|
|
// -> private
|
2021-06-30 21:17:32 +02:00
|
|
|
|
2021-06-17 16:17:15 +02:00
|
|
|
createQueryBuilder(uid) {
|
|
|
|
return createQueryBuilder(uid, db);
|
|
|
|
},
|
|
|
|
|
|
|
|
getRepository(uid) {
|
|
|
|
if (!repoMap[uid]) {
|
|
|
|
repoMap[uid] = createRepository(uid, db);
|
|
|
|
}
|
|
|
|
|
|
|
|
return repoMap[uid];
|
|
|
|
},
|
|
|
|
|
|
|
|
clearRepositories() {
|
|
|
|
repoMap.clear();
|
|
|
|
},
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
module.exports = {
|
|
|
|
createEntityManager,
|
|
|
|
};
|