{"version":3,"sources":["../browser/src/persistence/subject-builder/ManyToManySubjectBuilder.ts"],"names":[],"mappings":"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","file":"ManyToManySubjectBuilder.js","sourcesContent":["import {Subject} from \"../Subject\";\nimport {OrmUtils} from \"../../util/OrmUtils\";\nimport {ObjectLiteral} from \"../../common/ObjectLiteral\";\nimport {RelationMetadata} from \"../../metadata/RelationMetadata\";\nimport {EntityMetadata} from \"../../metadata/EntityMetadata\";\n\n/**\n * Builds operations needs to be executed for many-to-many relations of the given subjects.\n *\n * by example: post contains owner many-to-many relation with categories in the property called \"categories\", e.g.\n * @ManyToMany(type => Category, category => category.posts) categories: Category[]\n * If user adds categories into the post and saves post we need to bind them.\n * This operation requires updation of junction table.\n */\nexport class ManyToManySubjectBuilder {\n\n // ---------------------------------------------------------------------\n // Constructor\n // ---------------------------------------------------------------------\n\n constructor(protected subjects: Subject[]) {\n }\n\n // ---------------------------------------------------------------------\n // Public Methods\n // ---------------------------------------------------------------------\n\n /**\n * Builds operations for any changes in the many-to-many relations of the subjects.\n */\n build(): void {\n this.subjects.forEach(subject => {\n\n // if subject doesn't have entity then no need to find something that should be inserted or removed\n if (!subject.entity)\n return;\n\n // go through all persistence enabled many-to-many relations and build subject operations for them\n subject.metadata.manyToManyRelations.forEach(relation => {\n\n // skip relations for which persistence is disabled\n if (relation.persistenceEnabled === false)\n return;\n\n this.buildForSubjectRelation(subject, relation);\n });\n });\n }\n\n /**\n * Builds operations for removal of all many-to-many records of all many-to-many relations of the given subject.\n */\n buildForAllRemoval(subject: Subject) {\n\n // if subject does not have a database entity then it means it does not exist in the database\n // if it does not exist in the database then we don't have anything for deletion\n if (!subject.databaseEntity)\n return;\n\n // go through all persistence enabled many-to-many relations and build subject operations for them\n subject.metadata.manyToManyRelations.forEach(relation => {\n\n // skip relations for which persistence is disabled\n if (relation.persistenceEnabled === false)\n return;\n\n // get all related entities (actually related entity relation ids) bind to this subject entity\n // by example: returns category ids of the post we are currently working with (subject.entity is post)\n const relatedEntityRelationIdsInDatabase: ObjectLiteral[] = relation.getEntityValue(subject.databaseEntity!);\n\n // go through all related entities and create a new junction subject for each row in junction table\n relatedEntityRelationIdsInDatabase.forEach(relationId => {\n const junctionSubject = new Subject({\n metadata: relation.junctionEntityMetadata!,\n parentSubject: subject,\n mustBeRemoved: true,\n identifier: this.buildJunctionIdentifier(subject, relation, relationId)\n });\n\n // we use unshift because we need to perform those operations before post deletion is performed\n // but post deletion was already added as an subject\n // this is temporary solution, later we need to implement proper sorting of subjects before their removal\n this.subjects.push(junctionSubject);\n });\n });\n }\n\n // ---------------------------------------------------------------------\n // Protected Methods\n // ---------------------------------------------------------------------\n\n /**\n * Builds operations for a given subject and relation.\n *\n * by example: subject is \"post\" entity we are saving here and relation is \"categories\" inside it here.\n */\n protected buildForSubjectRelation(subject: Subject, relation: RelationMetadata) {\n\n // load from db all relation ids of inverse entities that are \"bind\" to the subject's entity\n // this way we gonna check which relation ids are missing and which are new (e.g. inserted or removed)\n let databaseRelatedEntityIds: ObjectLiteral[] = [];\n\n // if subject don't have database entity it means all related entities in persisted subject are new and must be bind\n // and we don't need to remove something that is not exist\n if (subject.databaseEntity)\n databaseRelatedEntityIds = relation.getEntityValue(subject.databaseEntity);\n\n // extract entity's relation value\n // by example: categories inside our post (subject.entity is post)\n let relatedEntities: ObjectLiteral[] = relation.getEntityValue(subject.entity!);\n if (relatedEntities === null) // if value set to null its equal if we set it to empty array - all items must be removed from the database\n relatedEntities = [];\n if (!(relatedEntities instanceof Array))\n return;\n\n // from all related entities find only those which aren't found in the db - for them we will create operation subjects\n relatedEntities.forEach(relatedEntity => { // by example: relatedEntity is category from categories saved with post\n\n // todo: check how it will work for entities which are saved by cascades, but aren't saved in the database yet\n\n // extract only relation id from the related entities, since we only need it for comparision\n // by example: extract from category only relation id (category id, or let's say category title, depend on join column options)\n let relatedEntityRelationIdMap = relation.inverseEntityMetadata!.getEntityIdMap(relatedEntity);\n\n // try to find a subject of this related entity, maybe it was loaded or was marked for persistence\n const relatedEntitySubject = this.subjects.find(subject => {\n return subject.entity === relatedEntity;\n });\n\n // if subject with entity was found take subject identifier as relation id map since it may contain extra properties resolved\n if (relatedEntitySubject)\n relatedEntityRelationIdMap = relatedEntitySubject.identifier;\n\n // if related entity relation id map is empty it means related entity is newly persisted\n if (!relatedEntityRelationIdMap) {\n\n // we decided to remove this error because it brings complications when saving object with non-saved entities\n // if related entity does not have a subject then it means user tries to bind entity which wasn't saved\n // in this persistence because he didn't pass this entity for save or he did not set cascades\n // but without entity being inserted we cannot bind it in the relation operation, so we throw an exception here\n // we decided to remove this error because it brings complications when saving object with non-saved entities\n // if (!relatedEntitySubject)\n // throw new Error(`Many-to-many relation \"${relation.entityMetadata.name}.${relation.propertyPath}\" contains ` +\n // `entities which do not exist in the database yet, thus they cannot be bind in the database. ` +\n // `Please setup cascade insertion or save entities before binding it.`);\n if (!relatedEntitySubject)\n return;\n }\n\n // try to find related entity in the database\n // by example: find post's category in the database post's categories\n const relatedEntityExistInDatabase = databaseRelatedEntityIds.find(databaseRelatedEntityRelationId => {\n return EntityMetadata.compareIds(databaseRelatedEntityRelationId, relatedEntityRelationIdMap);\n });\n\n // if entity is found then don't do anything - it means binding in junction table already exist, we don't need to add anything\n if (relatedEntityExistInDatabase)\n return;\n\n const ownerValue = relation.isOwning ? subject : (relatedEntitySubject || relatedEntity); // by example: ownerEntityMap is post from subject here\n const inverseValue = relation.isOwning ? (relatedEntitySubject || relatedEntity) : subject; // by example: inverseEntityMap is category from categories array here\n\n // create a new subject for insert operation of junction rows\n const junctionSubject = new Subject({\n metadata: relation.junctionEntityMetadata!,\n parentSubject: subject,\n canBeInserted: true,\n });\n this.subjects.push(junctionSubject);\n\n relation.junctionEntityMetadata!.ownerColumns.forEach(column => {\n junctionSubject.changeMaps.push({\n column: column,\n value: ownerValue,\n // valueFactory: (value) => column.referencedColumn!.getEntityValue(value) // column.referencedColumn!.getEntityValue(ownerEntityMap),\n });\n });\n\n relation.junctionEntityMetadata!.inverseColumns.forEach(column => {\n junctionSubject.changeMaps.push({\n column: column,\n value: inverseValue,\n // valueFactory: (value) => column.referencedColumn!.getEntityValue(value) // column.referencedColumn!.getEntityValue(inverseEntityMap),\n });\n });\n });\n\n // get all inverse entities relation ids that are \"bind\" to the currently persisted entity\n const changedInverseEntityRelationIds: ObjectLiteral[] = [];\n relatedEntities.forEach(relatedEntity => {\n // relation.inverseEntityMetadata!.getEntityIdMap(relatedEntity)\n let relatedEntityRelationIdMap = relation.inverseEntityMetadata!.getEntityIdMap(relatedEntity);\n\n // try to find a subject of this related entity, maybe it was loaded or was marked for persistence\n const relatedEntitySubject = this.subjects.find(subject => {\n return subject.entity === relatedEntity;\n });\n\n // if subject with entity was found take subject identifier as relation id map since it may contain extra properties resolved\n if (relatedEntitySubject)\n relatedEntityRelationIdMap = relatedEntitySubject.identifier;\n\n if (relatedEntityRelationIdMap !== undefined && relatedEntityRelationIdMap !== null)\n changedInverseEntityRelationIds.push(relatedEntityRelationIdMap);\n });\n\n // now from all entities in the persisted entity find only those which aren't found in the db\n const removedJunctionEntityIds = databaseRelatedEntityIds.filter(existRelationId => {\n return !changedInverseEntityRelationIds.find(changedRelationId => {\n return EntityMetadata.compareIds(changedRelationId, existRelationId);\n });\n });\n\n // finally create a new junction remove operations for missing related entities\n removedJunctionEntityIds.forEach(removedEntityRelationId => {\n const junctionSubject = new Subject({\n metadata: relation.junctionEntityMetadata!,\n parentSubject: subject,\n mustBeRemoved: true,\n identifier: this.buildJunctionIdentifier(subject, relation, removedEntityRelationId)\n });\n this.subjects.push(junctionSubject);\n });\n }\n\n /**\n * Creates identifiers for junction table.\n * Example: { postId: 1, categoryId: 2 }\n */\n protected buildJunctionIdentifier(subject: Subject, relation: RelationMetadata, relationId: ObjectLiteral) {\n const ownerEntityMap = relation.isOwning ? subject.entity! : relationId;\n const inverseEntityMap = relation.isOwning ? relationId : subject.entity!;\n\n const identifier: ObjectLiteral = {};\n relation.junctionEntityMetadata!.ownerColumns.forEach(column => {\n OrmUtils.mergeDeep(identifier, column.createValueMap(column.referencedColumn!.getEntityValue(ownerEntityMap)));\n });\n relation.junctionEntityMetadata!.inverseColumns.forEach(column => {\n OrmUtils.mergeDeep(identifier, column.createValueMap(column.referencedColumn!.getEntityValue(inverseEntityMap)));\n });\n return identifier;\n }\n\n}\n"],"sourceRoot":"../.."}
|