mirror of
https://github.com/typeorm/typeorm.git
synced 2025-12-08 21:26:23 +00:00
working on relations in embeds
This commit is contained in:
parent
90addb83e1
commit
3dd69a3887
@ -309,10 +309,15 @@ export class MetadataArgsStorage {
|
||||
return Object.getPrototypeOf(target1.prototype).constructor === target2;
|
||||
}
|
||||
|
||||
findJoinTable(target: Function|string, propertyName: string) {
|
||||
findJoinTable(target: Function|string, propertyName: string): JoinTableMetadataArgs|undefined {
|
||||
return this.joinTables.toArray().find(joinTable => {
|
||||
return joinTable.target === target && joinTable.propertyName === propertyName;
|
||||
});
|
||||
}
|
||||
|
||||
findJoinColumns(target: Function|string, propertyName: string): JoinColumnMetadataArgs[] {
|
||||
return this.joinColumns.toArray().filter(joinColumn => {
|
||||
return joinColumn.target === target && joinColumn.propertyName === propertyName;
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -124,7 +124,7 @@ export class EntityMetadataBuilder {
|
||||
entityMetadata.relations.forEach(relation => {
|
||||
const inverseEntityMetadata = entityMetadatas.find(m => m.target === relation.type || (typeof relation.type === "string" && m.targetName === relation.type));
|
||||
if (!inverseEntityMetadata)
|
||||
throw new Error("Entity metadata for " + entityMetadata.name + "#" + relation.propertyName + " was not found.");
|
||||
throw new Error("Entity metadata for " + entityMetadata.name + "#" + relation.propertyPath + " was not found.");
|
||||
|
||||
relation.inverseEntityMetadata = inverseEntityMetadata;
|
||||
});
|
||||
@ -178,10 +178,6 @@ export class EntityMetadataBuilder {
|
||||
});
|
||||
|
||||
entityMetadatas.forEach(entityMetadata => {
|
||||
const mergedArgs = allMergedArgs.find(mergedArgs => {
|
||||
return mergedArgs.table.target === entityMetadata.target;
|
||||
});
|
||||
if (!mergedArgs) return;
|
||||
|
||||
// create entity's relations join columns
|
||||
entityMetadata.oneToOneRelations
|
||||
@ -211,7 +207,7 @@ export class EntityMetadataBuilder {
|
||||
// we need to go thought each many-to-one relation without join column decorator set
|
||||
// and create join column metadata args for them
|
||||
|
||||
const joinColumnArgsArray = mergedArgs.joinColumns.filterByProperty(relation.propertyName);
|
||||
const joinColumnArgsArray = metadataArgsStorage.findJoinColumns(relation.target, relation.propertyName);
|
||||
|
||||
const hasAnyReferencedColumnName = joinColumnArgsArray.find(joinColumnArgs => !!joinColumnArgs.referencedColumnName);
|
||||
const manyToOneWithoutJoinColumn = joinColumnArgsArray.length === 0 && relation.isManyToOne;
|
||||
|
||||
@ -631,18 +631,22 @@ export class EntityMetadata {
|
||||
|
||||
/**
|
||||
* Creates an object - map of columns and relations of the entity.
|
||||
*
|
||||
* example: Post{ id: number, name: string, counterEmbed: { count: number }, category: Category }.
|
||||
* This method will create following object:
|
||||
* { id: "id", counterEmbed: { count: "counterEmbed.count" }, category: "category" }
|
||||
*/
|
||||
createPropertiesMap(): { [name: string]: string|any } {
|
||||
const entity: { [name: string]: string|any } = {};
|
||||
this._columns.forEach(column => entity[column.propertyName] = column.propertyName);
|
||||
this.relations.forEach(relation => entity[relation.propertyName] = relation.propertyName);
|
||||
return entity;
|
||||
const map: { [name: string]: string|any } = {};
|
||||
this.columns.forEach(column => OrmUtils.mergeDeep(map, column.createValueMap(column.propertyPath)));
|
||||
this.relations.forEach(relation => OrmUtils.mergeDeep(map, relation.createValueMap(relation.propertyPath)));
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes property name of the entity using given PropertyTypeInFunction.
|
||||
*/
|
||||
computePropertyName(nameOrFn: PropertyTypeInFunction<any>) {
|
||||
computePropertyPath(nameOrFn: PropertyTypeInFunction<any>) {
|
||||
return typeof nameOrFn === "string" ? nameOrFn : nameOrFn(this.createPropertiesMap());
|
||||
}
|
||||
|
||||
@ -1007,27 +1011,13 @@ export class EntityMetadata {
|
||||
* Checks if given entity has an id.
|
||||
*/
|
||||
hasId(entity: ObjectLiteral): boolean {
|
||||
if (!entity)
|
||||
return false;
|
||||
|
||||
// if (this.metadata.parentEntityMetadata) {
|
||||
// return this.metadata.parentEntityMetadata.parentIdColumns.every(parentIdColumn => {
|
||||
// const columnName = parentIdColumn.propertyName;
|
||||
// return !!entity &&
|
||||
// entity.hasOwnProperty(columnName) &&
|
||||
// entity[columnName] !== null &&
|
||||
// entity[columnName] !== undefined &&
|
||||
// entity[columnName] !== "";
|
||||
// });
|
||||
|
||||
// } else {
|
||||
return this.primaryColumns.every(primaryColumn => {
|
||||
const columnName = primaryColumn.propertyName;
|
||||
return !!entity &&
|
||||
entity.hasOwnProperty(columnName) &&
|
||||
entity[columnName] !== null &&
|
||||
entity[columnName] !== undefined &&
|
||||
entity[columnName] !== "";
|
||||
return this.primaryColumns.every(primaryColumn => { /// todo: this.metadata.parentEntityMetadata ?
|
||||
const value = primaryColumn.getValue(entity);
|
||||
return value !== null && value !== undefined && value!== "";
|
||||
});
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -401,13 +401,13 @@ export class RelationMetadata {
|
||||
* Checks if inverse side is specified by a relation.
|
||||
*/
|
||||
get hasInverseSide(): boolean {
|
||||
return this.inverseEntityMetadata && this.inverseEntityMetadata.hasRelationWithPropertyName(this.inverseSideProperty);
|
||||
return this.inverseEntityMetadata && this.inverseEntityMetadata.hasRelationWithPropertyName(this.inverseSidePropertyPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the property name of the inverse side of the relation.
|
||||
*/
|
||||
get inverseSideProperty(): string { // todo: should be called inverseSidePropertyName ?
|
||||
get inverseSidePropertyPath(): string { // todo: should be called inverseSidePropertyName ?
|
||||
|
||||
if (this._inverseSideProperty) {
|
||||
return this.computeInverseSide(this._inverseSideProperty);
|
||||
@ -427,9 +427,9 @@ export class RelationMetadata {
|
||||
* Gets the relation metadata of the inverse side of this relation.
|
||||
*/
|
||||
get inverseRelation(): RelationMetadata {
|
||||
const relation = this.inverseEntityMetadata.findRelationWithPropertyName(this.inverseSideProperty);
|
||||
const relation = this.inverseEntityMetadata.findRelationWithPropertyPath(this.inverseSidePropertyPath);
|
||||
if (!relation)
|
||||
throw new Error(`Inverse side was not found in the relation ${this.entityMetadata.name}#${this.inverseSideProperty}`);
|
||||
throw new Error(`Inverse side was not found in the relation ${this.entityMetadata.name}#${this.inverseSidePropertyPath}`);
|
||||
|
||||
return relation;
|
||||
}
|
||||
@ -511,6 +511,45 @@ export class RelationMetadata {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates entity id map from the given entity ids array.
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
createValueMap(value: any) {
|
||||
|
||||
// extract column value from embeds of entity if column is in embedded
|
||||
if (this.embeddedMetadata) {
|
||||
|
||||
// example: post[data][information][counters].id where "data", "information" and "counters" are embeddeds
|
||||
// we need to get value of "id" column from the post real entity object and return it in a
|
||||
// { data: { information: { counters: { id: ... } } } } format
|
||||
|
||||
// first step - we extract all parent properties of the entity relative to this column, e.g. [data, information, counters]
|
||||
const propertyNames = this.embeddedMetadata.parentPropertyNames;
|
||||
|
||||
// now need to access post[data][information][counters] to get column value from the counters
|
||||
// and on each step we need to create complex literal object, e.g. first { data },
|
||||
// then { data: { information } }, then { data: { information: { counters } } },
|
||||
// then { data: { information: { counters: [this.propertyName]: entity[data][information][counters][this.propertyName] } } }
|
||||
// this recursive function helps doing that
|
||||
const extractEmbeddedColumnValue = (propertyNames: string[], map: ObjectLiteral): any => {
|
||||
const propertyName = propertyNames.shift();
|
||||
if (propertyName) {
|
||||
map[propertyName] = {};
|
||||
extractEmbeddedColumnValue(propertyNames, map[propertyName]);
|
||||
return map;
|
||||
}
|
||||
map[this.propertyName] = value;
|
||||
return map;
|
||||
};
|
||||
return extractEmbeddedColumnValue(propertyNames, {});
|
||||
|
||||
} else { // no embeds - no problems. Simply return column property name and its value of the entity
|
||||
return { [this.propertyName]: value };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* todo: lazy relations are not supported here? implement logic?
|
||||
*
|
||||
|
||||
@ -394,12 +394,12 @@ export class Subject {
|
||||
// 1. related entity can be another entity which is natural way
|
||||
// 2. related entity can be entity id which is hacked way of updating entity
|
||||
// todo: what to do if there is a column with relationId? (cover this too?)
|
||||
const updatedEntityRelationId: any =
|
||||
this.entity[relation.propertyName] instanceof Object ?
|
||||
relation.inverseEntityMetadata.getEntityIdMixedMap(this.entity[relation.propertyName])
|
||||
: this.entity[relation.propertyName];
|
||||
const entityValue = relation.getEntityValue(this.entity);
|
||||
const updatedEntityRelationId: any = entityValue instanceof Object
|
||||
? relation.inverseEntityMetadata.getEntityIdMixedMap(entityValue)
|
||||
: entityValue;
|
||||
|
||||
const dbEntityRelationId = this.databaseEntity[relation.propertyName];
|
||||
const dbEntityRelationId = relation.getEntityValue(this.databaseEntity);
|
||||
|
||||
// todo: try to find if there is update by relation operation - we dont need to generate update relation operation for this
|
||||
// todo: if (updatesByRelations.find(operation => operation.targetEntity === this && operation.updatedRelation === relation))
|
||||
|
||||
@ -345,7 +345,7 @@ export class SubjectBuilder<Entity extends ObjectLiteral> {
|
||||
|
||||
// note that if databaseEntity has relation, it can only be a relation id,
|
||||
// because of query builder option "RELATION_ID_VALUES" we used
|
||||
const relationIdInDatabaseEntity = subject.databaseEntity[relation.propertyName]; // (example) returns post.detailsId
|
||||
const relationIdInDatabaseEntity = relation.getEntityValue(subject.databaseEntity); // (example) returns post.detailsId
|
||||
|
||||
// if database relation id does not exist in the database object then nothing to remove
|
||||
if (relationIdInDatabaseEntity === null || relationIdInDatabaseEntity === undefined)
|
||||
@ -356,7 +356,7 @@ export class SubjectBuilder<Entity extends ObjectLiteral> {
|
||||
if (subject.hasEntity) {
|
||||
const persistValue = relation.getEntityValue(subject.entity);
|
||||
if (persistValue === null) persistValueRelationId = null;
|
||||
if (persistValue) persistValueRelationId = persistValue[relation.joinColumns[0].referencedColumn.propertyName];
|
||||
if (persistValue) persistValueRelationId = relation.joinColumns[0].referencedColumn.getValue(persistValue);
|
||||
if (persistValueRelationId === undefined) return; // skip undefined properties
|
||||
}
|
||||
|
||||
@ -374,7 +374,7 @@ export class SubjectBuilder<Entity extends ObjectLiteral> {
|
||||
// (example) here we seek a Details loaded from the database in the subjects
|
||||
// (example) here relatedSubject.databaseEntity is a Details
|
||||
// (example) and we need to compare details.id === post.detailsId
|
||||
return relatedSubject.databaseEntity[relation.joinColumns[0].referencedColumn.propertyName] === relationIdInDatabaseEntity;
|
||||
return relation.joinColumns[0].referencedColumn.getValue(relatedSubject.databaseEntity) === relationIdInDatabaseEntity;
|
||||
});
|
||||
|
||||
// if not loaded yet then load it from the database
|
||||
@ -384,7 +384,7 @@ export class SubjectBuilder<Entity extends ObjectLiteral> {
|
||||
const databaseEntity = await this.connection
|
||||
.getRepository<ObjectLiteral>(valueMetadata.target)
|
||||
.createQueryBuilder(qbAlias, this.queryRunnerProvider) // todo: this wont work for mongodb. implement this in some method and call it here instead?
|
||||
.where(qbAlias + "." + relation.joinColumns[0].referencedColumn.propertyName + "=:id")
|
||||
.where(qbAlias + "." + relation.joinColumns[0].referencedColumn.propertyPath + "=:id")
|
||||
.setParameter("id", relationIdInDatabaseEntity) // (example) subject.entity is a post here
|
||||
.enableAutoRelationIdsLoad()
|
||||
.getOne();
|
||||
@ -461,7 +461,7 @@ export class SubjectBuilder<Entity extends ObjectLiteral> {
|
||||
const databaseEntity = await this.connection
|
||||
.getRepository<ObjectLiteral>(valueMetadata.target)
|
||||
.createQueryBuilder(qbAlias, this.queryRunnerProvider) // todo: this wont work for mongodb. implement this in some method and call it here instead?
|
||||
.where(qbAlias + "." + relation.inverseSideProperty + "=:id")
|
||||
.where(qbAlias + "." + relation.inverseSidePropertyPath + "=:id")
|
||||
.setParameter("id", relationIdInDatabaseEntity) // (example) subject.entity is a details here, and the value is details.id
|
||||
.enableAutoRelationIdsLoad()
|
||||
.getOne();
|
||||
@ -610,7 +610,7 @@ export class SubjectBuilder<Entity extends ObjectLiteral> {
|
||||
databaseEntities = await this.connection
|
||||
.getRepository<ObjectLiteral>(valueMetadata.target)
|
||||
.createQueryBuilder(qbAlias, this.queryRunnerProvider) // todo: this wont work for mongodb. implement this in some method and call it here instead?
|
||||
.where(qbAlias + "." + relation.inverseSideProperty + "=:id")
|
||||
.where(qbAlias + "." + relation.inverseSidePropertyPath + "=:id")
|
||||
.setParameter("id", relationIdInDatabaseEntity)
|
||||
.enableAutoRelationIdsLoad()
|
||||
.getMany();
|
||||
|
||||
@ -325,6 +325,7 @@ export class SubjectOperationExecutor {
|
||||
// we need to update relation ids if newly inserted objects are used from inverse side in one-to-many inverse relation
|
||||
// we also need to update relation ids if newly inserted objects are used from inverse side in one-to-one inverse relation
|
||||
const oneToManyAndOneToOneNonOwnerRelations = subject.metadata.oneToManyRelations.concat(subject.metadata.oneToOneRelations.filter(relation => !relation.isOwning));
|
||||
// console.log(oneToManyAndOneToOneNonOwnerRelations);
|
||||
subject.metadata.extractRelationValuesFromEntity(subject.entity, oneToManyAndOneToOneNonOwnerRelations)
|
||||
.forEach(([relation, subRelatedEntity, inverseEntityMetadata]) => {
|
||||
relation.inverseRelation.joinColumns.forEach(joinColumn => {
|
||||
@ -723,7 +724,7 @@ export class SubjectOperationExecutor {
|
||||
throw new Error(`Internal error. Cannot get id of the updating entity.`);
|
||||
|
||||
|
||||
const addEmbeddedValuesRecursively = (entity: any, value: any, embeddeds: EmbeddedMetadata[]) => {
|
||||
/*const addEmbeddedValuesRecursively = (entity: any, value: any, embeddeds: EmbeddedMetadata[]) => {
|
||||
embeddeds.forEach(embedded => {
|
||||
if (!entity[embedded.propertyName])
|
||||
return;
|
||||
@ -746,14 +747,14 @@ export class SubjectOperationExecutor {
|
||||
}
|
||||
addEmbeddedValuesRecursively(entity[embedded.propertyName], value[embedded.prefix], embedded.embeddeds);
|
||||
});
|
||||
};
|
||||
};*/
|
||||
|
||||
const value: ObjectLiteral = {};
|
||||
subject.metadata.columnsWithoutEmbeddeds.forEach(column => {
|
||||
subject.metadata.columns.forEach(column => {
|
||||
if (entity[column.propertyName] !== undefined)
|
||||
value[column.fullName] = entity[column.propertyName];
|
||||
value[column.fullName] = column.getValue(entity);
|
||||
});
|
||||
addEmbeddedValuesRecursively(entity, value, subject.metadata.embeddeds);
|
||||
// addEmbeddedValuesRecursively(entity, value, subject.metadata.embeddeds);
|
||||
|
||||
// if number of updated columns = 0 no need to update updated date and version columns
|
||||
if (Object.keys(value).length === 0)
|
||||
@ -787,10 +788,9 @@ export class SubjectOperationExecutor {
|
||||
});
|
||||
|
||||
subject.diffRelations.forEach(relation => {
|
||||
const metadata = this.connection.getMetadata(relation.entityTarget);
|
||||
let valueMap = valueMaps.find(valueMap => valueMap.tableName === metadata.tableName);
|
||||
let valueMap = valueMaps.find(valueMap => valueMap.tableName === relation.entityMetadata.tableName);
|
||||
if (!valueMap) {
|
||||
valueMap = { tableName: metadata.tableName, metadata: metadata, values: {} };
|
||||
valueMap = { tableName: relation.entityMetadata.tableName, metadata: relation.entityMetadata, values: {} };
|
||||
valueMaps.push(valueMap);
|
||||
}
|
||||
|
||||
@ -1019,8 +1019,9 @@ export class SubjectOperationExecutor {
|
||||
const junctionMetadata = junctionRemove.relation.junctionEntityMetadata;
|
||||
const entity = subject.hasEntity ? subject.entity : subject.databaseEntity;
|
||||
|
||||
const firstJoinColumns = junctionRemove.relation.isOwning ? junctionRemove.relation.joinColumns : junctionRemove.relation.inverseJoinColumns;
|
||||
const secondJoinColumns = junctionRemove.relation.isOwning ? junctionRemove.relation.inverseJoinColumns : junctionRemove.relation.joinColumns;
|
||||
console.log(junctionRemove);
|
||||
const firstJoinColumns = junctionRemove.relation.isOwning ? junctionRemove.relation.joinColumns : junctionRemove.relation.inverseRelation.inverseJoinColumns;
|
||||
const secondJoinColumns = junctionRemove.relation.isOwning ? junctionRemove.relation.inverseJoinColumns : junctionRemove.relation.inverseRelation.joinColumns;
|
||||
let conditions: ObjectLiteral = {};
|
||||
firstJoinColumns.forEach(joinColumn => {
|
||||
conditions[joinColumn.fullName] = joinColumn.referencedColumn.getValue(entity);
|
||||
|
||||
@ -1402,20 +1402,20 @@ export class QueryBuilder<Entity> {
|
||||
protected replacePropertyNames(statement: string) {
|
||||
this.expressionMap.aliases.forEach(alias => {
|
||||
if (!alias.hasMetadata) return;
|
||||
alias.metadata.embeddeds.forEach(embedded => {
|
||||
embedded.columns.forEach(column => {
|
||||
const expression = "([ =]|^.{0})" + alias.name + "\\." + embedded.propertyName + "\\." + column.propertyName + "([ =]|.{0}$)";
|
||||
statement = statement.replace(new RegExp(expression, "gm"), "$1" + this.escapeAlias(alias.name) + "." + this.escapeColumn(column.fullName) + "$2");
|
||||
});
|
||||
// todo: what about embedded relations here?
|
||||
});
|
||||
alias.metadata.columns.filter(column => !column.isInEmbedded).forEach(column => {
|
||||
const expression = "([ =\(]|^.{0})" + alias.name + "\\." + column.propertyName + "([ =]|.{0}$)";
|
||||
// alias.metadata.embeddeds.forEach(embedded => {
|
||||
// embedded.columns.forEach(column => {
|
||||
// const expression = "([ =]|^.{0})" + alias.name + "\\." + embedded.propertyName + "\\." + column.propertyName + "([ =]|.{0}$)";
|
||||
// statement = statement.replace(new RegExp(expression, "gm"), "$1" + this.escapeAlias(alias.name) + "." + this.escapeColumn(column.fullName) + "$2");
|
||||
// });
|
||||
// todo: what about embedded relations here?
|
||||
// });
|
||||
alias.metadata.columns.forEach(column => {
|
||||
const expression = "([ =\(]|^.{0})" + alias.name + "\\." + column.propertyPath + "([ =]|.{0}$)";
|
||||
statement = statement.replace(new RegExp(expression, "gm"), "$1" + this.escapeAlias(alias.name) + "." + this.escapeColumn(column.fullName) + "$2");
|
||||
});
|
||||
alias.metadata.relationsWithJoinColumns/*.filter(relation => !relation.isInEmbedded)*/.forEach(relation => {
|
||||
const expression = "([ =\(]|^.{0})" + alias.name + "\\." + relation.propertyName + "([ =]|.{0}$)";
|
||||
statement = statement.replace(new RegExp(expression, "gm"), "$1" + this.escapeAlias(alias.name) + "." + this.escapeColumn(relation.name) + "$2");
|
||||
const expression = "([ =\(]|^.{0})" + alias.name + "\\." + relation.propertyPath + "([ =]|.{0}$)";
|
||||
statement = statement.replace(new RegExp(expression, "gm"), "$1" + this.escapeAlias(alias.name) + "." + this.escapeColumn(relation.joinColumns[0].fullName) + "$2"); // todo: fix relation.joinColumns[0], what if multiple columns
|
||||
});
|
||||
});
|
||||
return statement;
|
||||
|
||||
@ -59,7 +59,7 @@ export class RelationIdLoader {
|
||||
const inverseSideTable = relation.inverseEntityMetadata.target; // Post
|
||||
const inverseSideTableName = relation.inverseEntityMetadata.tableName; // post
|
||||
const inverseSideTableAlias = relationIdAttr.alias || inverseSideTableName; // if condition (custom query builder factory) is set then relationIdAttr.alias defined
|
||||
const inverseSidePropertyName = inverseRelation.propertyName; // "category" from "post.category"
|
||||
const inverseSidePropertyName = inverseRelation.propertyPath; // "category" from "post.category"
|
||||
|
||||
const referenceColumnValues = rawEntities
|
||||
.map(rawEntity => rawEntity[relationIdAttr.parentAlias + "_" + referenceColumnName])
|
||||
@ -165,12 +165,12 @@ export class RelationIdLoader {
|
||||
const inverseSideTableName = relationIdAttr.joinInverseSideMetadata.tableName;
|
||||
const inverseSideTableAlias = relationIdAttr.alias || inverseSideTableName;
|
||||
const junctionTableName = relationIdAttr.relation.junctionEntityMetadata.tableName;
|
||||
const condition = junctionAlias + "." + firstJunctionColumn.propertyName + " IN (" + referenceColumnValues + ")" +
|
||||
" AND " + junctionAlias + "." + secondJunctionColumn.propertyName + " = " + inverseSideTableAlias + "." + inverseJoinColumnName;
|
||||
const condition = junctionAlias + "." + firstJunctionColumn.propertyPath + " IN (" + referenceColumnValues + ")" +
|
||||
" AND " + junctionAlias + "." + secondJunctionColumn.propertyPath + " = " + inverseSideTableAlias + "." + inverseJoinColumnName;
|
||||
|
||||
const qb = new QueryBuilder(this.connection, this.queryRunnerProvider)
|
||||
.select(inverseSideTableAlias + "." + inverseJoinColumnName, "id")
|
||||
.addSelect(junctionAlias + "." + firstJunctionColumn.propertyName, "manyToManyId")
|
||||
.addSelect(junctionAlias + "." + firstJunctionColumn.propertyPath, "manyToManyId")
|
||||
.fromTable(inverseSideTableName, inverseSideTableAlias)
|
||||
.innerJoin(junctionTableName, junctionAlias, condition);
|
||||
|
||||
|
||||
@ -46,12 +46,9 @@ export class SpecificRepository<Entity extends ObjectLiteral> {
|
||||
* Should be used when you want quickly and efficiently set a relation (for many-to-one and one-to-many) to some entity.
|
||||
* Note that event listeners and event subscribers won't work (and will not send any events) when using this operation.
|
||||
*/
|
||||
async setRelation(relationName: string|((t: Entity) => string|any), entityId: any, relatedEntityId: any): Promise<void> {
|
||||
const propertyName = this.metadata.computePropertyName(relationName);
|
||||
if (!this.metadata.hasRelationWithPropertyName(propertyName))
|
||||
throw new Error(`Relation ${propertyName} was not found in the ${this.metadata.name} entity.`);
|
||||
|
||||
const relation = this.metadata.findRelationWithPropertyName(propertyName);
|
||||
async setRelation(relationProperty: string|((t: Entity) => string|any), entityId: any, relatedEntityId: any): Promise<void> {
|
||||
const propertyPath = this.metadata.computePropertyPath(relationProperty);
|
||||
const relation = this.metadata.findRelationWithPropertyPath(propertyPath);
|
||||
// if (relation.isManyToMany || relation.isOneToMany || relation.isOneToOneNotOwner)
|
||||
// throw new Error(`Only many-to-one and one-to-one with join column are supported for this operation. ${this.metadata.name}#${propertyName} relation type is ${relation.relationType}`);
|
||||
if (relation.isManyToMany)
|
||||
@ -97,14 +94,10 @@ export class SpecificRepository<Entity extends ObjectLiteral> {
|
||||
* Should be used when you want quickly and efficiently set a relation (for many-to-one and one-to-many) to some entity.
|
||||
* Note that event listeners and event subscribers won't work (and will not send any events) when using this operation.
|
||||
*/
|
||||
async setInverseRelation(relationName: string|((t: Entity) => string|any), relatedEntityId: any, entityId: any): Promise<void> {
|
||||
const propertyName = this.metadata.computePropertyName(relationName);
|
||||
if (!this.metadata.hasRelationWithPropertyName(propertyName))
|
||||
throw new Error(`Relation ${propertyName} was not found in the ${this.metadata.name} entity.`);
|
||||
|
||||
async setInverseRelation(relationProperty: string|((t: Entity) => string|any), relatedEntityId: any, entityId: any): Promise<void> {
|
||||
const propertyPath = this.metadata.computePropertyPath(relationProperty);
|
||||
// todo: fix issues with joinColumns[0]
|
||||
|
||||
const relation = this.metadata.findRelationWithPropertyName(propertyName);
|
||||
const relation = this.metadata.findRelationWithPropertyPath(propertyPath);
|
||||
// if (relation.isManyToMany || relation.isOneToMany || relation.isOneToOneNotOwner)
|
||||
// throw new Error(`Only many-to-one and one-to-one with join column are supported for this operation. ${this.metadata.name}#${propertyName} relation type is ${relation.relationType}`);
|
||||
if (relation.isManyToMany)
|
||||
@ -147,14 +140,11 @@ export class SpecificRepository<Entity extends ObjectLiteral> {
|
||||
* Should be used when you want quickly and efficiently add a relation between two entities.
|
||||
* Note that event listeners and event subscribers won't work (and will not send any events) when using this operation.
|
||||
*/
|
||||
async addToRelation(relationName: string|((t: Entity) => string|any), entityId: any, relatedEntityIds: any[]): Promise<void> {
|
||||
const propertyName = this.metadata.computePropertyName(relationName);
|
||||
if (!this.metadata.hasRelationWithPropertyName(propertyName))
|
||||
throw new Error(`Relation ${propertyName} was not found in the ${this.metadata.name} entity.`);
|
||||
|
||||
const relation = this.metadata.findRelationWithPropertyName(propertyName);
|
||||
async addToRelation(relationProperty: string|((t: Entity) => string|any), entityId: any, relatedEntityIds: any[]): Promise<void> {
|
||||
const propertyPath = this.metadata.computePropertyPath(relationProperty);
|
||||
const relation = this.metadata.findRelationWithPropertyPath(propertyPath);
|
||||
if (!relation.isManyToMany)
|
||||
throw new Error(`Only many-to-many relation supported for this operation. However ${this.metadata.name}#${propertyName} relation type is ${relation.relationType}`);
|
||||
throw new Error(`Only many-to-many relation supported for this operation. However ${this.metadata.name}#${propertyPath} relation type is ${relation.relationType}`);
|
||||
|
||||
const queryRunnerProvider = this.queryRunnerProvider ? this.queryRunnerProvider : new QueryRunnerProvider(this.connection.driver);
|
||||
const queryRunner = await queryRunnerProvider.provide();
|
||||
@ -195,15 +185,11 @@ export class SpecificRepository<Entity extends ObjectLiteral> {
|
||||
* Should be used when you want quickly and efficiently add a relation between two entities.
|
||||
* Note that event listeners and event subscribers won't work (and will not send any events) when using this operation.
|
||||
*/
|
||||
async addToInverseRelation(relationName: string|((t: Entity) => string|any), relatedEntityId: any, entityIds: any[]): Promise<void> {
|
||||
const propertyName = this.metadata.computePropertyName(relationName);
|
||||
if (!this.metadata.hasRelationWithPropertyName(propertyName))
|
||||
throw new Error(`Relation ${propertyName} was not found in the ${this.metadata.name} entity.`);
|
||||
|
||||
const relation = this.metadata.findRelationWithPropertyName(propertyName);
|
||||
async addToInverseRelation(relationProperty: string|((t: Entity) => string|any), relatedEntityId: any, entityIds: any[]): Promise<void> {
|
||||
const propertyPath = this.metadata.computePropertyPath(relationProperty);
|
||||
const relation = this.metadata.findRelationWithPropertyName(propertyPath);
|
||||
if (!relation.isManyToMany)
|
||||
throw new Error(`Only many-to-many relation supported for this operation. However ${this.metadata.name}#${propertyName} relation type is ${relation.relationType}`);
|
||||
|
||||
throw new Error(`Only many-to-many relation supported for this operation. However ${this.metadata.name}#${propertyPath} relation type is ${relation.relationType}`);
|
||||
|
||||
const queryRunnerProvider = this.queryRunnerProvider ? this.queryRunnerProvider : new QueryRunnerProvider(this.connection.driver);
|
||||
const queryRunner = await queryRunnerProvider.provide();
|
||||
@ -247,14 +233,11 @@ export class SpecificRepository<Entity extends ObjectLiteral> {
|
||||
* Should be used when you want quickly and efficiently remove a many-to-many relation between two entities.
|
||||
* Note that event listeners and event subscribers won't work (and will not send any events) when using this operation.
|
||||
*/
|
||||
async removeFromRelation(relationName: string|((t: Entity) => string|any), entityId: any, relatedEntityIds: any[]): Promise<void> {
|
||||
const propertyName = this.metadata.computePropertyName(relationName);
|
||||
if (!this.metadata.hasRelationWithPropertyName(propertyName))
|
||||
throw new Error(`Relation ${propertyName} was not found in the ${this.metadata.name} entity.`);
|
||||
|
||||
const relation = this.metadata.findRelationWithPropertyName(propertyName);
|
||||
async removeFromRelation(relationProperty: string|((t: Entity) => string|any), entityId: any, relatedEntityIds: any[]): Promise<void> {
|
||||
const propertyPath = this.metadata.computePropertyPath(relationProperty);
|
||||
const relation = this.metadata.findRelationWithPropertyPath(propertyPath);
|
||||
if (!relation.isManyToMany)
|
||||
throw new Error(`Only many-to-many relation supported for this operation. However ${this.metadata.name}#${propertyName} relation type is ${relation.relationType}`);
|
||||
throw new Error(`Only many-to-many relation supported for this operation. However ${this.metadata.name}#${propertyPath} relation type is ${relation.relationType}`);
|
||||
|
||||
// check if given relation entity ids is empty - then nothing to do here (otherwise next code will remove all ids)
|
||||
if (!relatedEntityIds || !relatedEntityIds.length)
|
||||
@ -296,14 +279,11 @@ export class SpecificRepository<Entity extends ObjectLiteral> {
|
||||
* Should be used when you want quickly and efficiently remove a many-to-many relation between two entities.
|
||||
* Note that event listeners and event subscribers won't work (and will not send any events) when using this operation.
|
||||
*/
|
||||
async removeFromInverseRelation(relationName: string|((t: Entity) => string|any), relatedEntityId: any, entityIds: any[]): Promise<void> {
|
||||
const propertyName = this.metadata.computePropertyName(relationName);
|
||||
if (!this.metadata.hasRelationWithPropertyName(propertyName))
|
||||
throw new Error(`Relation ${propertyName} was not found in the ${this.metadata.name} entity.`);
|
||||
|
||||
const relation = this.metadata.findRelationWithPropertyName(propertyName);
|
||||
async removeFromInverseRelation(relationProperty: string|((t: Entity) => string|any), relatedEntityId: any, entityIds: any[]): Promise<void> {
|
||||
const propertyPath = this.metadata.computePropertyPath(relationProperty);
|
||||
const relation = this.metadata.findRelationWithPropertyName(propertyPath);
|
||||
if (!relation.isManyToMany)
|
||||
throw new Error(`Only many-to-many relation supported for this operation. However ${this.metadata.name}#${propertyName} relation type is ${relation.relationType}`);
|
||||
throw new Error(`Only many-to-many relation supported for this operation. However ${this.metadata.name}#${propertyPath} relation type is ${relation.relationType}`);
|
||||
|
||||
// check if given entity ids is empty - then nothing to do here (otherwise next code will remove all ids)
|
||||
if (!entityIds || !entityIds.length)
|
||||
@ -526,8 +506,8 @@ export class SpecificRepository<Entity extends ObjectLiteral> {
|
||||
if (relationOrName instanceof RelationMetadata)
|
||||
return relationOrName;
|
||||
|
||||
const relationName = relationOrName instanceof Function ? relationOrName(this.metadata.createPropertiesMap()) : relationOrName;
|
||||
return this.metadata.findRelationWithPropertyName(relationName);
|
||||
const relationPropertyPath = relationOrName instanceof Function ? relationOrName(this.metadata.createPropertiesMap()) : relationOrName;
|
||||
return this.metadata.findRelationWithPropertyPath(relationPropertyPath);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -18,177 +18,399 @@ describe("embedded > embedded-many-to-many", () => {
|
||||
beforeEach(() => reloadTestingDatabases(connections));
|
||||
after(() => closeTestingConnections(connections));
|
||||
|
||||
it("should insert, load, update and remove entities with embeddeds when primary column defined only in embedded entity", () => Promise.all(connections.map(async connection => {
|
||||
describe("owner side", () => {
|
||||
|
||||
const user1 = new User();
|
||||
user1.name = "Alice";
|
||||
await connection.getRepository(User).persist(user1);
|
||||
it("should insert, load, update and remove entities with embeddeds when embedded entity having ManyToMany relation", () => Promise.all(connections.map(async connection => {
|
||||
|
||||
const user2 = new User();
|
||||
user2.name = "Bob";
|
||||
await connection.getRepository(User).persist(user2);
|
||||
const user1 = new User();
|
||||
user1.name = "Alice";
|
||||
await connection.getRepository(User).persist(user1);
|
||||
|
||||
const user3 = new User();
|
||||
user3.name = "Clara";
|
||||
await connection.getRepository(User).persist(user3);
|
||||
const user2 = new User();
|
||||
user2.name = "Bob";
|
||||
await connection.getRepository(User).persist(user2);
|
||||
|
||||
const postRepository = connection.getRepository(Post);
|
||||
const user3 = new User();
|
||||
user3.name = "Clara";
|
||||
await connection.getRepository(User).persist(user3);
|
||||
|
||||
const post1 = new Post();
|
||||
post1.title = "About cars";
|
||||
post1.counters = new Counters();
|
||||
post1.counters.code = 1;
|
||||
post1.counters.comments = 1;
|
||||
post1.counters.favorites = 2;
|
||||
post1.counters.likes = 3;
|
||||
post1.counters.likedUsers = [user1, user2];
|
||||
post1.counters.subcounters = new Subcounters();
|
||||
post1.counters.subcounters.version = 1;
|
||||
post1.counters.subcounters.watches = 5;
|
||||
await postRepository.persist(post1);
|
||||
const postRepository = connection.getRepository(Post);
|
||||
|
||||
const post2 = new Post();
|
||||
post2.title = "About airplanes";
|
||||
post2.counters = new Counters();
|
||||
post2.counters.code = 2;
|
||||
post2.counters.comments = 2;
|
||||
post2.counters.favorites = 3;
|
||||
post2.counters.likes = 4;
|
||||
post2.counters.likedUsers = [user3];
|
||||
post2.counters.subcounters = new Subcounters();
|
||||
post2.counters.subcounters.version = 1;
|
||||
post2.counters.subcounters.watches = 10;
|
||||
await postRepository.persist(post2);
|
||||
const post1 = new Post();
|
||||
post1.title = "About cars";
|
||||
post1.counters = new Counters();
|
||||
post1.counters.code = 1;
|
||||
post1.counters.comments = 1;
|
||||
post1.counters.favorites = 2;
|
||||
post1.counters.likes = 3;
|
||||
post1.counters.likedUsers = [user1, user2];
|
||||
post1.counters.subcounters = new Subcounters();
|
||||
post1.counters.subcounters.version = 1;
|
||||
post1.counters.subcounters.watches = 5;
|
||||
await postRepository.persist(post1);
|
||||
|
||||
const loadedPosts = await connection.entityManager
|
||||
.createQueryBuilder(Post, "post")
|
||||
.leftJoinAndSelect("post.counters.likedUsers", "likedUser")
|
||||
.orderBy("post.id, likedUser.id")
|
||||
.getMany();
|
||||
const post2 = new Post();
|
||||
post2.title = "About airplanes";
|
||||
post2.counters = new Counters();
|
||||
post2.counters.code = 2;
|
||||
post2.counters.comments = 2;
|
||||
post2.counters.favorites = 3;
|
||||
post2.counters.likes = 4;
|
||||
post2.counters.likedUsers = [user3];
|
||||
post2.counters.subcounters = new Subcounters();
|
||||
post2.counters.subcounters.version = 1;
|
||||
post2.counters.subcounters.watches = 10;
|
||||
await postRepository.persist(post2);
|
||||
|
||||
expect(loadedPosts[0].should.be.eql(
|
||||
{
|
||||
id: 1,
|
||||
title: "About cars",
|
||||
counters: {
|
||||
code: 1,
|
||||
comments: 1,
|
||||
favorites: 2,
|
||||
likes: 3,
|
||||
likedUsers: [
|
||||
const loadedPosts = await connection.entityManager
|
||||
.createQueryBuilder(Post, "post")
|
||||
.leftJoinAndSelect("post.counters.likedUsers", "likedUser")
|
||||
.orderBy("post.id")
|
||||
.addOrderBy("likedUser.id")
|
||||
.getMany();
|
||||
|
||||
expect(loadedPosts[0].should.be.eql(
|
||||
{
|
||||
id: 1,
|
||||
title: "About cars",
|
||||
counters: {
|
||||
code: 1,
|
||||
comments: 1,
|
||||
favorites: 2,
|
||||
likes: 3,
|
||||
likedUsers: [
|
||||
{
|
||||
id: 1,
|
||||
name: "Alice"
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "Bob"
|
||||
}
|
||||
],
|
||||
subcounters: {
|
||||
version: 1,
|
||||
watches: 5
|
||||
}
|
||||
}
|
||||
}
|
||||
));
|
||||
expect(loadedPosts[1].should.be.eql(
|
||||
{
|
||||
id: 2,
|
||||
title: "About airplanes",
|
||||
counters: {
|
||||
code: 2,
|
||||
comments: 2,
|
||||
favorites: 3,
|
||||
likes: 4,
|
||||
likedUsers: [
|
||||
{
|
||||
id: 3,
|
||||
name: "Clara"
|
||||
}
|
||||
],
|
||||
subcounters: {
|
||||
version: 1,
|
||||
watches: 10
|
||||
}
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
const loadedPost = await connection.entityManager
|
||||
.createQueryBuilder(Post, "post")
|
||||
.leftJoinAndSelect("post.counters.likedUsers", "likedUser")
|
||||
.orderBy("likedUser.id")
|
||||
.where("post.id = :id", {id: 1})
|
||||
.getOne();
|
||||
|
||||
expect(loadedPost!.should.be.eql(
|
||||
{
|
||||
id: 1,
|
||||
title: "About cars",
|
||||
counters: {
|
||||
code: 1,
|
||||
comments: 1,
|
||||
favorites: 2,
|
||||
likes: 3,
|
||||
likedUsers: [
|
||||
{
|
||||
id: 1,
|
||||
name: "Alice"
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "Bob"
|
||||
}
|
||||
],
|
||||
subcounters: {
|
||||
version: 1,
|
||||
watches: 5
|
||||
}
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
loadedPost!.counters.favorites += 1;
|
||||
loadedPost!.counters.subcounters.watches += 1;
|
||||
loadedPost!.counters.likedUsers = [user1];
|
||||
await postRepository.persist(loadedPost!);
|
||||
|
||||
const loadedPost2 = await connection.entityManager
|
||||
.createQueryBuilder(Post, "post")
|
||||
.leftJoinAndSelect("post.counters.likedUsers", "likedUser")
|
||||
.orderBy("likedUser.id")
|
||||
.where("post.id = :id", {id: 1})
|
||||
.getOne();
|
||||
|
||||
expect(loadedPost2!.should.be.eql(
|
||||
{
|
||||
id: 1,
|
||||
title: "About cars",
|
||||
counters: {
|
||||
code: 1,
|
||||
comments: 1,
|
||||
favorites: 3,
|
||||
likes: 3,
|
||||
likedUsers: [
|
||||
{
|
||||
id: 1,
|
||||
name: "Alice"
|
||||
}
|
||||
],
|
||||
subcounters: {
|
||||
version: 1,
|
||||
watches: 6
|
||||
}
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
await postRepository.remove(loadedPost2!);
|
||||
|
||||
const loadedPosts2 = (await postRepository.find())!;
|
||||
expect(loadedPosts2.length).to.be.equal(1);
|
||||
expect(loadedPosts2[0].title).to.be.equal("About airplanes");
|
||||
})));
|
||||
});
|
||||
|
||||
describe("inverse side", () => {
|
||||
|
||||
it("should insert, load, update and remove entities with embeddeds when embedded entity having ManyToMany relation", () => Promise.all(connections.map(async connection => {
|
||||
|
||||
const post1 = new Post();
|
||||
post1.title = "About cars";
|
||||
post1.counters = new Counters();
|
||||
post1.counters.code = 1;
|
||||
post1.counters.comments = 1;
|
||||
post1.counters.favorites = 2;
|
||||
post1.counters.likes = 3;
|
||||
post1.counters.subcounters = new Subcounters();
|
||||
post1.counters.subcounters.version = 1;
|
||||
post1.counters.subcounters.watches = 5;
|
||||
await connection.getRepository(Post).persist(post1);
|
||||
|
||||
const post2 = new Post();
|
||||
post2.title = "About airplanes";
|
||||
post2.counters = new Counters();
|
||||
post2.counters.code = 2;
|
||||
post2.counters.comments = 2;
|
||||
post2.counters.favorites = 3;
|
||||
post2.counters.likes = 4;
|
||||
post2.counters.subcounters = new Subcounters();
|
||||
post2.counters.subcounters.version = 1;
|
||||
post2.counters.subcounters.watches = 10;
|
||||
await connection.getRepository(Post).persist(post2);
|
||||
|
||||
const user1 = new User();
|
||||
user1.name = "Alice";
|
||||
user1.likedPosts = [post1, post2];
|
||||
await connection.getRepository(User).persist(user1);
|
||||
|
||||
const user2 = new User();
|
||||
user2.name = "Bob";
|
||||
user2.likedPosts = [post1];
|
||||
await connection.getRepository(User).persist(user2);
|
||||
|
||||
const user3 = new User();
|
||||
user3.name = "Clara";
|
||||
user3.likedPosts = [post2];
|
||||
await connection.getRepository(User).persist(user3);
|
||||
|
||||
const loadedUsers = await connection.entityManager
|
||||
.createQueryBuilder(User, "user")
|
||||
.leftJoinAndSelect("user.likedPosts", "likedPost")
|
||||
.orderBy("user.id")
|
||||
.addOrderBy("likedPost.id")
|
||||
.getMany();
|
||||
|
||||
expect(loadedUsers[0].should.be.eql(
|
||||
{
|
||||
id: 1,
|
||||
name: "Alice",
|
||||
likedPosts: [
|
||||
{
|
||||
id: 1,
|
||||
name: "Alice"
|
||||
title: "About cars",
|
||||
counters: {
|
||||
code: 1,
|
||||
comments: 1,
|
||||
favorites: 2,
|
||||
likes: 3,
|
||||
subcounters: {
|
||||
version: 1,
|
||||
watches: 5
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "Bob"
|
||||
title: "About airplanes",
|
||||
counters: {
|
||||
code: 2,
|
||||
comments: 2,
|
||||
favorites: 3,
|
||||
likes: 4,
|
||||
subcounters: {
|
||||
version: 1,
|
||||
watches: 10
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
subcounters: {
|
||||
version: 1,
|
||||
watches: 5
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
));
|
||||
expect(loadedPosts[1].should.be.eql(
|
||||
{
|
||||
id: 2,
|
||||
title: "About airplanes",
|
||||
counters: {
|
||||
code: 2,
|
||||
comments: 2,
|
||||
favorites: 3,
|
||||
likes: 4,
|
||||
likedUsers: [
|
||||
{
|
||||
id: 3,
|
||||
name: "Clara"
|
||||
}
|
||||
],
|
||||
subcounters: {
|
||||
version: 1,
|
||||
watches: 10
|
||||
}
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
const loadedPost = await connection.entityManager
|
||||
.createQueryBuilder(Post, "post")
|
||||
.leftJoinAndSelect("post.counters.likedUsers", "likedUser")
|
||||
.orderBy("likedUser.id")
|
||||
.where("post.id = :id", { id: 1 })
|
||||
.getOne();
|
||||
|
||||
expect(loadedPost!.should.be.eql(
|
||||
{
|
||||
id: 1,
|
||||
title: "About cars",
|
||||
counters: {
|
||||
code: 1,
|
||||
comments: 1,
|
||||
favorites: 2,
|
||||
likes: 3,
|
||||
likedUsers: [
|
||||
));
|
||||
expect(loadedUsers[1].should.be.eql(
|
||||
{
|
||||
id: 2,
|
||||
name: "Bob",
|
||||
likedPosts: [
|
||||
{
|
||||
id: 1,
|
||||
name: "Alice"
|
||||
title: "About cars",
|
||||
counters: {
|
||||
code: 1,
|
||||
comments: 1,
|
||||
favorites: 2,
|
||||
likes: 3,
|
||||
subcounters: {
|
||||
version: 1,
|
||||
watches: 5
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
));
|
||||
expect(loadedUsers[2].should.be.eql(
|
||||
{
|
||||
id: 3,
|
||||
name: "Clara",
|
||||
likedPosts: [
|
||||
{
|
||||
id: 2,
|
||||
title: "About airplanes",
|
||||
counters: {
|
||||
code: 2,
|
||||
comments: 2,
|
||||
favorites: 3,
|
||||
likes: 4,
|
||||
subcounters: {
|
||||
version: 1,
|
||||
watches: 10
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
));
|
||||
|
||||
const loadedUser = await connection.entityManager
|
||||
.createQueryBuilder(User, "user")
|
||||
.leftJoinAndSelect("user.likedPosts", "likedPost")
|
||||
.orderBy("likedPost.id")
|
||||
.where("user.id = :id", {id: 1})
|
||||
.getOne();
|
||||
|
||||
expect(loadedUser!.should.be.eql(
|
||||
{
|
||||
id: 1,
|
||||
name: "Alice",
|
||||
likedPosts: [
|
||||
{
|
||||
id: 1,
|
||||
title: "About cars",
|
||||
counters: {
|
||||
code: 1,
|
||||
comments: 1,
|
||||
favorites: 2,
|
||||
likes: 3,
|
||||
subcounters: {
|
||||
version: 1,
|
||||
watches: 5
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "Bob"
|
||||
title: "About airplanes",
|
||||
counters: {
|
||||
code: 2,
|
||||
comments: 2,
|
||||
favorites: 3,
|
||||
likes: 4,
|
||||
subcounters: {
|
||||
version: 1,
|
||||
watches: 10
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
subcounters: {
|
||||
version: 1,
|
||||
watches: 5
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
));
|
||||
));
|
||||
|
||||
loadedPost!.counters.favorites += 1;
|
||||
loadedPost!.counters.subcounters.watches += 1;
|
||||
loadedPost!.counters.likedUsers = [user1];
|
||||
await postRepository.persist(loadedPost!);
|
||||
loadedUser!.name = "Anna";
|
||||
loadedUser!.likedPosts = [post1];
|
||||
await connection.getRepository(User).persist(loadedUser!);
|
||||
|
||||
const loadedPost2 = await connection.entityManager
|
||||
.createQueryBuilder(Post, "post")
|
||||
.leftJoinAndSelect("post.counters.likedUsers", "likedUser")
|
||||
.orderBy("likedUser.id")
|
||||
.where("post.id = :id", { id: 1 })
|
||||
.getOne();
|
||||
const loadedUser2 = await connection.entityManager
|
||||
.createQueryBuilder(User, "user")
|
||||
.leftJoinAndSelect("user.likedPosts", "likedPost")
|
||||
.orderBy("likedPost.id")
|
||||
.where("user.id = :id", {id: 1})
|
||||
.getOne();
|
||||
|
||||
expect(loadedPost2!.should.be.eql(
|
||||
{
|
||||
id: 1,
|
||||
title: "About cars",
|
||||
counters: {
|
||||
code: 1,
|
||||
comments: 1,
|
||||
favorites: 3,
|
||||
likes: 3,
|
||||
likedUsers: [
|
||||
expect(loadedUser2!.should.be.eql(
|
||||
{
|
||||
id: 1,
|
||||
name: "Anna",
|
||||
likedPosts: [
|
||||
{
|
||||
id: 1,
|
||||
name: "Alice"
|
||||
title: "About cars",
|
||||
counters: {
|
||||
code: 1,
|
||||
comments: 1,
|
||||
favorites: 2,
|
||||
likes: 3,
|
||||
subcounters: {
|
||||
version: 1,
|
||||
watches: 5
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
subcounters: {
|
||||
version: 1,
|
||||
watches: 6
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
));
|
||||
));
|
||||
|
||||
await postRepository.remove(loadedPost2!);
|
||||
await connection.getRepository(User).remove(loadedUser2!);
|
||||
|
||||
const loadedPosts2 = (await postRepository.find())!;
|
||||
expect(loadedPosts2.length).to.be.equal(1);
|
||||
expect(loadedPosts2[0].title).to.be.equal("About airplanes");
|
||||
})));
|
||||
const loadedUsers2 = (await connection.getRepository(User).find())!;
|
||||
expect(loadedUsers2.length).to.be.equal(2);
|
||||
expect(loadedUsers2[0].name).to.be.equal("Bob");
|
||||
expect(loadedUsers2[1].name).to.be.equal("Clara");
|
||||
})));
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import {EmbeddableEntity} from "../../../../../src/decorator/entity/EmbeddableEntity";
|
||||
import {Column} from "../../../../../src/decorator/columns/Column";
|
||||
import {Embedded} from "../../../../../src/decorator/Embedded";
|
||||
import {Subcounters} from "./Subcounters";
|
||||
import {User} from "./User";
|
||||
import {ManyToMany} from "../../../../../src/decorator/relations/ManyToMany";
|
||||
import {JoinTable} from "../../../../../src/decorator/relations/JoinTable";
|
||||
import {Subcounters} from "./Subcounters";
|
||||
import {User} from "./User";
|
||||
|
||||
@EmbeddableEntity()
|
||||
export class Counters {
|
||||
@ -24,7 +24,7 @@ export class Counters {
|
||||
@Embedded(() => Subcounters)
|
||||
subcounters: Subcounters;
|
||||
|
||||
@ManyToMany(type => User)
|
||||
@ManyToMany(type => User, user => user.likedPosts)
|
||||
@JoinTable()
|
||||
likedUsers: User[];
|
||||
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
import {Column} from "../../../../../src/decorator/columns/Column";
|
||||
import {PrimaryGeneratedColumn} from "../../../../../src/decorator/columns/PrimaryGeneratedColumn";
|
||||
import {Entity} from "../../../../../src/decorator/entity/Entity";
|
||||
import {ManyToMany} from "../../../../../src/decorator/relations/ManyToMany";
|
||||
import {Post} from "./Post";
|
||||
|
||||
@Entity()
|
||||
export class User {
|
||||
@ -11,4 +13,7 @@ export class User {
|
||||
@Column()
|
||||
name: string;
|
||||
|
||||
@ManyToMany(type => Post, post => post.counters.likedUsers)
|
||||
likedPosts: Post[];
|
||||
|
||||
}
|
||||
@ -0,0 +1,362 @@
|
||||
import "reflect-metadata";
|
||||
import {Post} from "./entity/Post";
|
||||
import {Counters} from "./entity/Counters";
|
||||
import {Connection} from "../../../../src/connection/Connection";
|
||||
import {expect} from "chai";
|
||||
import {closeTestingConnections, createTestingConnections, reloadTestingDatabases} from "../../../utils/test-utils";
|
||||
import {Subcounters} from "./entity/Subcounters";
|
||||
import {User} from "./entity/User";
|
||||
|
||||
describe("embedded > embedded-many-to-one", () => {
|
||||
|
||||
let connections: Connection[];
|
||||
before(async () => connections = await createTestingConnections({
|
||||
entities: [__dirname + "/entity/*{.js,.ts}"],
|
||||
schemaCreate: true,
|
||||
dropSchemaOnConnection: true,
|
||||
}));
|
||||
beforeEach(() => reloadTestingDatabases(connections));
|
||||
after(() => closeTestingConnections(connections));
|
||||
|
||||
describe("owner side", () => {
|
||||
|
||||
it("should insert, load, update and remove entities with embeddeds when embedded entity having ManyToOne relation", () => Promise.all(connections.map(async connection => {
|
||||
|
||||
const user1 = new User();
|
||||
user1.name = "Alice";
|
||||
await connection.getRepository(User).persist(user1);
|
||||
|
||||
const user2 = new User();
|
||||
user2.name = "Bob";
|
||||
await connection.getRepository(User).persist(user2);
|
||||
|
||||
const user3 = new User();
|
||||
user3.name = "Clara";
|
||||
await connection.getRepository(User).persist(user3);
|
||||
|
||||
const postRepository = connection.getRepository(Post);
|
||||
|
||||
const post1 = new Post();
|
||||
post1.title = "About cars";
|
||||
post1.counters = new Counters();
|
||||
post1.counters.code = 1;
|
||||
post1.counters.comments = 1;
|
||||
post1.counters.favorites = 2;
|
||||
post1.counters.likes = 3;
|
||||
post1.counters.likedUser = user1;
|
||||
post1.counters.subcounters = new Subcounters();
|
||||
post1.counters.subcounters.version = 1;
|
||||
post1.counters.subcounters.watches = 5;
|
||||
await postRepository.persist(post1);
|
||||
|
||||
const post2 = new Post();
|
||||
post2.title = "About airplanes";
|
||||
post2.counters = new Counters();
|
||||
post2.counters.code = 2;
|
||||
post2.counters.comments = 2;
|
||||
post2.counters.favorites = 3;
|
||||
post2.counters.likes = 4;
|
||||
post2.counters.likedUser = user2;
|
||||
post2.counters.subcounters = new Subcounters();
|
||||
post2.counters.subcounters.version = 1;
|
||||
post2.counters.subcounters.watches = 10;
|
||||
await postRepository.persist(post2);
|
||||
|
||||
const loadedPosts = await connection.entityManager
|
||||
.createQueryBuilder(Post, "post")
|
||||
.leftJoinAndSelect("post.counters.likedUser", "likedUser")
|
||||
.orderBy("post.id")
|
||||
.getMany();
|
||||
|
||||
expect(loadedPosts[0].should.be.eql(
|
||||
{
|
||||
id: 1,
|
||||
title: "About cars",
|
||||
counters: {
|
||||
code: 1,
|
||||
comments: 1,
|
||||
favorites: 2,
|
||||
likes: 3,
|
||||
likedUser: { id: 1, name: "Alice" },
|
||||
subcounters: {
|
||||
version: 1,
|
||||
watches: 5
|
||||
}
|
||||
}
|
||||
}
|
||||
));
|
||||
expect(loadedPosts[1].should.be.eql(
|
||||
{
|
||||
id: 2,
|
||||
title: "About airplanes",
|
||||
counters: {
|
||||
code: 2,
|
||||
comments: 2,
|
||||
favorites: 3,
|
||||
likes: 4,
|
||||
likedUser: { id: 2, name: "Bob" },
|
||||
subcounters: {
|
||||
version: 1,
|
||||
watches: 10
|
||||
}
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
const loadedPost = await connection.entityManager
|
||||
.createQueryBuilder(Post, "post")
|
||||
.leftJoinAndSelect("post.counters.likedUser", "likedUser")
|
||||
.where("post.id = :id", { id: 1 })
|
||||
.getOne();
|
||||
|
||||
expect(loadedPost!.should.be.eql(
|
||||
{
|
||||
id: 1,
|
||||
title: "About cars",
|
||||
counters: {
|
||||
code: 1,
|
||||
comments: 1,
|
||||
favorites: 2,
|
||||
likes: 3,
|
||||
likedUser: { id: 1, name: "Alice" },
|
||||
subcounters: {
|
||||
version: 1,
|
||||
watches: 5
|
||||
}
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
loadedPost!.counters.favorites += 1;
|
||||
loadedPost!.counters.subcounters.watches += 1;
|
||||
loadedPost!.counters.likedUser = user3;
|
||||
await postRepository.persist(loadedPost!);
|
||||
|
||||
const loadedPost2 = await connection.entityManager
|
||||
.createQueryBuilder(Post, "post")
|
||||
.leftJoinAndSelect("post.counters.likedUser", "likedUser")
|
||||
.where("post.id = :id", { id: 1 })
|
||||
.getOne();
|
||||
|
||||
expect(loadedPost2!.should.be.eql(
|
||||
{
|
||||
id: 1,
|
||||
title: "About cars",
|
||||
counters: {
|
||||
code: 1,
|
||||
comments: 1,
|
||||
favorites: 3,
|
||||
likes: 3,
|
||||
likedUser: { id: 3, name: "Clara" },
|
||||
subcounters: {
|
||||
version: 1,
|
||||
watches: 6
|
||||
}
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
await postRepository.remove(loadedPost2!);
|
||||
|
||||
const loadedPosts2 = (await postRepository.find())!;
|
||||
expect(loadedPosts2.length).to.be.equal(1);
|
||||
expect(loadedPosts2[0].title).to.be.equal("About airplanes");
|
||||
})));
|
||||
});
|
||||
|
||||
describe("inverse side", () => {
|
||||
|
||||
it("should insert, load, update and remove entities with embeddeds when embedded entity having ManyToOne relation", () => Promise.all(connections.map(async connection => {
|
||||
|
||||
const post1 = new Post();
|
||||
post1.title = "About cars";
|
||||
post1.counters = new Counters();
|
||||
post1.counters.code = 1;
|
||||
post1.counters.comments = 1;
|
||||
post1.counters.favorites = 2;
|
||||
post1.counters.likes = 3;
|
||||
post1.counters.subcounters = new Subcounters();
|
||||
post1.counters.subcounters.version = 1;
|
||||
post1.counters.subcounters.watches = 5;
|
||||
await connection.getRepository(Post).persist(post1);
|
||||
|
||||
const post2 = new Post();
|
||||
post2.title = "About airplanes";
|
||||
post2.counters = new Counters();
|
||||
post2.counters.code = 2;
|
||||
post2.counters.comments = 2;
|
||||
post2.counters.favorites = 3;
|
||||
post2.counters.likes = 4;
|
||||
post2.counters.subcounters = new Subcounters();
|
||||
post2.counters.subcounters.version = 1;
|
||||
post2.counters.subcounters.watches = 10;
|
||||
await connection.getRepository(Post).persist(post2);
|
||||
|
||||
const post3 = new Post();
|
||||
post3.title = "About horses";
|
||||
post3.counters = new Counters();
|
||||
post3.counters.code = 3;
|
||||
post3.counters.comments = 5;
|
||||
post3.counters.favorites = 10;
|
||||
post3.counters.likes = 15;
|
||||
post3.counters.subcounters = new Subcounters();
|
||||
post3.counters.subcounters.version = 1;
|
||||
post3.counters.subcounters.watches = 30;
|
||||
await connection.getRepository(Post).persist(post3);
|
||||
|
||||
const user1 = new User();
|
||||
user1.name = "Alice";
|
||||
user1.likedPosts = [post1, post2];
|
||||
await connection.getRepository(User).persist(user1);
|
||||
|
||||
const user2 = new User();
|
||||
user2.name = "Bob";
|
||||
user2.likedPosts = [post3];
|
||||
await connection.getRepository(User).persist(user2);
|
||||
|
||||
const loadedUsers = await connection.entityManager
|
||||
.createQueryBuilder(User, "user")
|
||||
.leftJoinAndSelect("user.likedPosts", "likedPost")
|
||||
.orderBy("user.id, likedPost.id")
|
||||
.getMany();
|
||||
|
||||
expect(loadedUsers[0].should.be.eql(
|
||||
{
|
||||
id: 1,
|
||||
name: "Alice",
|
||||
likedPosts: [
|
||||
{
|
||||
id: 1,
|
||||
title: "About cars",
|
||||
counters: {
|
||||
code: 1,
|
||||
comments: 1,
|
||||
favorites: 2,
|
||||
likes: 3,
|
||||
subcounters: {
|
||||
version: 1,
|
||||
watches: 5
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: "About airplanes",
|
||||
counters: {
|
||||
code: 2,
|
||||
comments: 2,
|
||||
favorites: 3,
|
||||
likes: 4,
|
||||
subcounters: {
|
||||
version: 1,
|
||||
watches: 10
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
));
|
||||
expect(loadedUsers[1].should.be.eql(
|
||||
{
|
||||
id: 2,
|
||||
name: "Bob",
|
||||
likedPosts: [
|
||||
{
|
||||
id: 3,
|
||||
title: "About horses",
|
||||
counters: {
|
||||
code: 3,
|
||||
comments: 5,
|
||||
favorites: 10,
|
||||
likes: 15,
|
||||
subcounters: {
|
||||
version: 1,
|
||||
watches: 30
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
));
|
||||
|
||||
const loadedUser = await connection.entityManager
|
||||
.createQueryBuilder(User, "user")
|
||||
.leftJoinAndSelect("user.likedPosts", "likedPost")
|
||||
.orderBy("likedPost.id")
|
||||
.where("user.id = :id", { id: 1 })
|
||||
.getOne();
|
||||
|
||||
expect(loadedUser!.should.be.eql(
|
||||
{
|
||||
id: 1,
|
||||
name: "Alice",
|
||||
likedPosts: [
|
||||
{
|
||||
id: 1,
|
||||
title: "About cars",
|
||||
counters: {
|
||||
code: 1,
|
||||
comments: 1,
|
||||
favorites: 2,
|
||||
likes: 3,
|
||||
subcounters: {
|
||||
version: 1,
|
||||
watches: 5
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: "About airplanes",
|
||||
counters: {
|
||||
code: 2,
|
||||
comments: 2,
|
||||
favorites: 3,
|
||||
likes: 4,
|
||||
subcounters: {
|
||||
version: 1,
|
||||
watches: 10
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
));
|
||||
|
||||
loadedUser!.name = "Anna";
|
||||
loadedUser!.likedPosts = [post1];
|
||||
await connection.getRepository(User).persist(loadedUser!);
|
||||
|
||||
const loadedUser2 = await connection.entityManager
|
||||
.createQueryBuilder(User, "user")
|
||||
.leftJoinAndSelect("user.likedPosts", "likedPost")
|
||||
.orderBy("likedPost.id")
|
||||
.where("user.id = :id", { id: 1 })
|
||||
.getOne();
|
||||
|
||||
expect(loadedUser2!.should.be.eql(
|
||||
{
|
||||
id: 1,
|
||||
name: "Anna",
|
||||
likedPosts: [
|
||||
{
|
||||
id: 1,
|
||||
title: "About cars",
|
||||
counters: {
|
||||
code: 1,
|
||||
comments: 1,
|
||||
favorites: 2,
|
||||
likes: 3,
|
||||
subcounters: {
|
||||
version: 1,
|
||||
watches: 5
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
));
|
||||
})));
|
||||
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,31 @@
|
||||
import {EmbeddableEntity} from "../../../../../src/decorator/entity/EmbeddableEntity";
|
||||
import {Column} from "../../../../../src/decorator/columns/Column";
|
||||
import {JoinColumn} from "../../../../../src/decorator/relations/JoinColumn";
|
||||
import {ManyToOne} from "../../../../../src/decorator/relations/ManyToOne";
|
||||
import {Embedded} from "../../../../../src/decorator/Embedded";
|
||||
import {User} from "./User";
|
||||
import {Subcounters} from "./Subcounters";
|
||||
|
||||
@EmbeddableEntity()
|
||||
export class Counters {
|
||||
|
||||
@Column()
|
||||
code: number;
|
||||
|
||||
@Column()
|
||||
likes: number;
|
||||
|
||||
@Column()
|
||||
comments: number;
|
||||
|
||||
@Column()
|
||||
favorites: number;
|
||||
|
||||
@Embedded(() => Subcounters)
|
||||
subcounters: Subcounters;
|
||||
|
||||
@ManyToOne(type => User)
|
||||
@JoinColumn()
|
||||
likedUser: User;
|
||||
|
||||
}
|
||||
19
test/functional/embedded/embedded-many-to-one/entity/Post.ts
Normal file
19
test/functional/embedded/embedded-many-to-one/entity/Post.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import {Entity} from "../../../../../src/decorator/entity/Entity";
|
||||
import {Column} from "../../../../../src/decorator/columns/Column";
|
||||
import {Embedded} from "../../../../../src/decorator/Embedded";
|
||||
import {PrimaryGeneratedColumn} from "../../../../../src/decorator/columns/PrimaryGeneratedColumn";
|
||||
import {Counters} from "./Counters";
|
||||
|
||||
@Entity()
|
||||
export class Post {
|
||||
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
title: string;
|
||||
|
||||
@Embedded(() => Counters)
|
||||
counters: Counters;
|
||||
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
import {EmbeddableEntity} from "../../../../../src/decorator/entity/EmbeddableEntity";
|
||||
import {Column} from "../../../../../src/decorator/columns/Column";
|
||||
|
||||
@EmbeddableEntity()
|
||||
export class Subcounters {
|
||||
|
||||
@Column()
|
||||
version: number;
|
||||
|
||||
@Column()
|
||||
watches: number;
|
||||
|
||||
}
|
||||
19
test/functional/embedded/embedded-many-to-one/entity/User.ts
Normal file
19
test/functional/embedded/embedded-many-to-one/entity/User.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import {Column} from "../../../../../src/decorator/columns/Column";
|
||||
import {PrimaryGeneratedColumn} from "../../../../../src/decorator/columns/PrimaryGeneratedColumn";
|
||||
import {Entity} from "../../../../../src/decorator/entity/Entity";
|
||||
import {OneToMany} from "../../../../../src/decorator/relations/OneToMany";
|
||||
import {Post} from "./Post";
|
||||
|
||||
@Entity()
|
||||
export class User {
|
||||
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
name: string;
|
||||
|
||||
@OneToMany(type => Post, post => post.counters.likedUser)
|
||||
likedPosts: Post[];
|
||||
|
||||
}
|
||||
@ -0,0 +1,331 @@
|
||||
import "reflect-metadata";
|
||||
import {Post} from "./entity/Post";
|
||||
import {Counters} from "./entity/Counters";
|
||||
import {Connection} from "../../../../src/connection/Connection";
|
||||
import {expect} from "chai";
|
||||
import {closeTestingConnections, createTestingConnections, reloadTestingDatabases} from "../../../utils/test-utils";
|
||||
import {Subcounters} from "./entity/Subcounters";
|
||||
import {User} from "./entity/User";
|
||||
|
||||
describe("embedded > embedded-one-to-one", () => {
|
||||
|
||||
let connections: Connection[];
|
||||
before(async () => connections = await createTestingConnections({
|
||||
entities: [__dirname + "/entity/*{.js,.ts}"],
|
||||
schemaCreate: true,
|
||||
dropSchemaOnConnection: true,
|
||||
}));
|
||||
beforeEach(() => reloadTestingDatabases(connections));
|
||||
after(() => closeTestingConnections(connections));
|
||||
|
||||
describe("owner side", () => {
|
||||
|
||||
it("should insert, load, update and remove entities with embeddeds when embedded entity having OneToOne relation", () => Promise.all(connections.map(async connection => {
|
||||
|
||||
const user1 = new User();
|
||||
user1.name = "Alice";
|
||||
await connection.getRepository(User).persist(user1);
|
||||
|
||||
const user2 = new User();
|
||||
user2.name = "Bob";
|
||||
await connection.getRepository(User).persist(user2);
|
||||
|
||||
const user3 = new User();
|
||||
user3.name = "Clara";
|
||||
await connection.getRepository(User).persist(user3);
|
||||
|
||||
const postRepository = connection.getRepository(Post);
|
||||
|
||||
const post1 = new Post();
|
||||
post1.title = "About cars";
|
||||
post1.counters = new Counters();
|
||||
post1.counters.code = 1;
|
||||
post1.counters.comments = 1;
|
||||
post1.counters.favorites = 2;
|
||||
post1.counters.likes = 3;
|
||||
post1.counters.likedUser = user1;
|
||||
post1.counters.subcounters = new Subcounters();
|
||||
post1.counters.subcounters.version = 1;
|
||||
post1.counters.subcounters.watches = 5;
|
||||
await postRepository.persist(post1);
|
||||
|
||||
const post2 = new Post();
|
||||
post2.title = "About airplanes";
|
||||
post2.counters = new Counters();
|
||||
post2.counters.code = 2;
|
||||
post2.counters.comments = 2;
|
||||
post2.counters.favorites = 3;
|
||||
post2.counters.likes = 4;
|
||||
post2.counters.likedUser = user2;
|
||||
post2.counters.subcounters = new Subcounters();
|
||||
post2.counters.subcounters.version = 1;
|
||||
post2.counters.subcounters.watches = 10;
|
||||
await postRepository.persist(post2);
|
||||
|
||||
const loadedPosts = await connection.entityManager
|
||||
.createQueryBuilder(Post, "post")
|
||||
.leftJoinAndSelect("post.counters.likedUser", "likedUser")
|
||||
.orderBy("post.id")
|
||||
.getMany();
|
||||
|
||||
expect(loadedPosts[0].should.be.eql(
|
||||
{
|
||||
id: 1,
|
||||
title: "About cars",
|
||||
counters: {
|
||||
code: 1,
|
||||
comments: 1,
|
||||
favorites: 2,
|
||||
likes: 3,
|
||||
likedUser: { id: 1, name: "Alice" },
|
||||
subcounters: {
|
||||
version: 1,
|
||||
watches: 5
|
||||
}
|
||||
}
|
||||
}
|
||||
));
|
||||
expect(loadedPosts[1].should.be.eql(
|
||||
{
|
||||
id: 2,
|
||||
title: "About airplanes",
|
||||
counters: {
|
||||
code: 2,
|
||||
comments: 2,
|
||||
favorites: 3,
|
||||
likes: 4,
|
||||
likedUser: { id: 2, name: "Bob" },
|
||||
subcounters: {
|
||||
version: 1,
|
||||
watches: 10
|
||||
}
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
const loadedPost = await connection.entityManager
|
||||
.createQueryBuilder(Post, "post")
|
||||
.leftJoinAndSelect("post.counters.likedUser", "likedUser")
|
||||
.where("post.id = :id", { id: 1 })
|
||||
.getOne();
|
||||
|
||||
expect(loadedPost!.should.be.eql(
|
||||
{
|
||||
id: 1,
|
||||
title: "About cars",
|
||||
counters: {
|
||||
code: 1,
|
||||
comments: 1,
|
||||
favorites: 2,
|
||||
likes: 3,
|
||||
likedUser: { id: 1, name: "Alice" },
|
||||
subcounters: {
|
||||
version: 1,
|
||||
watches: 5
|
||||
}
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
loadedPost!.counters.favorites += 1;
|
||||
loadedPost!.counters.subcounters.watches += 1;
|
||||
loadedPost!.counters.likedUser = user3;
|
||||
await postRepository.persist(loadedPost!);
|
||||
|
||||
const loadedPost2 = await connection.entityManager
|
||||
.createQueryBuilder(Post, "post")
|
||||
.leftJoinAndSelect("post.counters.likedUser", "likedUser")
|
||||
.where("post.id = :id", { id: 1 })
|
||||
.getOne();
|
||||
|
||||
expect(loadedPost2!.should.be.eql(
|
||||
{
|
||||
id: 1,
|
||||
title: "About cars",
|
||||
counters: {
|
||||
code: 1,
|
||||
comments: 1,
|
||||
favorites: 3,
|
||||
likes: 3,
|
||||
likedUser: { id: 3, name: "Clara" },
|
||||
subcounters: {
|
||||
version: 1,
|
||||
watches: 6
|
||||
}
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
await postRepository.remove(loadedPost2!);
|
||||
|
||||
const loadedPosts2 = (await postRepository.find())!;
|
||||
expect(loadedPosts2.length).to.be.equal(1);
|
||||
expect(loadedPosts2[0].title).to.be.equal("About airplanes");
|
||||
})));
|
||||
});
|
||||
|
||||
// uncomment this section once inverse side persistment of one-to-one relation will be finished
|
||||
describe.skip("inverse side", () => {
|
||||
|
||||
|
||||
it("should insert, load, update and remove entities with embeddeds when embedded entity having OneToOne relation", () => Promise.all(connections.map(async connection => {
|
||||
|
||||
const post1 = new Post();
|
||||
post1.title = "About cars";
|
||||
post1.counters = new Counters();
|
||||
post1.counters.code = 1;
|
||||
post1.counters.comments = 1;
|
||||
post1.counters.favorites = 2;
|
||||
post1.counters.likes = 3;
|
||||
post1.counters.subcounters = new Subcounters();
|
||||
post1.counters.subcounters.version = 1;
|
||||
post1.counters.subcounters.watches = 5;
|
||||
await connection.getRepository(Post).persist(post1);
|
||||
|
||||
const post2 = new Post();
|
||||
post2.title = "About airplanes";
|
||||
post2.counters = new Counters();
|
||||
post2.counters.code = 2;
|
||||
post2.counters.comments = 2;
|
||||
post2.counters.favorites = 3;
|
||||
post2.counters.likes = 4;
|
||||
post2.counters.subcounters = new Subcounters();
|
||||
post2.counters.subcounters.version = 1;
|
||||
post2.counters.subcounters.watches = 10;
|
||||
await connection.getRepository(Post).persist(post2);
|
||||
|
||||
const post3 = new Post();
|
||||
post3.title = "About horses";
|
||||
post3.counters = new Counters();
|
||||
post3.counters.code = 3;
|
||||
post3.counters.comments = 4;
|
||||
post3.counters.favorites = 5;
|
||||
post3.counters.likes = 6;
|
||||
post3.counters.subcounters = new Subcounters();
|
||||
post3.counters.subcounters.version = 1;
|
||||
post3.counters.subcounters.watches = 12;
|
||||
await connection.getRepository(Post).persist(post3);
|
||||
|
||||
const user1 = new User();
|
||||
user1.name = "Alice";
|
||||
user1.likedPost = post1;
|
||||
await connection.getRepository(User).persist(user1);
|
||||
|
||||
const user2 = new User();
|
||||
user2.name = "Bob";
|
||||
user2.likedPost = post2;
|
||||
await connection.getRepository(User).persist(user2);
|
||||
|
||||
const loadedUsers = await connection.entityManager
|
||||
.createQueryBuilder(User, "user")
|
||||
.leftJoinAndSelect("user.likedPost", "likedPost")
|
||||
.orderBy("user.id")
|
||||
.getMany();
|
||||
|
||||
expect(loadedUsers[0].should.be.eql(
|
||||
{
|
||||
id: 1,
|
||||
name: "Alice",
|
||||
likedPost: {
|
||||
id: 1,
|
||||
title: "About cars",
|
||||
counters: {
|
||||
code: 1,
|
||||
comments: 1,
|
||||
favorites: 2,
|
||||
likes: 3,
|
||||
subcounters: {
|
||||
version: 1,
|
||||
watches: 5
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
));
|
||||
expect(loadedUsers[1].should.be.eql(
|
||||
{
|
||||
id: 2,
|
||||
name: "Bob",
|
||||
likedPost: {
|
||||
id: 2,
|
||||
title: "About airplanes",
|
||||
counters: {
|
||||
code: 2,
|
||||
comments: 2,
|
||||
favorites: 3,
|
||||
likes: 4,
|
||||
subcounters: {
|
||||
version: 1,
|
||||
watches: 10
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
const loadedUser = await connection.entityManager
|
||||
.createQueryBuilder(User, "user")
|
||||
.leftJoinAndSelect("user.likedPost", "likedPost")
|
||||
.where("user.id = :id", { id: 1 })
|
||||
.getOne();
|
||||
|
||||
expect(loadedUser!.should.be.eql(
|
||||
{
|
||||
id: 1,
|
||||
name: "Alice",
|
||||
likedPost: {
|
||||
id: 1,
|
||||
title: "About cars",
|
||||
counters: {
|
||||
code: 1,
|
||||
comments: 1,
|
||||
favorites: 2,
|
||||
likes: 3,
|
||||
subcounters: {
|
||||
version: 1,
|
||||
watches: 5
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
loadedUser!.name = "Anna";
|
||||
loadedUser!.likedPost = post3;
|
||||
await connection.getRepository(User).persist(loadedUser!);
|
||||
|
||||
const loadedUser2 = await connection.entityManager
|
||||
.createQueryBuilder(User, "user")
|
||||
.leftJoinAndSelect("user.likedPost", "likedPost")
|
||||
.where("user.id = :id", { id: 1 })
|
||||
.getOne();
|
||||
|
||||
expect(loadedUser2!.should.be.eql(
|
||||
{
|
||||
id: 1,
|
||||
name: "Anna",
|
||||
likedPost: {
|
||||
id: 3,
|
||||
title: "About horses",
|
||||
counters: {
|
||||
code: 3,
|
||||
comments: 4,
|
||||
favorites: 5,
|
||||
likes: 6,
|
||||
subcounters: {
|
||||
version: 1,
|
||||
watches: 12
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
await connection.getRepository(User).remove(loadedUser2!);
|
||||
|
||||
const loadedUsers2 = (await connection.getRepository(User).find())!;
|
||||
expect(loadedUsers2.length).to.be.equal(1);
|
||||
expect(loadedUsers2[0].name).to.be.equal("Bob");
|
||||
})));
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,31 @@
|
||||
import {EmbeddableEntity} from "../../../../../src/decorator/entity/EmbeddableEntity";
|
||||
import {Column} from "../../../../../src/decorator/columns/Column";
|
||||
import {JoinColumn} from "../../../../../src/decorator/relations/JoinColumn";
|
||||
import {Embedded} from "../../../../../src/decorator/Embedded";
|
||||
import {OneToOne} from "../../../../../src/decorator/relations/OneToOne";
|
||||
import {User} from "./User";
|
||||
import {Subcounters} from "./Subcounters";
|
||||
|
||||
@EmbeddableEntity()
|
||||
export class Counters {
|
||||
|
||||
@Column()
|
||||
code: number;
|
||||
|
||||
@Column()
|
||||
likes: number;
|
||||
|
||||
@Column()
|
||||
comments: number;
|
||||
|
||||
@Column()
|
||||
favorites: number;
|
||||
|
||||
@Embedded(() => Subcounters)
|
||||
subcounters: Subcounters;
|
||||
|
||||
@OneToOne(type => User)
|
||||
@JoinColumn()
|
||||
likedUser: User;
|
||||
|
||||
}
|
||||
19
test/functional/embedded/embedded-one-to-one/entity/Post.ts
Normal file
19
test/functional/embedded/embedded-one-to-one/entity/Post.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import {Entity} from "../../../../../src/decorator/entity/Entity";
|
||||
import {Column} from "../../../../../src/decorator/columns/Column";
|
||||
import {Embedded} from "../../../../../src/decorator/Embedded";
|
||||
import {PrimaryGeneratedColumn} from "../../../../../src/decorator/columns/PrimaryGeneratedColumn";
|
||||
import {Counters} from "./Counters";
|
||||
|
||||
@Entity()
|
||||
export class Post {
|
||||
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
title: string;
|
||||
|
||||
@Embedded(() => Counters)
|
||||
counters: Counters;
|
||||
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
import {EmbeddableEntity} from "../../../../../src/decorator/entity/EmbeddableEntity";
|
||||
import {Column} from "../../../../../src/decorator/columns/Column";
|
||||
|
||||
@EmbeddableEntity()
|
||||
export class Subcounters {
|
||||
|
||||
@Column()
|
||||
version: number;
|
||||
|
||||
@Column()
|
||||
watches: number;
|
||||
|
||||
}
|
||||
19
test/functional/embedded/embedded-one-to-one/entity/User.ts
Normal file
19
test/functional/embedded/embedded-one-to-one/entity/User.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import {Column} from "../../../../../src/decorator/columns/Column";
|
||||
import {PrimaryGeneratedColumn} from "../../../../../src/decorator/columns/PrimaryGeneratedColumn";
|
||||
import {Entity} from "../../../../../src/decorator/entity/Entity";
|
||||
import {Post} from "./Post";
|
||||
import {OneToOne} from "../../../../../src/decorator/relations/OneToOne";
|
||||
|
||||
@Entity()
|
||||
export class User {
|
||||
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
name: string;
|
||||
|
||||
@OneToOne(type => Post, post => post.counters.likedUser)
|
||||
likedPost: Post;
|
||||
|
||||
}
|
||||
@ -0,0 +1,62 @@
|
||||
import "reflect-metadata";
|
||||
import {Post} from "./entity/Post";
|
||||
import {Counters} from "./entity/Counters";
|
||||
import {Connection} from "../../../src/connection/Connection";
|
||||
import {expect} from "chai";
|
||||
import {setupTestingConnections} from "../../utils/test-utils";
|
||||
import {Subcounters} from "./entity/Subcounters";
|
||||
import {User} from "./entity/User";
|
||||
import {getConnectionManager} from "../../../src/index";
|
||||
|
||||
describe("entity-metadata > property-map", () => {
|
||||
|
||||
let connections: Connection[];
|
||||
before(() => {
|
||||
connections = setupTestingConnections({ entities: [__dirname + "/entity/*{.js,.ts}"] })
|
||||
.map(options => getConnectionManager().create(options))
|
||||
.map(connection => {
|
||||
connection.buildMetadatas();
|
||||
return connection;
|
||||
});
|
||||
});
|
||||
|
||||
it("should create correct property map object", () => Promise.all(connections.map(async connection => {
|
||||
|
||||
const user1 = new User();
|
||||
user1.id = 1;
|
||||
user1.name = "Alice";
|
||||
|
||||
const post1 = new Post();
|
||||
post1.title = "About cars";
|
||||
post1.counters = new Counters();
|
||||
post1.counters.code = 1;
|
||||
post1.counters.comments = 1;
|
||||
post1.counters.favorites = 2;
|
||||
post1.counters.likes = 3;
|
||||
post1.counters.likedUsers = [user1];
|
||||
post1.counters.subcounters = new Subcounters();
|
||||
post1.counters.subcounters.version = 1;
|
||||
post1.counters.subcounters.watches = 5;
|
||||
post1.counters.subcounters.watchedUsers = [user1];
|
||||
|
||||
const postPropertiesMap = connection.getMetadata(Post).createPropertiesMap();
|
||||
expect(postPropertiesMap.should.be.eql(
|
||||
{
|
||||
id: "id",
|
||||
title: "title",
|
||||
counters: {
|
||||
code: "counters.code",
|
||||
likes: "counters.likes",
|
||||
comments: "counters.comments",
|
||||
favorites: "counters.favorites",
|
||||
subcounters: {
|
||||
version: "counters.subcounters.version",
|
||||
watches: "counters.subcounters.watches",
|
||||
watchedUsers: "counters.subcounters.watchedUsers"
|
||||
},
|
||||
likedUsers: "counters.likedUsers"
|
||||
}
|
||||
}
|
||||
));
|
||||
})));
|
||||
});
|
||||
31
test/functional/entity-metadata/entity/Counters.ts
Normal file
31
test/functional/entity-metadata/entity/Counters.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import {EmbeddableEntity} from "../../../../src/decorator/entity/EmbeddableEntity";
|
||||
import {Column} from "../../../../src/decorator/columns/Column";
|
||||
import {Embedded} from "../../../../src/decorator/Embedded";
|
||||
import {ManyToMany} from "../../../../src/decorator/relations/ManyToMany";
|
||||
import {JoinTable} from "../../../../src/decorator/relations/JoinTable";
|
||||
import {Subcounters} from "./Subcounters";
|
||||
import {User} from "./User";
|
||||
|
||||
@EmbeddableEntity()
|
||||
export class Counters {
|
||||
|
||||
@Column()
|
||||
code: number;
|
||||
|
||||
@Column()
|
||||
likes: number;
|
||||
|
||||
@Column()
|
||||
comments: number;
|
||||
|
||||
@Column()
|
||||
favorites: number;
|
||||
|
||||
@Embedded(() => Subcounters)
|
||||
subcounters: Subcounters;
|
||||
|
||||
@ManyToMany(type => User, user => user.likedPosts)
|
||||
@JoinTable()
|
||||
likedUsers: User[];
|
||||
|
||||
}
|
||||
19
test/functional/entity-metadata/entity/Post.ts
Normal file
19
test/functional/entity-metadata/entity/Post.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import {Entity} from "../../../../src/decorator/entity/Entity";
|
||||
import {Column} from "../../../../src/decorator/columns/Column";
|
||||
import {Embedded} from "../../../../src/decorator/Embedded";
|
||||
import {PrimaryGeneratedColumn} from "../../../../src/decorator/columns/PrimaryGeneratedColumn";
|
||||
import {Counters} from "./Counters";
|
||||
|
||||
@Entity()
|
||||
export class Post {
|
||||
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
title: string;
|
||||
|
||||
@Embedded(() => Counters)
|
||||
counters: Counters;
|
||||
|
||||
}
|
||||
20
test/functional/entity-metadata/entity/Subcounters.ts
Normal file
20
test/functional/entity-metadata/entity/Subcounters.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import {EmbeddableEntity} from "../../../../src/decorator/entity/EmbeddableEntity";
|
||||
import {Column} from "../../../../src/decorator/columns/Column";
|
||||
import {ManyToMany} from "../../../../src/decorator/relations/ManyToMany";
|
||||
import {JoinTable} from "../../../../src/decorator/relations/JoinTable";
|
||||
import {User} from "./User";
|
||||
|
||||
@EmbeddableEntity()
|
||||
export class Subcounters {
|
||||
|
||||
@Column()
|
||||
version: number;
|
||||
|
||||
@Column()
|
||||
watches: number;
|
||||
|
||||
@ManyToMany(type => User)
|
||||
@JoinTable()
|
||||
watchedUsers: User[];
|
||||
|
||||
}
|
||||
19
test/functional/entity-metadata/entity/User.ts
Normal file
19
test/functional/entity-metadata/entity/User.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import {Column} from "../../../../src/decorator/columns/Column";
|
||||
import {PrimaryGeneratedColumn} from "../../../../src/decorator/columns/PrimaryGeneratedColumn";
|
||||
import {Entity} from "../../../../src/decorator/entity/Entity";
|
||||
import {ManyToMany} from "../../../../src/decorator/relations/ManyToMany";
|
||||
import {Post} from "./Post";
|
||||
|
||||
@Entity()
|
||||
export class User {
|
||||
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
name: string;
|
||||
|
||||
@ManyToMany(type => Post, post => post.counters.likedUsers)
|
||||
likedPosts: Post[];
|
||||
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user