mirror of
https://github.com/strapi/strapi.git
synced 2025-12-04 11:02:12 +00:00
introduce getDeepCount for relations performance
This commit is contained in:
parent
25e1435173
commit
27b8b5b353
@ -4,6 +4,7 @@ const { assoc, has, prop, omit } = require('lodash/fp');
|
|||||||
const strapiUtils = require('@strapi/utils');
|
const strapiUtils = require('@strapi/utils');
|
||||||
const { ApplicationError } = require('@strapi/utils').errors;
|
const { ApplicationError } = require('@strapi/utils').errors;
|
||||||
const { getDeepPopulate, getDeepPopulateDraftCount } = require('./utils/populate');
|
const { getDeepPopulate, getDeepPopulateDraftCount } = require('./utils/populate');
|
||||||
|
const { getDeepRelationsCount } = require('./utils/count');
|
||||||
const { sumDraftCounts } = require('./utils/draft');
|
const { sumDraftCounts } = require('./utils/draft');
|
||||||
|
|
||||||
const { hasDraftAndPublish } = strapiUtils.contentTypes;
|
const { hasDraftAndPublish } = strapiUtils.contentTypes;
|
||||||
@ -26,12 +27,8 @@ const wrapWithEmitEvent = (event, fn) => async (entity, body, model) => {
|
|||||||
entry: sanitizedEntity,
|
entry: sanitizedEntity,
|
||||||
});
|
});
|
||||||
|
|
||||||
// If relations were populated, load the entity again without populating them,
|
|
||||||
// to avoid performance issues
|
|
||||||
if (isRelationsPopulateEnabled(model)) {
|
if (isRelationsPopulateEnabled(model)) {
|
||||||
return strapi.entityService.findOne(model, entity.id, {
|
return getDeepRelationsCount(entity, model);
|
||||||
populate: getCountDeepPopulate(model),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@ -64,7 +61,7 @@ const addCreatedByRolesPopulate = (populate) => {
|
|||||||
* For performance reasons, it is recommended to set it to false,
|
* For performance reasons, it is recommended to set it to false,
|
||||||
*/
|
*/
|
||||||
const isRelationsPopulateEnabled = () => {
|
const isRelationsPopulateEnabled = () => {
|
||||||
return strapi.config.get('server.relations.populate', false);
|
return strapi.config.get('server.relations.populate', true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getCountDeepPopulate = (uid) => getDeepPopulate(uid, { countMany: true, countOne: true });
|
const getCountDeepPopulate = (uid) => getDeepPopulate(uid, { countMany: true, countOne: true });
|
||||||
@ -143,7 +140,7 @@ module.exports = ({ strapi }) => ({
|
|||||||
// If relations were populated, load the entity again without populating them,
|
// If relations were populated, load the entity again without populating them,
|
||||||
// to avoid performance issues
|
// to avoid performance issues
|
||||||
if (populateRelations) {
|
if (populateRelations) {
|
||||||
return strapi.entityService.findOne(uid, entity.id, { populate: getCountDeepPopulate(uid) });
|
return getDeepRelationsCount(entity, uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
return entity;
|
return entity;
|
||||||
@ -160,32 +157,27 @@ module.exports = ({ strapi }) => ({
|
|||||||
|
|
||||||
const updatedEntity = await strapi.entityService.update(uid, entity.id, params);
|
const updatedEntity = await strapi.entityService.update(uid, entity.id, params);
|
||||||
|
|
||||||
// If relations were populated, load the entity again without populating them,
|
|
||||||
// to avoid performance issues
|
|
||||||
if (populateRelations) {
|
if (populateRelations) {
|
||||||
return strapi.entityService.findOne(uid, entity.id, { populate: getCountDeepPopulate(uid) });
|
return getDeepRelationsCount(updatedEntity, uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
return updatedEntity;
|
return updatedEntity;
|
||||||
},
|
},
|
||||||
|
|
||||||
async delete(entity, uid) {
|
async delete(entity, uid) {
|
||||||
let entityToDelete;
|
|
||||||
const populateRelations = isRelationsPopulateEnabled(uid);
|
const populateRelations = isRelationsPopulateEnabled(uid);
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
populate: populateRelations ? getDeepPopulate(uid, {}) : getCountDeepPopulate(uid),
|
populate: populateRelations ? getDeepPopulate(uid, {}) : getCountDeepPopulate(uid),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (populateRelations) {
|
|
||||||
entityToDelete = await strapi.entityService.findOne(uid, entity.id, {
|
|
||||||
populate: getCountDeepPopulate(uid),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const deletedEntity = await strapi.entityService.delete(uid, entity.id, params);
|
const deletedEntity = await strapi.entityService.delete(uid, entity.id, params);
|
||||||
|
|
||||||
return entityToDelete || deletedEntity;
|
if (populateRelations) {
|
||||||
|
return getDeepRelationsCount(deletedEntity, uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
return deletedEntity;
|
||||||
},
|
},
|
||||||
|
|
||||||
// FIXME: handle relations
|
// FIXME: handle relations
|
||||||
|
|||||||
@ -0,0 +1,182 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { getDeepRelationsCount } = require('../count');
|
||||||
|
|
||||||
|
const fakeModels = {
|
||||||
|
component: {
|
||||||
|
modelName: 'Fake component model',
|
||||||
|
attributes: {
|
||||||
|
componentAttrName: {
|
||||||
|
type: 'component',
|
||||||
|
component: 'relationMTM',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
dynZone: {
|
||||||
|
modelName: 'Fake dynamic zone model',
|
||||||
|
attributes: {
|
||||||
|
dynZoneAttrName: {
|
||||||
|
type: 'dynamiczone',
|
||||||
|
components: ['component'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
relationMTM: {
|
||||||
|
modelName: 'Fake relation manyToMany model',
|
||||||
|
attributes: {
|
||||||
|
relationAttrName: {
|
||||||
|
type: 'relation',
|
||||||
|
relation: 'oneToMany',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
relationOTO: {
|
||||||
|
modelName: 'Fake relation oneToOne model',
|
||||||
|
attributes: {
|
||||||
|
relationAttrName: {
|
||||||
|
type: 'relation',
|
||||||
|
relation: 'oneToOne',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
media: {
|
||||||
|
modelName: 'Fake media model',
|
||||||
|
attributes: {
|
||||||
|
mediaAttrName: {
|
||||||
|
type: 'media',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('Count', () => {
|
||||||
|
describe('getDeepRelationsCount', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
global.strapi = {
|
||||||
|
getModel: jest.fn((uid) => fakeModels[uid]),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('with many to many', () => {
|
||||||
|
const count = getDeepRelationsCount(
|
||||||
|
{
|
||||||
|
relationAttrName: [
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
name: 'rel1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 7,
|
||||||
|
name: 'rel2',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
'relationMTM'
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(count).toEqual({
|
||||||
|
relationAttrName: {
|
||||||
|
count: 2,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('with one to one', () => {
|
||||||
|
const count = getDeepRelationsCount(
|
||||||
|
{
|
||||||
|
relationAttrName: {
|
||||||
|
id: 2,
|
||||||
|
name: 'rel1',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'relationOTO'
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(count).toEqual({
|
||||||
|
relationAttrName: {
|
||||||
|
id: 2,
|
||||||
|
name: 'rel1',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('with media', () => {
|
||||||
|
const mediaEntity = {
|
||||||
|
mediaAttrName: { id: 1, name: 'img1' },
|
||||||
|
};
|
||||||
|
const count = getDeepRelationsCount(mediaEntity, 'media');
|
||||||
|
|
||||||
|
expect(count).toEqual(mediaEntity);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('with component', () => {
|
||||||
|
const count = getDeepRelationsCount(
|
||||||
|
{
|
||||||
|
componentAttrName: {
|
||||||
|
relationAttrName: [
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
name: 'rel1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 7,
|
||||||
|
name: 'rel2',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'component'
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(count).toEqual({
|
||||||
|
componentAttrName: {
|
||||||
|
relationAttrName: {
|
||||||
|
count: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('with dynamic zone', () => {
|
||||||
|
const count = getDeepRelationsCount(
|
||||||
|
{
|
||||||
|
dynZoneAttrName: [
|
||||||
|
{
|
||||||
|
__component: 'component',
|
||||||
|
componentAttrName: {
|
||||||
|
relationAttrName: [
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
name: 'rel1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 7,
|
||||||
|
name: 'rel2',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
'dynZone'
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(count).toEqual({
|
||||||
|
dynZoneAttrName: [
|
||||||
|
{
|
||||||
|
__component: 'component',
|
||||||
|
componentAttrName: {
|
||||||
|
relationAttrName: {
|
||||||
|
count: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
59
packages/core/content-manager/server/services/utils/count.js
Normal file
59
packages/core/content-manager/server/services/utils/count.js
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { merge } = require('lodash/fp');
|
||||||
|
|
||||||
|
function getCountForRelation(entity, attributeName) {
|
||||||
|
const entityAttribute = entity[attributeName];
|
||||||
|
|
||||||
|
// Check if is an array? what happens if its a one to one relation?
|
||||||
|
if (Array.isArray(entityAttribute)) {
|
||||||
|
return { count: entityAttribute.length };
|
||||||
|
}
|
||||||
|
|
||||||
|
return entityAttribute;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCountForDZ(attributeName, entity) {
|
||||||
|
return entity[attributeName].map((component) => {
|
||||||
|
return getDeepRelationsCount(component, component.__component);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCountFor(attributeName, entity, model) {
|
||||||
|
const attribute = model.attributes[attributeName];
|
||||||
|
|
||||||
|
// Check if attribute is empty
|
||||||
|
if (!entity[attributeName]) {
|
||||||
|
return { [attributeName]: entity[attributeName] };
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (attribute?.type) {
|
||||||
|
case 'relation':
|
||||||
|
return {
|
||||||
|
[attributeName]: getCountForRelation(entity, attributeName),
|
||||||
|
};
|
||||||
|
case 'component':
|
||||||
|
return {
|
||||||
|
[attributeName]: getDeepRelationsCount(entity[attributeName], attribute.component),
|
||||||
|
};
|
||||||
|
case 'dynamiczone':
|
||||||
|
return {
|
||||||
|
[attributeName]: getCountForDZ(attributeName, entity),
|
||||||
|
};
|
||||||
|
default:
|
||||||
|
return { [attributeName]: entity[attributeName] };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getDeepRelationsCount = (entity, uid) => {
|
||||||
|
const model = strapi.getModel(uid);
|
||||||
|
|
||||||
|
return Object.keys(entity).reduce(
|
||||||
|
(populateAcc, attributeName) => merge(populateAcc, getCountFor(attributeName, entity, model)),
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
getDeepRelationsCount,
|
||||||
|
};
|
||||||
Loading…
x
Reference in New Issue
Block a user