metadata refactoring

This commit is contained in:
Umed Khudoiberdiev 2017-05-19 13:21:05 +05:00
parent 68f4cc48be
commit 4dfe6f9946
8 changed files with 135 additions and 75 deletions

View File

@ -94,9 +94,7 @@ export class EntityMetadataBuilder {
// here we create a junction entity metadata for a new junction table of many-to-many relation
const junctionEntityMetadata = this.junctionEntityMetadataBuilder.build(relation, joinTable);
relation.registerForeignKeys(...junctionEntityMetadata.foreignKeys);
relation.junctionEntityMetadata = junctionEntityMetadata;
if (relation.inverseRelation)
relation.inverseRelation.junctionEntityMetadata = junctionEntityMetadata;
relation.registerJunctionEntityMetadata(junctionEntityMetadata);
// compute new entity metadata properties and push it to entity metadatas pool
this.computeEntityMetadata(junctionEntityMetadata);
@ -210,10 +208,10 @@ export class EntityMetadataBuilder {
entityMetadata.embeddeds.forEach(embedded => embedded.build(this.connection.driver.namingStrategy));
entityMetadata.embeddeds.forEach(embedded => {
embedded.columnsFromTree.forEach(column => column.build(this.connection.driver.namingStrategy));
embedded.relationsFromTree.forEach(relation => relation.build(this.connection.driver.namingStrategy));
embedded.relationsFromTree.forEach(relation => relation.build());
});
entityMetadata.ownColumns.forEach(column => column.build(this.connection.driver.namingStrategy));
entityMetadata.ownRelations.forEach(relation => relation.build(this.connection.driver.namingStrategy));
entityMetadata.ownRelations.forEach(relation => relation.build());
entityMetadata.relations = entityMetadata.embeddeds.reduce((relations, embedded) => relations.concat(embedded.relationsFromTree), entityMetadata.ownRelations);
entityMetadata.oneToOneRelations = entityMetadata.relations.filter(relation => relation.isOneToOne);
entityMetadata.oneToManyRelations = entityMetadata.relations.filter(relation => relation.isOneToMany);
@ -236,6 +234,13 @@ export class EntityMetadataBuilder {
entityMetadata.objectIdColumn = entityMetadata.columns.find(column => column.isObjectId);
entityMetadata.foreignKeys.forEach(foreignKey => foreignKey.build(this.connection.driver.namingStrategy));
entityMetadata.propertiesMap = entityMetadata.createPropertiesMap();
entityMetadata.relationIds.forEach(relationId => relationId.build());
// entityMetadata.relationCounts.forEach(relationCount => relationCount.build());
entityMetadata.embeddeds.forEach(embedded => {
embedded.relationIdsFromTree.forEach(relationId => relationId.build());
// embedded.relationCountsFromTree.forEach(relationCount => relationCount.build());
});
}
/**

View File

@ -17,6 +17,8 @@ export class ColumnMetadata {
/**
* Entity metadata where this column metadata is.
*
* For example for @Column() name: string in Post, entityMetadata will be metadata of Post entity.
*/
entityMetadata: EntityMetadata;

View File

@ -3,6 +3,8 @@ import {RelationMetadata} from "./RelationMetadata";
import {EntityMetadata} from "./EntityMetadata";
import {NamingStrategyInterface} from "../naming-strategy/NamingStrategyInterface";
import {EmbeddedMetadataArgs} from "../metadata-args/EmbeddedMetadataArgs";
import {RelationIdMetadata} from "./RelationIdMetadata";
import {RelationCountMetadata} from "./RelationCountMetadata";
/**
* Contains all information about entity's embedded property.
@ -87,7 +89,7 @@ export class EmbeddedMetadata {
embeddedMetadataTree: EmbeddedMetadata[] = [];
/**
* Returns embed metadatas from all levels of the parent tree.
* Embed metadatas from all levels of the parent tree.
*
* example: post[data][information][counters].id where "data", "information" and "counters" are embeds
* this method will return [embed metadata of data, embed metadata of information, embed metadata of counters]
@ -95,10 +97,20 @@ export class EmbeddedMetadata {
columnsFromTree: ColumnMetadata[] = [];
/**
* Returns all relations of this embed and all relations from its child embeds.
* Relations of this embed and all relations from its child embeds.
*/
relationsFromTree: RelationMetadata[] = [];
/**
* Relation ids of this embed and all relation ids from its child embeds.
*/
relationIdsFromTree: RelationIdMetadata[] = [];
/**
* Relation counts of this embed and all relation counts from its child embeds.
*/
relationCountsFromTree: RelationCountMetadata[] = [];
// ---------------------------------------------------------------------
// Constructor
// ---------------------------------------------------------------------

View File

@ -87,6 +87,10 @@ export class ForeignKeyMetadata {
// Public Methods
// ---------------------------------------------------------------------
/**
* Builds some depend foreign key properties.
* Must be called after all entity metadatas and their columns are built.
*/
build(namingStrategy: NamingStrategyInterface) {
this.columnNames = this.columns.map(column => column.databaseName);
this.referencedColumnNames = this.referencedColumns.map(column => column.databaseName);

View File

@ -79,6 +79,10 @@ export class IndexMetadata {
// Public Build Methods
// ---------------------------------------------------------------------
/**
* Builds some depend index properties.
* Must be called after all entity metadata's properties map, columns and relations are built.
*/
build(namingStrategy: NamingStrategyInterface): this {
this.tableName = this.entityMetadata.tableName;

View File

@ -17,6 +17,11 @@ export class RelationCountMetadata {
*/
entityMetadata: EntityMetadata;
/**
* Relation which needs to be counted.
*/
relation: RelationMetadata;
/**
* Relation name which need to count.
*/
@ -51,27 +56,28 @@ export class RelationCountMetadata {
args: RelationCountMetadataArgs
}) {
this.entityMetadata = options.entityMetadata;
const args = options.args;
this.target = args.target;
this.propertyName = args.propertyName;
this.relationNameOrFactory = args.relation;
this.alias = args.alias;
this.queryBuilderFactory = args.queryBuilderFactory;
this.target = options.args.target;
this.propertyName = options.args.propertyName;
this.relationNameOrFactory = options.args.relation;
this.alias = options.args.alias;
this.queryBuilderFactory = options.args.queryBuilderFactory;
}
// ---------------------------------------------------------------------
// Accessors
// Public Builder Methods
// ---------------------------------------------------------------------
/**
* Relation which need to count.
* Builds some depend relation count metadata properties.
* This builder method should be used only after entity metadata, its properties map and all relations are build.
*/
get relation(): RelationMetadata {
const propertyName = this.relationNameOrFactory instanceof Function ? this.relationNameOrFactory(this.entityMetadata.propertiesMap) : this.relationNameOrFactory;
const relation = this.entityMetadata.relations.find(relation => relation.propertyName === propertyName);
build() {
const propertyPath = this.relationNameOrFactory instanceof Function ? this.relationNameOrFactory(this.entityMetadata.propertiesMap) : this.relationNameOrFactory;
const relation = this.entityMetadata.findRelationWithPropertyPath(propertyPath);
if (!relation)
throw new Error(`Cannot find relation ${propertyName}. Wrong relation specified for @RelationCount decorator.`);
throw new Error(`Cannot find relation ${propertyPath}. Wrong relation specified for @RelationCount decorator.`);
return relation;
this.relation = relation;
}
}

View File

@ -17,6 +17,11 @@ export class RelationIdMetadata {
*/
entityMetadata: EntityMetadata;
/**
* Relation from which ids will be extracted.
*/
relation: RelationMetadata;
/**
* Relation name which need to count.
*/
@ -51,28 +56,28 @@ export class RelationIdMetadata {
args: RelationIdMetadataArgs
}) {
this.entityMetadata = options.entityMetadata;
const args = options.args;
this.target = args.target;
this.propertyName = args.propertyName;
this.relationNameOrFactory = args.relation;
this.alias = args.alias;
this.queryBuilderFactory = args.queryBuilderFactory;
this.target = options.args.target;
this.propertyName = options.args.propertyName;
this.relationNameOrFactory = options.args.relation;
this.alias = options.args.alias;
this.queryBuilderFactory = options.args.queryBuilderFactory;
}
// ---------------------------------------------------------------------
// Accessors
// Public Builder Methods
// ---------------------------------------------------------------------
/**
* Relation which need to count.
* Builds some depend relation id properties.
* This builder method should be used only after entity metadata, its properties map and all relations are build.
*/
get relation(): RelationMetadata {
const propertyName = this.relationNameOrFactory instanceof Function ? this.relationNameOrFactory(this.entityMetadata.propertiesMap) : this.relationNameOrFactory;
const relation = this.entityMetadata.relations.find(relation => relation.propertyName === propertyName);
build() {
const propertyPath = this.relationNameOrFactory instanceof Function ? this.relationNameOrFactory(this.entityMetadata.propertiesMap) : this.relationNameOrFactory;
const relation = this.entityMetadata.findRelationWithPropertyPath(propertyPath);
if (!relation)
throw new Error(`Cannot find relation ${propertyName}. Wrong relation specified for @RelationId decorator.`);
throw new Error(`Cannot find relation ${propertyPath}. Wrong relation specified for @RelationId decorator.`);
return relation;
this.relation = relation;
}
}

View File

@ -18,11 +18,6 @@ export class RelationMetadata {
// Public Properties
// ---------------------------------------------------------------------
/**
* Relation type, e.g. is it one-to-one, one-to-many, many-to-one or many-to-many.
*/
relationType: RelationType;
/**
* Entity metadata of the entity where this relation is placed.
*
@ -39,6 +34,7 @@ export class RelationMetadata {
/**
* Entity metadata of the junction table.
* Junction tables have their own entity metadata objects.
* Defined only for many-to-many relations.
*/
junctionEntityMetadata?: EntityMetadata;
@ -50,9 +46,9 @@ export class RelationMetadata {
embeddedMetadata?: EmbeddedMetadata;
/**
* Foreign keys created for this relation.
* Relation type, e.g. is it one-to-one, one-to-many, many-to-one or many-to-many.
*/
foreignKeys: ForeignKeyMetadata[] = [];
relationType: RelationType;
/**
* Target entity to which this relation is applied.
@ -124,28 +120,6 @@ export class RelationMetadata {
*/
onDelete?: OnDeleteType;
/**
* Join table name.
*/
joinTableName: string;
/**
* Join table columns.
* Join columns can be obtained only from owner side of the relation.
* From non-owner side of the relation join columns will be empty.
* If this relation is a many-to-one/one-to-one then it takes join columns from the current entity.
* If this relation is many-to-many then it takes all owner join columns from the junction entity.
*/
joinColumns: ColumnMetadata[] = [];
/**
* Inverse join table columns.
* Inverse join columns are supported only for many-to-many relations
* and can be obtained only from owner side of the relation.
* From non-owner side of the relation join columns will be undefined.
*/
inverseJoinColumns: ColumnMetadata[] = [];
/**
* Gets the property's type to which this relation is applied.
*
@ -213,7 +187,11 @@ export class RelationMetadata {
inverseSidePropertyPath: string;
/**
* Inverse side of the relation.
* Inverse side of the relation set by user.
*
* Inverse side set in the relation can be either string - property name of the column on inverse side,
* either can be a function that accepts a map of properties with the object and returns one of them.
* Second approach is used to achieve type-safety.
*/
givenInverseSidePropertyFactory: PropertyTypeFactory<any>;
@ -222,6 +200,33 @@ export class RelationMetadata {
*/
inverseRelation?: RelationMetadata;
/**
* Join table name.
*/
joinTableName: string;
/**
* Foreign keys created for this relation.
*/
foreignKeys: ForeignKeyMetadata[] = [];
/**
* Join table columns.
* Join columns can be obtained only from owner side of the relation.
* From non-owner side of the relation join columns will be empty.
* If this relation is a many-to-one/one-to-one then it takes join columns from the current entity.
* If this relation is many-to-many then it takes all owner join columns from the junction entity.
*/
joinColumns: ColumnMetadata[] = [];
/**
* Inverse join table columns.
* Inverse join columns are supported only for many-to-many relations
* and can be obtained only from owner side of the relation.
* From non-owner side of the relation join columns will be undefined.
*/
inverseJoinColumns: ColumnMetadata[] = [];
// ---------------------------------------------------------------------
// Constructor
// ---------------------------------------------------------------------
@ -368,10 +373,18 @@ export class RelationMetadata {
// Builder Methods
// ---------------------------------------------------------------------
build(namingStrategy: NamingStrategyInterface) {
/**
* Builds some depend relation metadata properties.
* This builder method should be used only after embedded metadata tree was build.
*/
build() {
this.propertyPath = this.buildPropertyPath();
}
/**
* Registers given foreign keys in the relation.
* This builder method should be used to register foreign key in the relation.
*/
registerForeignKeys(...foreignKeys: ForeignKeyMetadata[]) {
this.foreignKeys.push(...foreignKeys);
this.joinColumns = this.foreignKeys[0] ? this.foreignKeys[0].columns : [];
@ -385,11 +398,21 @@ export class RelationMetadata {
}
/**
* Inverse side set in the relation can be either string - property name of the column on inverse side,
* either can be a function that accepts a map of properties with the object and returns one of them.
* Second approach is used to achieve type-safety.
*
* todo: revisit comment
* Registers a given junction entity metadata.
* This builder method can be called after junction entity metadata for the many-to-many relation was created.
*/
registerJunctionEntityMetadata(junctionEntityMetadata: EntityMetadata) {
this.junctionEntityMetadata = junctionEntityMetadata;
this.joinTableName = junctionEntityMetadata.tableName;
if (this.inverseRelation) {
this.inverseRelation.junctionEntityMetadata = junctionEntityMetadata;
this.joinTableName = junctionEntityMetadata.tableName;
}
}
/**
* Builds inverse side property path based on given inverse side property factory.
* This builder method should be used only after properties map of the inverse entity metadata was build.
*/
buildInverseSidePropertyPath(): string {
@ -408,14 +431,13 @@ export class RelationMetadata {
return this.entityMetadata.treeParentRelation.propertyName;
}
return ""; // todo: return undefined instead?
return "";
}
// ---------------------------------------------------------------------
// Protected Methods
// ---------------------------------------------------------------------
protected buildPropertyPath(): string {
/**
* Builds relation's property path based on its embedded tree.
*/
buildPropertyPath(): string {
if (!this.embeddedMetadata || !this.embeddedMetadata.parentPropertyNames.length)
return this.propertyName;