mirror of
https://github.com/strapi/strapi.git
synced 2025-12-05 03:21:22 +00:00
Crud + relations Associations v1
This commit is contained in:
parent
bdbf90b567
commit
d33a363c88
@ -41,17 +41,17 @@ module.exports = async () => {
|
||||
registerAdminConditions();
|
||||
registerPermissionActions();
|
||||
|
||||
const permissionService = getService('permission');
|
||||
// const permissionService = getService('permission');
|
||||
const userService = getService('user');
|
||||
const roleService = getService('role');
|
||||
|
||||
await roleService.createRolesIfNoneExist();
|
||||
|
||||
await roleService.resetSuperAdminPermissions();
|
||||
// await roleService.resetSuperAdminPermissions();
|
||||
await roleService.displayWarningIfNoSuperAdmin();
|
||||
|
||||
await permissionService.ensureBoundPermissionsInDatabase();
|
||||
await permissionService.cleanPermissionsInDatabase();
|
||||
// await permissionService.ensureBoundPermissionsInDatabase();
|
||||
// await permissionService.cleanPermissionsInDatabase();
|
||||
|
||||
await userService.displayWarningIfUsersDontHaveRole();
|
||||
|
||||
|
||||
@ -256,27 +256,14 @@ const deleteByIds = async ids => {
|
||||
/** Count the users that don't have any associated roles
|
||||
* @returns {Promise<number>}
|
||||
*/
|
||||
// FIXME: test / cleanup
|
||||
const countUsersWithoutRole = async () => {
|
||||
return strapi.query('strapi::user').count({ where: { roles: { id: { $null: true } } } });
|
||||
|
||||
// const userModel = strapi.query('strapi::user').model;
|
||||
// let count;
|
||||
|
||||
// if (userModel.orm === 'bookshelf') {
|
||||
// count = await strapi.query('strapi::user').count({ roles_null: true });
|
||||
// } else if (userModel.orm === 'mongoose') {
|
||||
// count = await strapi.query('strapi::user').model.countDocuments({
|
||||
// $or: [{ roles: { $exists: false } }, { roles: { $size: 0 } }],
|
||||
// });
|
||||
// } else {
|
||||
// const allRoles = await strapi.query('role', 'admin').find({ _limit: -1 });
|
||||
// count = await strapi.query('strapi::user').count({
|
||||
// roles_nin: allRoles.map(r => r.id),
|
||||
// });
|
||||
// }
|
||||
|
||||
// return count;
|
||||
return strapi.query('strapi::user').count({
|
||||
where: {
|
||||
roles: {
|
||||
id: { $null: true },
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@ -291,13 +278,7 @@ const count = async (where = {}) => {
|
||||
/** Assign some roles to several users
|
||||
* @returns {undefined}
|
||||
*/
|
||||
// TODO: impl with updateMany
|
||||
const assignARoleToAll = async roleId => {
|
||||
// await strapi.query('strapi::user').updateMany({
|
||||
// where: { roles: { id: { $null: true } } },
|
||||
// data: { roles: [roleId] },
|
||||
// });
|
||||
|
||||
const users = await strapi.query('strapi::user').findMany({
|
||||
select: ['id'],
|
||||
where: {
|
||||
@ -305,42 +286,12 @@ const assignARoleToAll = async roleId => {
|
||||
},
|
||||
});
|
||||
|
||||
console.log(users);
|
||||
|
||||
await strapi.query('strapi;:role').update({
|
||||
await strapi.query('strapi::role').update({
|
||||
where: { id: roleId },
|
||||
data: {
|
||||
users: users.map(u => u.id),
|
||||
},
|
||||
});
|
||||
|
||||
// for (const user of users) {
|
||||
// await strapi
|
||||
// .query('strapi::user')
|
||||
// .update({ where: { id: user.id }, data: { roles: [roleId] } });
|
||||
|
||||
// // or
|
||||
|
||||
// }
|
||||
|
||||
// const userModel = strapi.query('strapi::user').model;
|
||||
// if (userModel.orm === 'bookshelf') {
|
||||
// const assocTable = userModel.associations.find(a => a.alias === 'roles').tableCollectionName;
|
||||
// const userTable = userModel.collectionName;
|
||||
// const knex = strapi.connections[userModel.connection];
|
||||
// const usersIds = await knex
|
||||
// .select(`${userTable}.id`)
|
||||
// .from(userTable)
|
||||
// .leftJoin(assocTable, `${userTable}.id`, `${assocTable}.user_id`)
|
||||
// .where(`${assocTable}.role_id`, null)
|
||||
// .pluck(`${userTable}.id`);
|
||||
// if (usersIds.length > 0) {
|
||||
// const newRelations = usersIds.map(userId => ({ user_id: userId, role_id: roleId }));
|
||||
// await knex.insert(newRelations).into(assocTable);
|
||||
// }
|
||||
// } else if (userModel.orm === 'mongoose') {
|
||||
// await strapi.query('strapi::user').model.updateMany({}, { roles: [roleId] });
|
||||
// }
|
||||
};
|
||||
|
||||
/** Display a warning if some users don't have at least one role
|
||||
|
||||
@ -19,32 +19,6 @@ async function main() {
|
||||
|
||||
await orm.schema.reset();
|
||||
// await orm.schema.sync();
|
||||
|
||||
// const article = await orm.query('article').create({
|
||||
// data: {
|
||||
// title: 'Test',
|
||||
// },
|
||||
// });
|
||||
|
||||
// // console.log(article);
|
||||
|
||||
// await orm.query('article').findOne({
|
||||
// where: {
|
||||
// id: 2,
|
||||
// },
|
||||
// });
|
||||
|
||||
// await orm.query('article').update({
|
||||
// data: {
|
||||
// title: 'Test',
|
||||
// },
|
||||
// });
|
||||
|
||||
// await orm.query('article').delete({
|
||||
// where: {
|
||||
// id: 2,
|
||||
// },
|
||||
// });
|
||||
} finally {
|
||||
orm.destroy();
|
||||
}
|
||||
|
||||
@ -22,11 +22,6 @@ const category = {
|
||||
relation: 'oneToMany',
|
||||
target: 'article',
|
||||
mappedBy: 'category',
|
||||
// useJoinTable: false,
|
||||
},
|
||||
compo: {
|
||||
type: 'component',
|
||||
component: 'compo',
|
||||
},
|
||||
},
|
||||
};
|
||||
@ -52,7 +47,7 @@ const article = {
|
||||
target: 'tag',
|
||||
inversedBy: 'articles',
|
||||
},
|
||||
compo: {
|
||||
compos: {
|
||||
type: 'component',
|
||||
component: 'compo',
|
||||
repeatable: true,
|
||||
@ -94,7 +89,7 @@ const compo = {
|
||||
type: 'string',
|
||||
},
|
||||
value: {
|
||||
type: 'integer',
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@ -26,7 +26,10 @@ const pickRowAttributes = (metadata, data = {}) => {
|
||||
// TODO: ensure joinColumn name respect convention ?
|
||||
const joinColumnName = attribute.joinColumn.name;
|
||||
|
||||
const attrValue = data[attributeName] || data[joinColumnName];
|
||||
// allow setting to null
|
||||
const attrValue = !_.isUndefined(data[attributeName])
|
||||
? data[attributeName]
|
||||
: data[joinColumnName];
|
||||
|
||||
if (!_.isUndefined(attrValue)) {
|
||||
obj[joinColumnName] = attrValue;
|
||||
@ -38,6 +41,45 @@ const pickRowAttributes = (metadata, data = {}) => {
|
||||
return obj;
|
||||
};
|
||||
|
||||
/**
|
||||
* Attach relations to a new entity
|
||||
* oneToOne
|
||||
* if owner
|
||||
* if joinColumn
|
||||
* -> Id should have been added in the column of the model table beforehand to avoid extra updates
|
||||
* if joinTable
|
||||
* -> add relation
|
||||
*
|
||||
* if not owner
|
||||
* if joinColumn
|
||||
* -> add relation
|
||||
* if joinTable
|
||||
* -> add relation in join table
|
||||
*
|
||||
* oneToMany
|
||||
* owner -> cannot be owner
|
||||
* not owner
|
||||
* joinColumn
|
||||
* -> add relations in target
|
||||
* joinTable
|
||||
* -> add relations in join table
|
||||
*
|
||||
* manyToOne
|
||||
* not owner -> must be owner
|
||||
* owner
|
||||
* join Column
|
||||
* -> Id should have been added in the column of the model table beforehand to avoid extra updates
|
||||
* joinTable
|
||||
* -> add relation in join table
|
||||
*
|
||||
* manyToMany
|
||||
* -> add relation in join table
|
||||
*
|
||||
* @param {EntityManager} em - entity manager instance
|
||||
* @param {Metadata} metadata - model metadta
|
||||
* @param {ID} id - entity ID
|
||||
* @param {object} data - data received for creation
|
||||
*/
|
||||
const attachRelations = async (em, metadata, id, data) => {
|
||||
const { attributes } = metadata;
|
||||
|
||||
@ -80,11 +122,13 @@ const attachRelations = async (em, metadata, id, data) => {
|
||||
return {
|
||||
[joinColumn.name]: id,
|
||||
[inverseJoinColumn.name]: datum,
|
||||
...(joinTable.on || {}),
|
||||
};
|
||||
})
|
||||
: {
|
||||
[joinColumn.name]: id,
|
||||
[inverseJoinColumn.name]: data[attributeName],
|
||||
...(joinTable.on || {}),
|
||||
};
|
||||
|
||||
await em
|
||||
@ -93,49 +137,167 @@ const attachRelations = async (em, metadata, id, data) => {
|
||||
.execute();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
oneToOne
|
||||
if owner
|
||||
if joinColumn
|
||||
TODO: We might actually want to make the column unique and throw -> doing this makes the code generic and doesn't require specific logic
|
||||
removing existing relation
|
||||
-> Id should have been added in the column of the model table beforehand to avoid extra updates
|
||||
if joinTable
|
||||
-> clear join Table assoc
|
||||
-> add relation
|
||||
|
||||
if not owner
|
||||
if joinColumn
|
||||
remove existing relation
|
||||
-> add relation
|
||||
if joinTable
|
||||
-> clear join Table assoc
|
||||
-> add relation in join table
|
||||
|
||||
oneToMany
|
||||
owner -> cannot be owner
|
||||
not owner
|
||||
joinColumn
|
||||
-> add relations in target
|
||||
joinTable
|
||||
-> add relations in join table
|
||||
|
||||
manyToOne
|
||||
not owner -> must be owner
|
||||
owner
|
||||
join Column
|
||||
-> Id should have been added in the column of the model table beforehand to avoid extra updates
|
||||
joinTable
|
||||
-> add relation in join table
|
||||
|
||||
manyToMany
|
||||
-> add relation in join table
|
||||
|
||||
*/
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates relations of an existing entity
|
||||
* oneToOne
|
||||
* if owner
|
||||
* if joinColumn
|
||||
* -> handled in the DB row
|
||||
* if joinTable
|
||||
* -> clear join Table assoc
|
||||
* -> add relation in join table
|
||||
*
|
||||
* if not owner
|
||||
* if joinColumn
|
||||
* -> set join column on the target
|
||||
* if joinTable
|
||||
* -> clear join Table assoc
|
||||
* -> add relation in join table
|
||||
*
|
||||
* oneToMany
|
||||
* owner -> cannot be owner
|
||||
* not owner
|
||||
* joinColumn
|
||||
* -> set join column on the target
|
||||
* joinTable
|
||||
* -> add relations in join table
|
||||
*
|
||||
* manyToOne
|
||||
* not owner -> must be owner
|
||||
* owner
|
||||
* join Column
|
||||
* -> handled in the DB row
|
||||
* joinTable
|
||||
* -> add relation in join table
|
||||
*
|
||||
* manyToMany
|
||||
* -> clear join Table assoc
|
||||
* -> add relation in join table
|
||||
*
|
||||
* @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)
|
||||
const updateRelations = async (em, metadata, id, data) => {
|
||||
const { attributes } = metadata;
|
||||
|
||||
for (const attributeName in attributes) {
|
||||
const attribute = attributes[attributeName];
|
||||
|
||||
// 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.
|
||||
// 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;
|
||||
|
||||
if (data[attributeName]) {
|
||||
await em
|
||||
.createQueryBuilder(target)
|
||||
.update({ [attribute.joinColumn.referencedColumn]: id })
|
||||
// NOTE: works if it is an array or a single id
|
||||
.where({ id: data[attributeName] })
|
||||
.execute();
|
||||
}
|
||||
}
|
||||
|
||||
if (attribute.joinTable) {
|
||||
const { joinTable } = attribute;
|
||||
const { joinColumn, inverseJoinColumn } = joinTable;
|
||||
|
||||
if (data[attributeName]) {
|
||||
// clear previous associations in the joinTable
|
||||
await em
|
||||
.createQueryBuilder(joinTable.name)
|
||||
.delete()
|
||||
.where({
|
||||
[joinColumn.name]: id,
|
||||
})
|
||||
// TODO: add join.on filters to only clear the valid info
|
||||
.where(joinTable.on ? joinTable.on : {})
|
||||
.execute();
|
||||
|
||||
// TODO: add pivot informations too
|
||||
const insert = Array.isArray(data[attributeName])
|
||||
? data[attributeName].map(datum => {
|
||||
return {
|
||||
[joinColumn.name]: id,
|
||||
[inverseJoinColumn.name]: datum,
|
||||
...(joinTable.on || {}),
|
||||
};
|
||||
})
|
||||
: {
|
||||
[joinColumn.name]: id,
|
||||
[inverseJoinColumn.name]: data[attributeName],
|
||||
...(joinTable.on || {}),
|
||||
};
|
||||
|
||||
console.log(insert);
|
||||
|
||||
await em
|
||||
.createQueryBuilder(joinTable.name)
|
||||
.insert(insert)
|
||||
.execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete relations of an existing entity
|
||||
* This removes associations but doesn't do cascade deletions for components for example. This will be handled on the entity service layer instead
|
||||
* NOTE: Most of the deletion should be handled by ON DELETE CASCADE
|
||||
*
|
||||
* oneToOne
|
||||
* if owner
|
||||
* if joinColumn
|
||||
* -> handled in the DB row
|
||||
* if joinTable
|
||||
* -> clear join Table assoc
|
||||
*
|
||||
* if not owner
|
||||
* if joinColumn
|
||||
* -> set join column on the target // CASCADING should do the job
|
||||
* if joinTable
|
||||
* -> clear join Table assoc // CASCADING
|
||||
*
|
||||
* oneToMany
|
||||
* owner -> cannot be owner
|
||||
* not owner
|
||||
* joinColumn
|
||||
* -> set join column on the target
|
||||
* joinTable
|
||||
* -> add relations in join table
|
||||
*
|
||||
* manyToOne
|
||||
* not owner -> must be owner
|
||||
* owner
|
||||
* join Column
|
||||
* -> handled in the DB row
|
||||
* joinTable
|
||||
* -> add relation in join table
|
||||
*
|
||||
* manyToMany
|
||||
* -> clear join Table assoc
|
||||
* -> add relation in join table
|
||||
*
|
||||
* @param {EntityManager} em - entity manager instance
|
||||
* @param {Metadata} metadata - model metadta
|
||||
* @param {ID} id - entity ID
|
||||
*/
|
||||
// noop as cascade FKs does the job
|
||||
const deleteRelations = () => {};
|
||||
|
||||
const createEntityManager = db => {
|
||||
const repoMap = {};
|
||||
|
||||
@ -161,7 +323,6 @@ const createEntityManager = db => {
|
||||
return await Promise.all([this.findMany(uid, params), this.count(uid, params)]);
|
||||
},
|
||||
|
||||
// TODO: define api
|
||||
async count(uid, params = {}) {
|
||||
const qb = this.createQueryBuilder(uid).where(params.where);
|
||||
|
||||
@ -173,23 +334,24 @@ const createEntityManager = db => {
|
||||
return Number(res.count);
|
||||
},
|
||||
|
||||
// TODO: make it create one somehow
|
||||
async create(uid, params) {
|
||||
async create(uid, params = {}) {
|
||||
// create entry in DB
|
||||
|
||||
const metadata = db.metadata.get(uid);
|
||||
|
||||
const { data } = params;
|
||||
|
||||
if (!_.isPlainObject(data)) {
|
||||
throw new Error('Create expects a data object');
|
||||
}
|
||||
|
||||
// transform value to storage value
|
||||
// apply programatic defaults if any -> I think this should be handled outside of this layer as we might have some applicative rules in the entity service
|
||||
|
||||
// TODO: in query builder ?
|
||||
const dataToInsert = pickRowAttributes(metadata, data);
|
||||
|
||||
if (_.isEmpty(dataToInsert)) {
|
||||
throw new Error('Create requires data');
|
||||
}
|
||||
// if (_.isEmpty(dataToInsert)) {
|
||||
// throw new Error('Create requires data');
|
||||
// }
|
||||
|
||||
const [id] = await this.createQueryBuilder(uid)
|
||||
.insert(dataToInsert)
|
||||
@ -198,19 +360,25 @@ const createEntityManager = db => {
|
||||
// create relation associations or move this to the entity service & call attach on the repo instead
|
||||
await attachRelations(this, metadata, id, data);
|
||||
|
||||
// TODO: in case there is not select or populate specified return the inserted data ?
|
||||
|
||||
return this.findOne(uid, { where: { id }, select: params.select, populate: params.populate });
|
||||
},
|
||||
|
||||
async createMany(uid, params) {
|
||||
// TODO: where do we handle relation processing for many queries ?
|
||||
async createMany(uid, params = {}) {
|
||||
const { data } = params;
|
||||
|
||||
if (!_.isArray(data)) {
|
||||
throw new Error('CreateMany expecets data to be an array');
|
||||
}
|
||||
|
||||
const metadata = db.metadata.get(uid);
|
||||
|
||||
// Add defaults / transform to storage type
|
||||
const dataToInsert = data.map(datum => pickRowAttributes(metadata, datum));
|
||||
|
||||
if (_.isEmpty(dataToInsert)) {
|
||||
throw new Error('Create requires data');
|
||||
throw new Error('Nothing to insert');
|
||||
}
|
||||
|
||||
await this.createQueryBuilder(uid)
|
||||
@ -220,9 +388,43 @@ const createEntityManager = db => {
|
||||
return { count: data.length };
|
||||
},
|
||||
|
||||
// TODO: make it update one somehow
|
||||
// findOne + update with a return
|
||||
async update(uid, params) {
|
||||
async update(uid, params = {}) {
|
||||
const { where, data } = params;
|
||||
const metadata = db.metadata.get(uid);
|
||||
|
||||
if (_.isEmpty(where)) {
|
||||
throw new Error('Update requires a where parameter');
|
||||
}
|
||||
|
||||
const entity = await this.createQueryBuilder(uid)
|
||||
.select('id')
|
||||
.where(where)
|
||||
.first()
|
||||
.execute();
|
||||
|
||||
if (!entity) {
|
||||
// TODO: or throw ?
|
||||
return null;
|
||||
}
|
||||
|
||||
const { id } = entity;
|
||||
|
||||
const dataToUpdate = pickRowAttributes(metadata, data);
|
||||
|
||||
if (!_.isEmpty(dataToUpdate)) {
|
||||
await this.createQueryBuilder(uid)
|
||||
.where({ id })
|
||||
.update(dataToUpdate)
|
||||
.execute();
|
||||
}
|
||||
|
||||
await updateRelations(this, metadata, id, data);
|
||||
|
||||
return this.findOne(uid, { where: { id }, select: params.select, populate: params.populate });
|
||||
},
|
||||
|
||||
// TODO: where do we handle relation processing for many queries ?
|
||||
async updateMany(uid, params = {}) {
|
||||
const { where, data } = params;
|
||||
|
||||
const metadata = db.metadata.get(uid);
|
||||
@ -232,66 +434,54 @@ const createEntityManager = db => {
|
||||
throw new Error('Update requires data');
|
||||
}
|
||||
|
||||
const res = await this.createQueryBuilder(uid)
|
||||
const updatedRows = await this.createQueryBuilder(uid)
|
||||
.where(where)
|
||||
.update(dataToUpdate)
|
||||
.execute();
|
||||
|
||||
// TODO: update relations
|
||||
console.log({ res });
|
||||
|
||||
// TODO: return obj
|
||||
return {};
|
||||
return { count: updatedRows };
|
||||
},
|
||||
|
||||
// only returns the number of affected rows
|
||||
async updateMany(uid, params) {
|
||||
const { where, data } = params;
|
||||
|
||||
async delete(uid, params = {}) {
|
||||
const { where, select, populate } = params;
|
||||
const metadata = db.metadata.get(uid);
|
||||
const dataToUpdate = pickRowAttributes(metadata, data);
|
||||
|
||||
if (_.isEmpty(dataToUpdate)) {
|
||||
throw new Error('Update requires data');
|
||||
if (_.isEmpty(where)) {
|
||||
throw new Error('Delete requires a where parameter');
|
||||
}
|
||||
|
||||
const res = await this.createQueryBuilder(uid)
|
||||
.where(where)
|
||||
.update(dataToUpdate)
|
||||
.execute();
|
||||
const entity = await this.findOne(uid, {
|
||||
where,
|
||||
select: select && ['id'].concat(select),
|
||||
populate,
|
||||
});
|
||||
|
||||
console.log({ res });
|
||||
if (!entity) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// TODO: update relations
|
||||
const { id } = entity;
|
||||
|
||||
// TODO: Return count on updateMany
|
||||
},
|
||||
|
||||
// TODO: make it deleteOne somehow
|
||||
// findOne + delete with a return -> should go in the entity service
|
||||
async delete(uid, params) {
|
||||
const res = await this.createQueryBuilder(uid)
|
||||
.init(params)
|
||||
await this.createQueryBuilder(uid)
|
||||
.where({ id })
|
||||
.delete()
|
||||
.execute();
|
||||
|
||||
console.log({ res });
|
||||
// TODO: delete relations
|
||||
await deleteRelations(this, metadata, id);
|
||||
|
||||
return res;
|
||||
return entity;
|
||||
},
|
||||
|
||||
async deleteMany(uid, params) {
|
||||
// TODO: where do we handle relation processing for many queries ?
|
||||
async deleteMany(uid, params = {}) {
|
||||
const { where } = params;
|
||||
|
||||
const res = await this.createQueryBuilder(uid)
|
||||
const deletedRows = await this.createQueryBuilder(uid)
|
||||
.where(where)
|
||||
.delete()
|
||||
.execute();
|
||||
|
||||
// TODO: delete relations
|
||||
|
||||
return res;
|
||||
return { count: deletedRows };
|
||||
},
|
||||
|
||||
// populate already loaded entry
|
||||
|
||||
@ -389,7 +389,7 @@ const createCompoLinkModelMeta = baseModelMeta => {
|
||||
return {
|
||||
// TODO: make sure there can't be any conflicts with a prefix
|
||||
// singularName: 'compo',
|
||||
uid: `${baseModelMeta.uid}_components`,
|
||||
uid: `${baseModelMeta.tableName}_components`,
|
||||
tableName: `${baseModelMeta.tableName}_components`,
|
||||
attributes: {
|
||||
id: {
|
||||
|
||||
@ -280,7 +280,6 @@ const applyWhereToColumn = (qb, column, columnWhere) => {
|
||||
|
||||
return qb.where(column, '<>', value);
|
||||
}
|
||||
|
||||
case '$gt': {
|
||||
return qb.where(column, '>', value);
|
||||
}
|
||||
@ -293,20 +292,16 @@ const applyWhereToColumn = (qb, column, columnWhere) => {
|
||||
case '$lte': {
|
||||
return qb.where(column, '<=', value);
|
||||
}
|
||||
|
||||
case '$null': {
|
||||
return value === true ? qb.whereNull() : qb.whereNotNull();
|
||||
return value === true ? qb.whereNull(column) : qb.whereNotNull(column);
|
||||
}
|
||||
|
||||
case '$between': {
|
||||
return qb.whereBetween(column, value);
|
||||
}
|
||||
|
||||
case '$regexp': {
|
||||
// TODO:
|
||||
return;
|
||||
}
|
||||
|
||||
// string
|
||||
// TODO: use $case to make it case insensitive
|
||||
case '$like': {
|
||||
@ -447,22 +442,20 @@ const processPopulate = (populate, ctx) => {
|
||||
|
||||
const applyPopulate = async (results, populate, ctx) => {
|
||||
// TODO: cleanup code
|
||||
// TODO: create aliases for pivot columns
|
||||
// TODO: remove joinColumn
|
||||
// TODO: optimize depth to avoid overfetching
|
||||
// TODO: ⚠️ on join tables we might want to make one query to find all the xxx_id then one query instead of a join to avoid returning multiple times the same object
|
||||
|
||||
const { db, uid, qb } = ctx;
|
||||
const meta = db.metadata.get(uid);
|
||||
|
||||
// TODO: support deep populates
|
||||
for (const key in populate) {
|
||||
const populateValue = populate[key];
|
||||
const attribute = meta.attributes[key];
|
||||
|
||||
const targetMeta = db.metadata.get(attribute.target);
|
||||
|
||||
// TODO: use query builder directly ?
|
||||
|
||||
// will need some specific code per relation
|
||||
|
||||
if (attribute.relation === 'oneToOne' || attribute.relation === 'manyToOne') {
|
||||
if (attribute.joinColumn) {
|
||||
const {
|
||||
@ -497,7 +490,6 @@ const applyPopulate = async (results, populate, ctx) => {
|
||||
const alias = qb.getAlias();
|
||||
const rr = await qb
|
||||
.init(populateValue)
|
||||
.select('*')
|
||||
.join({
|
||||
alias: alias,
|
||||
referencedTable: joinTable.name,
|
||||
@ -550,16 +542,12 @@ const applyPopulate = async (results, populate, ctx) => {
|
||||
|
||||
if (attribute.joinTable) {
|
||||
const { joinTable } = attribute;
|
||||
// query the target through the join table
|
||||
|
||||
const qb = db.entityManager.createQueryBuilder(targetMeta.uid);
|
||||
|
||||
// TODO: create aliases for the columns
|
||||
|
||||
const alias = qb.getAlias();
|
||||
const rr = await qb
|
||||
.init(populateValue)
|
||||
.select('*')
|
||||
.join({
|
||||
alias: alias,
|
||||
referencedTable: joinTable.name,
|
||||
@ -568,7 +556,6 @@ const applyPopulate = async (results, populate, ctx) => {
|
||||
rootTable: qb.alias,
|
||||
on: joinTable.on,
|
||||
})
|
||||
//TODO: select join column
|
||||
.addSelect(`${alias}.${joinTable.joinColumn.name}`)
|
||||
.where({
|
||||
[`${alias}.${joinTable.joinColumn.name}`]: results.map(
|
||||
@ -590,16 +577,12 @@ const applyPopulate = async (results, populate, ctx) => {
|
||||
continue;
|
||||
} else if (attribute.relation === 'manyToMany') {
|
||||
const { joinTable } = attribute;
|
||||
// query the target through the join table
|
||||
|
||||
const qb = db.entityManager.createQueryBuilder(targetMeta.uid);
|
||||
|
||||
// TODO: create aliases for the columns
|
||||
|
||||
const alias = qb.getAlias();
|
||||
const rr = await qb
|
||||
.init(populateValue)
|
||||
.select('*')
|
||||
.join({
|
||||
alias: alias,
|
||||
referencedTable: joinTable.name,
|
||||
@ -618,12 +601,13 @@ const applyPopulate = async (results, populate, ctx) => {
|
||||
|
||||
const rrMap = _.groupBy(joinTable.joinColumn.name, rr);
|
||||
|
||||
// TODO: remove joinColumn
|
||||
results.forEach(r => {
|
||||
Object.assign(r, {
|
||||
[key]: rrMap[r[joinTable.joinColumn.referencedColumn]] || [],
|
||||
});
|
||||
});
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -244,17 +244,14 @@ const createQueryBuilder = (uid, db) => {
|
||||
helpers.applyJoins(qb, state.joins);
|
||||
}
|
||||
|
||||
// TODO: hanlde populate
|
||||
|
||||
console.log('Running query: ', qb.toSQL());
|
||||
// console.log('Running query: ', qb.toQuery());
|
||||
|
||||
const queryResult = await qb;
|
||||
|
||||
const results = db.dialect.processResult(queryResult, state.type);
|
||||
|
||||
// if query response should be process (in case of custom queries we shouldn't for example)
|
||||
|
||||
if (state.populate) {
|
||||
// TODO: hanlde populate
|
||||
await helpers.applyPopulate(_.castArray(results), state.populate, { qb: this, uid, db });
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user