Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hotfix 3702 perf issues round 2 #3737

Merged
merged 27 commits into from
Jun 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
8c36576
Backup advances
fnocetti Jun 2, 2021
98afbae
WIP, denormalize inherited specs
daneryl Jun 3, 2021
83deb58
Style changes, finished ad hoc implementation for green test
fnocetti Jun 3, 2021
d392b1d
WIP select/relationship denormalization test cases
daneryl Jun 4, 2021
9954a79
Pass test for transitive inheritance
fnocetti Jun 4, 2021
9ff5740
Absctract new fixtures factory and a bit of refactor
fnocetti Jun 4, 2021
cfa30f6
first abstraction denormalization cases
daneryl Jun 7, 2021
94b0121
New test case
fnocetti Jun 7, 2021
0dc6a13
Now: the actually useful test
fnocetti Jun 7, 2021
abff581
abstract update denormalization methods
daneryl Jun 8, 2021
4d4bdfa
fix testsuite
daneryl Jun 8, 2021
e159edb
tests for icon and language
fnocetti Jun 8, 2021
7eb34db
some refactors
daneryl Jun 9, 2021
df93934
more compact factory.entity
daneryl Jun 9, 2021
bc3d1c8
Indexation tests, refactored lang tests
fnocetti Jun 9, 2021
6193b03
Minor refactors and case for relations with no content type
fnocetti Jun 9, 2021
a8b41de
Add test case for index relations without content and fix
fnocetti Jun 9, 2021
7e5ed5b
test for repeated denormalized data
fnocetti Jun 10, 2021
3749d0b
update denormalized labels, without inherit
daneryl Jun 10, 2021
ff18eaf
refactor denormalization process
daneryl Jun 10, 2021
f80084f
Add missing indexing to thesauris denorm
fnocetti Jun 10, 2021
859b060
properties to sync case
fnocetti Jun 10, 2021
9f4a3b0
WIP extract denormalization method
daneryl Jun 11, 2021
788c6be
update denormalizations once every language
daneryl Jun 11, 2021
5332ba1
adapt to inheritProperty on master
daneryl Jun 11, 2021
4d32f68
fixed thesauri name change denormalization
daneryl Jun 12, 2021
c55eef5
same relationship and different inherit case
daneryl Jun 14, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions app/api/csv/specs/fixtures.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,37 +19,44 @@ export default {
name: 'base template',
properties: [
{
_id: db.id(),
type: propertyTypes.text,
label: 'text label',
name: templateUtils.safeName('text label'),
},
{
_id: db.id(),
type: propertyTypes.numeric,
label: 'numeric label',
name: templateUtils.safeName('numeric label'),
},
{
_id: db.id(),
type: propertyTypes.select,
label: 'select label',
name: templateUtils.safeName('select label'),
content: thesauri1Id,
},
{
_id: db.id(),
type: 'non_defined_type',
label: 'not defined type',
name: templateUtils.safeName('not defined type'),
},
{
_id: db.id(),
type: propertyTypes.text,
label: 'not configured on csv',
name: templateUtils.safeName('not configured on csv'),
},
{
_id: db.id(),
type: propertyTypes.geolocation,
label: 'geolocation',
name: templateUtils.safeName('geolocation_geolocation'),
},
{
_id: db.id(),
type: propertyTypes.generatedid,
label: 'Auto ID',
name: templateUtils.safeName('auto id'),
Expand Down
127 changes: 127 additions & 0 deletions app/api/entities/denormalize.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import { EntitySchema } from 'shared/types/entityType';
import { PropertySchema } from 'shared/types/commonTypes';
import templates from 'api/templates/templates';
import { search } from 'api/search';
import { TemplateSchema } from 'shared/types/templateType';
import { WithId } from 'api/odm';

import model from './entitiesModel';

interface Changes {
label: string;
icon?: EntitySchema['icon'];
}

interface Params {
id: string;
language?: string;
template?: string;
}

export const updateDenormalization = async (
{ id, language, template }: Params,
changes: Changes,
properties: PropertySchema[]
) =>
Promise.all(
properties.map(async property =>
model.updateMany(
{
...(template ? { template } : {}),
...(language ? { language } : {}),
[`metadata.${property.name}.value`]: id,
},
{
$set: Object.keys(changes).reduce(
(set, prop) => ({
...set,
[`metadata.${property.name}.$[valueObject].${prop}`]: changes[<keyof Changes>prop],
}),
{}
),
},
{ arrayFilters: [{ 'valueObject.value': id }] }
)
)
);

export const updateTransitiveDenormalization = async (
{ id, language }: Params,
changes: Changes,
properties: PropertySchema[]
) =>
Promise.all(
properties.map(async property =>
model.updateMany(
{ language, [`metadata.${property.name}.inheritedValue.value`]: id },
{
...(changes.icon
? { [`metadata.${property.name}.$[].inheritedValue.$[valueObject].icon`]: changes.icon }
: {}),
[`metadata.${property.name}.$[].inheritedValue.$[valueObject].label`]: changes.label,
},
{ arrayFilters: [{ 'valueObject.value': id }] }
)
)
);

export const denormalizeRelated = async (
entity: WithId<EntitySchema>,
template: WithId<TemplateSchema>
) => {
if (!entity.title || !entity.language || !entity.sharedId) {
throw new Error('denormalization requires an entity with title, sharedId and language');
}

const transitiveProperties = await templates.propsThatNeedTransitiveDenormalization(
template._id.toString()
);
const properties = await templates.propsThatNeedDenormalization(template._id.toString());

await updateTransitiveDenormalization(
{ id: entity.sharedId, language: entity.language },
{ label: entity.title, icon: entity.icon },
transitiveProperties
);

await Promise.all(
properties.map(async prop => {
const inheritProperty = (template.properties || []).find(
p => prop.inheritProperty === p._id?.toString()
);
return updateDenormalization(
{
// @ts-ignore we have a sharedId guard, why ts does not like this ? bug ?
id: entity.sharedId,
language: entity.language,
template: prop.template,
},
{
...(inheritProperty ? { inheritedValue: entity.metadata?.[inheritProperty.name] } : {}),
label: entity.title,
icon: entity.icon,
},
[prop]
);
})
);

if (properties.length || transitiveProperties.length) {
await search.indexEntities({
$and: [
{ language: entity.language },
{
$or: [
...properties.map(property => ({
[`metadata.${property.name}.value`]: entity.sharedId,
})),
...transitiveProperties.map(property => ({
[`metadata.${property.name}.inheritedValue.value`]: entity.sharedId,
})),
],
},
],
});
}
////Crappy draft code ends
};
123 changes: 51 additions & 72 deletions app/api/entities/entities.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ import { validateEntity } from 'shared/types/entitySchema';
import { deleteFiles, deleteUploadedFiles } from '../files/filesystem';
import model from './entitiesModel';
import settings from '../settings';
import {
denormalizeRelated,
updateTransitiveDenormalization,
updateDenormalization,
} from './denormalize';

/** Repopulate metadata object .label from thesauri and relationships. */
async function denormalizeMetadata(metadata, entity, template, dictionariesByKey) {
Expand Down Expand Up @@ -140,29 +145,25 @@ async function updateEntity(entity, _template, unrestricted = false) {
return Promise.all(
docLanguages.map(async d => {
if (d._id.toString() === entity._id.toString()) {
if (
(entity.title && currentDoc.title !== entity.title) ||
(entity.icon && !currentDoc.icon) ||
(entity.icon && currentDoc.icon && currentDoc.icon._id !== entity.icon._id)
) {
await this.renameRelatedEntityInMetadata({ ...currentDoc, ...entity });
}

const toSave = { ...entity };

delete toSave.published;
delete toSave.permissions;

if (entity.metadata) {
toSave.metadata = await denormalizeMetadata(entity.metadata, entity, template);
}

if (entity.suggestedMetadata) {
toSave.suggestedMetadata = await denormalizeMetadata(
entity.suggestedMetadata,
entity,
template
);
}
if (template._id) {
const fullEntity = { ...currentDoc, ...toSave };
await denormalizeRelated(fullEntity, template);
}
return saveFunc(toSave);
}

Expand All @@ -189,6 +190,11 @@ async function updateEntity(entity, _template, unrestricted = false) {
if (typeof entity.generatedToc !== 'undefined') {
d.generatedToc = entity.generatedToc;
}

if (template._id) {
await denormalizeRelated(d, template);
}

return saveFunc(d);
})
);
Expand Down Expand Up @@ -382,7 +388,7 @@ export default {
)[0];
if (updateRelationships) {
await relationships.saveEntityBasedReferences(entity, language);
await this.updateDenormalizedMetadataInRelatedEntities(entity);
// await this.updateDenormalizedMetadataInRelatedEntities(entity);
}
if (index) {
await search.indexEntities({ sharedId }, '+fullText');
Expand All @@ -391,12 +397,6 @@ export default {
return entity;
},

async updateDenormalizedMetadataInRelatedEntities(entity) {
const related = await relationships.getByDocument(entity.sharedId, entity.language);
const sharedIds = related.map(r => r.entityData.sharedId);
await this.updateMetdataFromRelationships(sharedIds, entity.language);
},

async denormalize(_doc, { user, language }) {
await validateEntity(_doc);
const doc = _doc;
Expand Down Expand Up @@ -755,68 +755,47 @@ export default {
]);
},

/** Propagate the change of a thesaurus or related entity label to all entity metadata. */
async renameInMetadata(valueId, changes, propertyContent, { types, restrictLanguage = null }) {
const properties = (await templates.get({ 'properties.content': propertyContent }))
/** Propagate the change of a thesaurus label to all entity metadata. */
async renameThesaurusInMetadata(valueId, newLabel, thesaurusId, language) {
const properties = (
await templates.get({
'properties.content': thesaurusId,
})
)
.reduce((m, t) => m.concat(t.properties), [])
.filter(p => types.includes(p.type))
.filter(
p => propertyContent && p.content && propertyContent.toString() === p.content.toString()
);
.filter(p => p.content && thesaurusId === p.content.toString());

if (!properties.length) {
return Promise.resolve();
}
await updateDenormalization({ id: valueId, language }, { label: newLabel }, properties);

await Promise.all(
properties.map(property =>
model.updateMany(
{ language: restrictLanguage, [`metadata.${property.name}.value`]: valueId },
{
$set: Object.keys(changes).reduce(
(set, prop) => ({
...set,
[`metadata.${property.name}.$[valueObject].${prop}`]: changes[prop],
}),
{}
),
},
{ arrayFilters: [{ 'valueObject.value': valueId }] }
)
)
const transitiveProps = await templates.propsThatNeedTransitiveDenormalization(
thesaurusId.toString()
);

return search.indexEntities({
$and: [
{
language: restrictLanguage,
},
{
$or: properties.map(property => ({ [`metadata.${property.name}.value`]: valueId })),
},
],
});
},

/** Propagate the change of a thesaurus label to all entity metadata. */
async renameThesaurusInMetadata(valueId, newLabel, thesaurusId, language) {
await this.renameInMetadata(valueId, { label: newLabel }, thesaurusId, {
types: [propertyTypes.select, propertyTypes.multiselect],
restrictLanguage: language,
});
},

/** Propagate the title change of a related entity to all entity metadata. */
async renameRelatedEntityInMetadata(relatedEntity) {
await this.renameInMetadata(
relatedEntity.sharedId,
{ label: relatedEntity.title, icon: relatedEntity.icon },
relatedEntity.template,
{
types: [propertyTypes.select, propertyTypes.multiselect, propertyTypes.relationship],
restrictLanguage: relatedEntity.language,
}
await updateTransitiveDenormalization(
{ id: valueId, language },
{ label: newLabel },
transitiveProps
);

if (properties.length || transitiveProps.length) {
await search.indexEntities({
$and: [
{
language,
},
{
$or: [
...properties.map(property => ({
[`metadata.${property.name}.value`]: valueId,
})),
...transitiveProps.map(property => ({
[`metadata.${property.name}.inheritedValue.value`]: valueId,
})),
],
},
],
});
}
},

async createThumbnail(entity) {
Expand Down
Loading