mirror of
https://github.com/strapi/strapi.git
synced 2025-11-02 10:55:37 +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 { ApplicationError } = require('@strapi/utils').errors;
|
||||
const { getDeepPopulate, getDeepPopulateDraftCount } = require('./utils/populate');
|
||||
const { getDeepRelationsCount } = require('./utils/count');
|
||||
const { sumDraftCounts } = require('./utils/draft');
|
||||
|
||||
const { hasDraftAndPublish } = strapiUtils.contentTypes;
|
||||
@ -26,12 +27,8 @@ const wrapWithEmitEvent = (event, fn) => async (entity, body, model) => {
|
||||
entry: sanitizedEntity,
|
||||
});
|
||||
|
||||
// If relations were populated, load the entity again without populating them,
|
||||
// to avoid performance issues
|
||||
if (isRelationsPopulateEnabled(model)) {
|
||||
return strapi.entityService.findOne(model, entity.id, {
|
||||
populate: getCountDeepPopulate(model),
|
||||
});
|
||||
return getDeepRelationsCount(entity, model);
|
||||
}
|
||||
|
||||
return result;
|
||||
@ -64,7 +61,7 @@ const addCreatedByRolesPopulate = (populate) => {
|
||||
* For performance reasons, it is recommended to set it to false,
|
||||
*/
|
||||
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 });
|
||||
@ -143,7 +140,7 @@ module.exports = ({ strapi }) => ({
|
||||
// If relations were populated, load the entity again without populating them,
|
||||
// to avoid performance issues
|
||||
if (populateRelations) {
|
||||
return strapi.entityService.findOne(uid, entity.id, { populate: getCountDeepPopulate(uid) });
|
||||
return getDeepRelationsCount(entity, uid);
|
||||
}
|
||||
|
||||
return entity;
|
||||
@ -160,32 +157,27 @@ module.exports = ({ strapi }) => ({
|
||||
|
||||
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) {
|
||||
return strapi.entityService.findOne(uid, entity.id, { populate: getCountDeepPopulate(uid) });
|
||||
return getDeepRelationsCount(updatedEntity, uid);
|
||||
}
|
||||
|
||||
return updatedEntity;
|
||||
},
|
||||
|
||||
async delete(entity, uid) {
|
||||
let entityToDelete;
|
||||
const populateRelations = isRelationsPopulateEnabled(uid);
|
||||
|
||||
const params = {
|
||||
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);
|
||||
|
||||
return entityToDelete || deletedEntity;
|
||||
if (populateRelations) {
|
||||
return getDeepRelationsCount(deletedEntity, uid);
|
||||
}
|
||||
|
||||
return deletedEntity;
|
||||
},
|
||||
|
||||
// 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