implementing multiple primary key functionality in to RelationId

This commit is contained in:
Zotov Dmitry 2017-05-12 17:16:41 +05:00
parent e4c40d936d
commit 50d350434b
61 changed files with 2827 additions and 568 deletions

View File

@ -11,6 +11,6 @@ export interface JoinColumnOptions {
/**
* Name of the column in the entity to which this column is referenced.
*/
readonly referencedColumnName?: string;
readonly referencedColumnName?: string; // TODO rename to referencedColumn
}

View File

@ -239,16 +239,7 @@ export class ColumnMetadata {
if (this.embeddedMetadata) {
// because embedded can be inside other embedded we need to go recursively and collect all prefix name
const prefixes: string[] = [];
const buildPrefixRecursively = (embeddedMetadata: EmbeddedMetadata) => {
if (embeddedMetadata.parentEmbeddedMetadata)
buildPrefixRecursively(embeddedMetadata.parentEmbeddedMetadata);
prefixes.push(embeddedMetadata.prefix);
};
buildPrefixRecursively(this.embeddedMetadata);
return this.entityMetadata.namingStrategy.embeddedColumnName(prefixes, this.propertyName, this._name);
return this.entityMetadata.namingStrategy.embeddedColumnName(this.embeddedMetadata.prefix, this.propertyName, this._name);
}
// if there is a naming strategy then use it to normalize propertyName as column name

View File

@ -4,7 +4,6 @@ import {IndexMetadata} from "./IndexMetadata";
import {RelationTypes} from "./types/RelationTypes";
import {ForeignKeyMetadata} from "./ForeignKeyMetadata";
import {NamingStrategyInterface} from "../naming-strategy/NamingStrategyInterface";
import {EntityMetadataArgs} from "../metadata-args/EntityMetadataArgs";
import {EmbeddedMetadata} from "./EmbeddedMetadata";
import {ObjectLiteral} from "../common/ObjectLiteral";
import {LazyRelationsWrapper} from "../lazy-loading/LazyRelationsWrapper";
@ -209,7 +208,6 @@ export class EntityMetadata {
/**
* Columns of the entity, including columns that are coming from the embeddeds of this entity.
* @deprecated
*/
get columns(): ColumnMetadata[] {
return this.embeddeds.reduce((columns, embedded) => columns.concat(embedded.columnsFromTree), this.ownColumns);

View File

@ -1,7 +1,6 @@
import {NamingStrategyInterface} from "./NamingStrategyInterface";
import {RandomGenerator} from "../util/RandomGenerator";
import {camelCase, snakeCase} from "../util/StringUtils";
import {TableType} from "../metadata/types/TableTypes";
/**
* Naming strategy that is used by default.
@ -31,11 +30,9 @@ export class DefaultNamingStrategy implements NamingStrategyInterface {
return customName ? customName : propertyName;
}
embeddedColumnName(prefixes: string[], columnPropertyName: string, columnCustomName?: string): string {
embeddedColumnName(prefix: string, columnPropertyName: string, columnCustomName?: string): string {
// todo: need snake case property name but only if its a property name and not a custom embedded prefix
prefixes = prefixes.filter(prefix => !!prefix);
const embeddedPropertyName = prefixes.length ? prefixes.join("_") + "_" : "";
return camelCase(embeddedPropertyName + (columnCustomName ? columnCustomName : columnPropertyName));
return camelCase(prefix + "_" + (columnCustomName ? columnCustomName : columnPropertyName));
}
relationName(propertyName: string): string {

View File

@ -32,7 +32,7 @@ export interface NamingStrategyInterface {
/**
* Gets the embedded's column name from the given property name.
*/
embeddedColumnName(prefixes: string[], columnPropertyName: string, columnCustomName?: string): string;
embeddedColumnName(prefix: string, columnPropertyName: string, columnCustomName?: string): string;
/**
* Gets the table's relation name from the given property name.

View File

@ -5,6 +5,7 @@ import {Subject} from "./Subject";
import {QueryRunnerProvider} from "../query-runner/QueryRunnerProvider";
import {SpecificRepository} from "../repository/SpecificRepository";
import {MongoDriver} from "../driver/mongodb/MongoDriver";
import {OrmUtils} from "../util/OrmUtils";
/**
* To be able to execute persistence operations we need to load all entities from the database we need.
@ -777,6 +778,7 @@ export class SubjectBuilder<Entity extends ObjectLiteral> {
const specificRepository = new SpecificRepository(this.connection, subject.metadata, this.queryRunnerProvider);
existInverseEntityRelationIds = await specificRepository
.findRelationIds(relation, subject.databaseEntity);
// console.log(existInverseEntityRelationIds);
}
// get all inverse entities relation ids that are "bind" to the currently persisted entity
@ -784,8 +786,7 @@ export class SubjectBuilder<Entity extends ObjectLiteral> {
.map(subRelationValue => {
const joinColumns = relation.isOwning ? relation.inverseJoinColumns : relation.inverseRelation.joinColumns;
return joinColumns.reduce((ids, joinColumn) => {
ids[joinColumn.referencedColumn!.propertyName] = subRelationValue[joinColumn.referencedColumn!.propertyName];
return ids;
return OrmUtils.mergeDeep(ids, joinColumn.createValueMap(joinColumn.referencedColumn!.getEntityValue(subRelationValue))); // todo: duplicate. relation.createJoinColumnsIdMap(entity) ?
}, {} as ObjectLiteral);
})
.filter(subRelationValue => subRelationValue !== undefined && subRelationValue !== null);
@ -804,8 +805,7 @@ export class SubjectBuilder<Entity extends ObjectLiteral> {
const joinColumns = relation.isOwning ? relation.inverseJoinColumns : relation.inverseRelation.joinColumns;
const ids = joinColumns.reduce((ids, joinColumn) => {
ids[joinColumn.referencedColumn!.propertyName] = subRelatedValue[joinColumn.referencedColumn!.propertyName];
return ids;
return OrmUtils.mergeDeep(ids, joinColumn.createValueMap(joinColumn.referencedColumn!.getEntityValue(subRelatedValue))); // todo: duplicate. relation.createJoinColumnsIdMap(entity) ?
}, {} as ObjectLiteral);
return !existInverseEntityRelationIds.find(relationId => {
return relation.inverseEntityMetadata.compareIds(relationId, ids);

View File

@ -8,7 +8,6 @@ import {QueryRunnerProvider} from "../query-runner/QueryRunnerProvider";
import {EntityManager} from "../entity-manager/EntityManager";
import {PromiseUtils} from "../util/PromiseUtils";
import {MongoDriver} from "../driver/mongodb/MongoDriver";
import {EmbeddedMetadata} from "../metadata/EmbeddedMetadata";
import {ColumnMetadata} from "../metadata/ColumnMetadata";
/**
@ -318,6 +317,8 @@ export class SubjectOperationExecutor {
if (!Object.keys(conditions).length)
return;
const updatePromise = this.queryRunner.update(subject.metadata.tableName, updateOptions, conditions);
updatePromises.push(updatePromise);
}

View File

@ -37,8 +37,8 @@ export class RelationIdAttribute {
// Constructor
// -------------------------------------------------------------------------
constructor(private expressionMap: QueryExpressionMap,
relationIdAttribute?: Partial<RelationIdAttribute>) {
constructor(private queryExpressionMap: QueryExpressionMap,
relationIdAttribute?: Partial<RelationIdAttribute>) {
Object.assign(this, relationIdAttribute || {});
}
@ -60,7 +60,7 @@ export class RelationIdAttribute {
if (!QueryBuilderUtils.isAliasProperty(this.relationName))
throw new Error(`Given value must be a string representation of alias property`);
return this.relationName.split(".")[0];
return this.relationName.substr(0, this.relationName.indexOf("."));
}
/**
@ -70,11 +70,11 @@ export class RelationIdAttribute {
* This value is extracted from entityOrProperty value.
* This is available when join was made using "post.category" syntax.
*/
get relationProperty(): string {
get relationPropertyPath(): string {
if (!QueryBuilderUtils.isAliasProperty(this.relationName))
throw new Error(`Given value must be a string representation of alias property`);
return this.relationName.split(".")[1];
return this.relationName.substr(this.relationName.indexOf(".") + 1);
}
/**
@ -86,9 +86,8 @@ export class RelationIdAttribute {
if (!QueryBuilderUtils.isAliasProperty(this.relationName))
throw new Error(`Given value must be a string representation of alias property`);
const [parentAlias, relationProperty] = this.relationName.split(".");
const relationOwnerSelection = this.expressionMap.findAliasByName(parentAlias);
return relationOwnerSelection.metadata.findRelationWithPropertyName(relationProperty);
const relationOwnerSelection = this.queryExpressionMap.findAliasByName(this.parentAlias!);
return relationOwnerSelection.metadata.findRelationWithPropertyPath(this.relationPropertyPath!);
}
/**
@ -99,18 +98,6 @@ export class RelationIdAttribute {
return parentAlias + "_" + relationProperty + "_relation_id";
}
/*get referenceColumnName(): string {
if (this.relation.isManyToOne || this.relation.isOneToOneOwner) {
return this.relation.joinColumn.referencedColumn.fullName;
} else if (this.relation.isOneToMany || this.relation.isOneToOneNotOwner) {
return this.relation.inverseRelation.joinColumn.referencedColumn.fullName;
} else {
return this.relation.isOwning ? this.relation.joinTable.referencedColumn.fullName : this.relation.inverseRelation.joinTable.referencedColumn.fullName;
}
}*/
/**
* Metadata of the joined entity.
* If extra condition without entity was joined, then it will return undefined.
@ -120,11 +107,11 @@ export class RelationIdAttribute {
}
get mapToPropertyParentAlias(): string {
return this.mapToProperty!.split(".")[0];
return this.mapToProperty.substr(0, this.mapToProperty.indexOf("."));
}
get mapToPropertyPropertyName(): string {
return this.mapToProperty!.split(".")[1];
get mapToPropertyPropertyPath(): string {
return this.mapToProperty.substr(this.mapToProperty.indexOf(".") + 1);
}
}

View File

@ -2,5 +2,5 @@ import {RelationIdAttribute} from "./RelationIdAttribute";
export interface RelationIdLoadResult {
relationIdAttribute: RelationIdAttribute;
results: { id: any, parentId: any, manyToManyId?: any }[];
results: any[];
}

View File

@ -47,85 +47,54 @@ export class RelationIdLoader {
} else if (relationIdAttr.relation.isOneToMany || relationIdAttr.relation.isOneToOneNotOwner) {
// example: Post and Category
// loadRelationIdAndMap("category.postIds", "category.posts")
// we expect it to load array of post ids
// loadRelationIdAndMap("post.categoryIds", "post.categories")
// we expect it to load array of category ids
// todo: take post ids - they can be multiple
// todo: create test with multiple primary columns usage
const relation = relationIdAttr.relation; // "post.categories"
const inverseRelation = relation.inverseRelation; // "category.post"
const joinColumns = relation.isOwning ? relation.joinColumns : inverseRelation.joinColumns;
const table = relation.inverseEntityMetadata.target; // category
const tableName = relation.inverseEntityMetadata.tableName; // category
const tableAlias = relationIdAttr.alias || tableName; // if condition (custom query builder factory) is set then relationIdAttr.alias defined
const relation = relationIdAttr.relation; // "category.posts"
const inverseRelation = relation.inverseRelation; // "post.category"
const referenceColumnName = inverseRelation.joinColumns[0].referencedColumn!.propertyName; // post id
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.propertyPath; // "category" from "post.category"
const referenceColumnValues = rawEntities
.map(rawEntity => rawEntity[relationIdAttr.parentAlias + "_" + referenceColumnName])
.filter(value => !!value);
/*const idMaps = rawEntities.map(rawEntity => {
return this.createIdMap(relationIdAttr.relation.entityMetadata.primaryColumns, relationIdAttr.parentAlias, rawEntity);
});*/
const parameters: ObjectLiteral = {};
const condition = rawEntities.map((rawEntity, index) => {
return joinColumns.map(joinColumn => {
const parameterName = joinColumn.databaseName + index;
parameters[parameterName] = rawEntity[relationIdAttr.parentAlias + "_" + joinColumn.referencedColumn!.databaseName];
return tableAlias + "." + joinColumn.databaseName + " = :" + parameterName;
}).join(" AND ");
}).map(condition => "(" + condition + ")")
.join(" OR ");
// ensure we won't perform redundant queries for joined data which was not found in selection
// example: if post.category was not found in db then no need to execute query for category.imageIds
if (referenceColumnValues.length === 0)
if (!condition)
return { relationIdAttribute: relationIdAttr, results: [] };
// const joinParameters: ObjectLiteral = {};
/*const joinCondition = idMaps.map((idMap, idMapIndex) => {
return "(" + Object.keys(idMap).map((idName, idIndex) => {
const parameterName = `var${idMapIndex}_${idIndex}`;
joinParameters[parameterName] = idMap[idName];
return `${inverseSidePropertyName}.${idName} = :${parameterName}`
}).join(" AND ") + ")";
}).join(" OR ");*/
// generate query:
// SELECT post.id AS id, category.id AS parentId FROM post post INNER JOIN category category ON category.id=post.category AND category.id IN [:categoryIds]
// SELECT category.id, category.postId FROM category category ON category.postId = :postId
const qb = new QueryBuilder(this.connection, this.queryRunnerProvider);
qb.select(inverseSideTableAlias + "." + inverseSidePropertyName, "manyToManyId");
inverseRelation.entityMetadata.primaryColumns.forEach(primaryColumn => {
qb.addSelect(inverseSideTableAlias + "." + primaryColumn.databaseName, inverseSideTableAlias + "_" + primaryColumn.databaseName);
joinColumns.forEach(joinColumn => {
qb.addSelect(tableAlias + "." + joinColumn.databaseName, joinColumn.databaseName);
});
qb.from(inverseSideTable, inverseSideTableAlias)
.where(inverseSideTableAlias + "." + inverseSidePropertyName + " IN (:ids)")
.setParameter("ids", referenceColumnValues);
inverseRelation.entityMetadata.primaryColumns.forEach(primaryColumn => {
qb.addSelect(tableAlias + "." + primaryColumn.databaseName, primaryColumn.databaseName);
});
qb.from(table, tableAlias)
.where("(" + condition + ")") // need brackets because if we have additional condition and no brackets, it looks like (a = 1) OR (a = 2) AND b = 1, that is incorrect
.setParameters(parameters);
// apply condition (custom query builder factory)
if (relationIdAttr.queryBuilderFactory)
relationIdAttr.queryBuilderFactory(qb);
const relationIdRawResults: any[] = await qb.getRawMany();
const results: { id: any[], parentId: any, manyToManyId?: any }[] = [];
relationIdRawResults.forEach(rawResult => {
let result = results.find(result => result.manyToManyId === rawResult["manyToManyId"]);
if (!result) {
result = { id: [], parentId: "", manyToManyId: rawResult["manyToManyId"] };
results.push(result);
}
if (inverseRelation.entityMetadata.primaryColumns.length === 1) {
result.id.push(rawResult[inverseSideTableAlias + "_" + inverseRelation.entityMetadata.firstPrimaryColumn.databaseName]);
} else {
result.id.push(inverseRelation.entityMetadata.primaryColumns.reduce((ids, primaryColumn) => {
ids[primaryColumn.propertyName] = rawResult[inverseSideTableAlias + "_" + primaryColumn.databaseName];
return ids;
}, {} as ObjectLiteral));
}
if (inverseRelation.isOneToOne) {
result.id = result.id[0];
}
});
return {
relationIdAttribute: relationIdAttr,
results: results
results: await qb.getRawMany()
};
} else {
@ -134,66 +103,60 @@ export class RelationIdLoader {
// inverse side: loadRelationIdAndMap("category.postIds", "category.posts")
// we expect it to load array of post ids
let joinTableColumnName: string;
let inverseJoinColumnName: string;
let firstJunctionColumn: ColumnMetadata;
let secondJunctionColumn: ColumnMetadata;
if (relationIdAttr.relation.isOwning) { // todo fix joinColumns[0]
joinTableColumnName = relationIdAttr.relation.joinColumns[0].referencedColumn!.databaseName;
inverseJoinColumnName = relationIdAttr.relation.joinColumns[0].referencedColumn!.databaseName;
firstJunctionColumn = relationIdAttr.relation.junctionEntityMetadata.columns[0];
secondJunctionColumn = relationIdAttr.relation.junctionEntityMetadata.columns[1];
} else {
joinTableColumnName = relationIdAttr.relation.inverseRelation.joinColumns[0].referencedColumn!.databaseName;
inverseJoinColumnName = relationIdAttr.relation.inverseRelation.joinColumns[0].referencedColumn!.databaseName;
firstJunctionColumn = relationIdAttr.relation.junctionEntityMetadata.columns[1];
secondJunctionColumn = relationIdAttr.relation.junctionEntityMetadata.columns[0];
}
const referenceColumnValues = rawEntities
.map(rawEntity => rawEntity[relationIdAttr.parentAlias + "_" + joinTableColumnName])
.filter(value => value);
// ensure we won't perform redundant queries for joined data which was not found in selection
// example: if post.category was not found in db then no need to execute query for category.imageIds
if (referenceColumnValues.length === 0)
return { relationIdAttribute: relationIdAttr, results: [] };
const relation = relationIdAttr.relation;
const joinColumns = relation.isOwning ? relation.joinColumns : relation.inverseRelation.inverseJoinColumns;
const inverseJoinColumns = relation.isOwning ? relation.inverseJoinColumns : relation.inverseRelation.joinColumns;
const junctionAlias = relationIdAttr.junctionAlias;
const inverseSideTableName = relationIdAttr.joinInverseSideMetadata.tableName;
const inverseSideTableAlias = relationIdAttr.alias || inverseSideTableName;
const junctionTableName = relationIdAttr.relation.junctionEntityMetadata.tableName;
const condition = junctionAlias + "." + firstJunctionColumn.propertyPath + " IN (" + referenceColumnValues + ")" +
" AND " + junctionAlias + "." + secondJunctionColumn.propertyPath + " = " + inverseSideTableAlias + "." + inverseJoinColumnName;
const junctionTableName = relation.junctionEntityMetadata.tableName;
const qb = new QueryBuilder(this.connection, this.queryRunnerProvider)
.select(inverseSideTableAlias + "." + inverseJoinColumnName, "id")
.addSelect(junctionAlias + "." + firstJunctionColumn.propertyPath, "manyToManyId")
.fromTable(inverseSideTableName, inverseSideTableAlias)
const mappedColumns = rawEntities.map(rawEntity => {
return joinColumns.reduce((map, joinColumn) => {
map[joinColumn.databaseName] = rawEntity[relationIdAttr.parentAlias + "_" + joinColumn.referencedColumn!.databaseName];
return map;
}, {} as ObjectLiteral);
});
// ensure we won't perform redundant queries for joined data which was not found in selection
// example: if post.category was not found in db then no need to execute query for category.imageIds
if (mappedColumns.length === 0)
return { relationIdAttribute: relationIdAttr, results: [] };
const joinColumnConditions = mappedColumns.map(mappedColumn => {
return Object.keys(mappedColumn).map(key => {
return junctionAlias + "." + key + " = " + mappedColumn[key];
}).join(" AND ");
});
const inverseJoinColumnCondition = inverseJoinColumns.map(joinColumn => {
return junctionAlias + "." + joinColumn.databaseName + " = " + inverseSideTableAlias + "." + joinColumn.referencedColumn!.databaseName;
}).join(" AND ");
const condition = joinColumnConditions.map(condition => {
return "(" + condition + " AND " + inverseJoinColumnCondition + ")";
}).join(" OR ");
const qb = new QueryBuilder(this.connection, this.queryRunnerProvider);
inverseJoinColumns.forEach(joinColumn => {
qb.addSelect(junctionAlias + "." + joinColumn.databaseName, joinColumn.databaseName);
});
joinColumns.forEach(joinColumn => {
qb.addSelect(junctionAlias + "." + joinColumn.databaseName, joinColumn.databaseName);
});
qb.fromTable(inverseSideTableName, inverseSideTableAlias)
.innerJoin(junctionTableName, junctionAlias, condition);
// apply condition (custom query builder factory)
if (relationIdAttr.queryBuilderFactory)
relationIdAttr.queryBuilderFactory(qb);
const relationIdRawResults: any[] = await qb.getRawMany();
const results: { id: any[], parentId: any, manyToManyId?: any }[] = [];
relationIdRawResults.forEach(rawResult => {
let result = results.find(result => result.manyToManyId === rawResult["manyToManyId"]);
if (!result) {
result = { id: [], parentId: "", manyToManyId: rawResult["manyToManyId"] };
results.push(result);
}
result.id.push(rawResult["id"]);
});
return {
relationIdAttribute: relationIdAttr,
results: results
results: await qb.getRawMany()
};
}
});

View File

@ -0,0 +1,218 @@
import {RelationIdAttribute} from "./RelationIdAttribute";
import {ColumnMetadata} from "../../metadata/ColumnMetadata";
import {QueryBuilder} from "../QueryBuilder";
import {Connection} from "../../connection/Connection";
import {QueryRunnerProvider} from "../../query-runner/QueryRunnerProvider";
import {RelationIdLoadResult} from "./RelationIdLoadResult";
import {ObjectLiteral} from "../../common/ObjectLiteral";
export class RelationIdLoaderOld {
// -------------------------------------------------------------------------
// Constructor
// -------------------------------------------------------------------------
constructor(protected connection: Connection,
protected queryRunnerProvider: QueryRunnerProvider|undefined,
protected relationIdAttributes: RelationIdAttribute[]) {
}
// -------------------------------------------------------------------------
// Public Methods
// -------------------------------------------------------------------------
async load(rawEntities: any[]): Promise<RelationIdLoadResult[]> {
const promises = this.relationIdAttributes.map(async relationIdAttr => {
if (relationIdAttr.relation.isManyToOne || relationIdAttr.relation.isOneToOneOwner) {
// example: Post and Tag
// loadRelationIdAndMap("post.tagId", "post.tag") post_tag
// we expect it to load id of tag
if (relationIdAttr.queryBuilderFactory)
throw new Error(""); // todo: fix
const results = rawEntities.map(rawEntity => {
return {
id: rawEntity[relationIdAttr.parentAlias + "_" + relationIdAttr.relation.name],
parentId: this.createIdMap(relationIdAttr.relation.entityMetadata.primaryColumns, relationIdAttr.parentAlias, rawEntity)
};
});
return {
relationIdAttribute: relationIdAttr,
results: results
};
} else if (relationIdAttr.relation.isOneToMany || relationIdAttr.relation.isOneToOneNotOwner) {
// example: Post and Category
// loadRelationIdAndMap("category.postIds", "category.posts")
// we expect it to load array of post ids
// todo: take post ids - they can be multiple
// todo: create test with multiple primary columns usage
const relation = relationIdAttr.relation; // "category.posts"
const inverseRelation = relation.inverseRelation; // "post.category"
const referenceColumnName = inverseRelation.joinColumns[0].referencedColumn!.propertyName; // post id
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.propertyPath; // "category" from "post.category"
const referenceColumnValues = rawEntities
.map(rawEntity => rawEntity[relationIdAttr.parentAlias + "_" + referenceColumnName])
.filter(value => !!value);
/*const idMaps = rawEntities.map(rawEntity => {
return this.createIdMap(relationIdAttr.relation.entityMetadata.primaryColumns, relationIdAttr.parentAlias, rawEntity);
});*/
// ensure we won't perform redundant queries for joined data which was not found in selection
// example: if post.category was not found in db then no need to execute query for category.imageIds
if (referenceColumnValues.length === 0)
return { relationIdAttribute: relationIdAttr, results: [] };
// const joinParameters: ObjectLiteral = {};
/*const joinCondition = idMaps.map((idMap, idMapIndex) => {
return "(" + Object.keys(idMap).map((idName, idIndex) => {
const parameterName = `var${idMapIndex}_${idIndex}`;
joinParameters[parameterName] = idMap[idName];
return `${inverseSidePropertyName}.${idName} = :${parameterName}`
}).join(" AND ") + ")";
}).join(" OR ");*/
// generate query:
// SELECT post.id AS id, category.id AS parentId FROM post post INNER JOIN category category ON category.id=post.category AND category.id IN [:categoryIds]
const qb = new QueryBuilder(this.connection, this.queryRunnerProvider);
qb.select(inverseSideTableAlias + "." + inverseSidePropertyName, "manyToManyId");
inverseRelation.entityMetadata.primaryColumns.forEach(primaryColumn => {
qb.addSelect(inverseSideTableAlias + "." + primaryColumn.databaseName, inverseSideTableAlias + "_" + primaryColumn.databaseName);
});
qb.from(inverseSideTable, inverseSideTableAlias)
.where(inverseSideTableAlias + "." + inverseSidePropertyName + " IN (:ids)")
.setParameter("ids", referenceColumnValues);
// apply condition (custom query builder factory)
if (relationIdAttr.queryBuilderFactory)
relationIdAttr.queryBuilderFactory(qb);
const relationIdRawResults: any[] = await qb.getRawMany();
const results: { id: any[], parentId: any, manyToManyId?: any }[] = [];
relationIdRawResults.forEach(rawResult => {
let result = results.find(result => result.manyToManyId === rawResult["manyToManyId"]);
if (!result) {
result = { id: [], parentId: "", manyToManyId: rawResult["manyToManyId"] };
results.push(result);
}
if (inverseRelation.entityMetadata.primaryColumns.length === 1) {
result.id.push(rawResult[inverseSideTableAlias + "_" + inverseRelation.entityMetadata.firstPrimaryColumn.databaseName]);
} else {
result.id.push(inverseRelation.entityMetadata.primaryColumns.reduce((ids, primaryColumn) => {
ids[primaryColumn.propertyName] = rawResult[inverseSideTableAlias + "_" + primaryColumn.databaseName];
return ids;
}, {} as ObjectLiteral));
}
if (inverseRelation.isOneToOne) {
result.id = result.id[0];
}
});
return {
relationIdAttribute: relationIdAttr,
results: results
};
} else {
// example: Post and Category
// owner side: loadRelationIdAndMap("post.categoryIds", "post.categories")
// inverse side: loadRelationIdAndMap("category.postIds", "category.posts")
// we expect it to load array of post ids
let joinTableColumnName: string;
let inverseJoinColumnName: string;
let firstJunctionColumn: ColumnMetadata;
let secondJunctionColumn: ColumnMetadata;
if (relationIdAttr.relation.isOwning) { // todo fix joinColumns[0]
joinTableColumnName = relationIdAttr.relation.joinColumns[0].referencedColumn!.databaseName;
inverseJoinColumnName = relationIdAttr.relation.joinColumns[0].referencedColumn!.databaseName;
firstJunctionColumn = relationIdAttr.relation.junctionEntityMetadata.columns[0];
secondJunctionColumn = relationIdAttr.relation.junctionEntityMetadata.columns[1];
} else {
joinTableColumnName = relationIdAttr.relation.inverseRelation.joinColumns[0].referencedColumn!.databaseName;
inverseJoinColumnName = relationIdAttr.relation.inverseRelation.joinColumns[0].referencedColumn!.databaseName;
firstJunctionColumn = relationIdAttr.relation.junctionEntityMetadata.columns[1];
secondJunctionColumn = relationIdAttr.relation.junctionEntityMetadata.columns[0];
}
const referenceColumnValues = rawEntities
.map(rawEntity => rawEntity[relationIdAttr.parentAlias + "_" + joinTableColumnName])
.filter(value => value);
// ensure we won't perform redundant queries for joined data which was not found in selection
// example: if post.category was not found in db then no need to execute query for category.imageIds
if (referenceColumnValues.length === 0)
return { relationIdAttribute: relationIdAttr, results: [] };
const junctionAlias = relationIdAttr.junctionAlias;
const inverseSideTableName = relationIdAttr.joinInverseSideMetadata.tableName;
const inverseSideTableAlias = relationIdAttr.alias || inverseSideTableName;
const junctionTableName = relationIdAttr.relation.junctionEntityMetadata.tableName;
const condition = junctionAlias + "." + firstJunctionColumn.propertyPath + " IN (" + referenceColumnValues + ")" +
" AND " + junctionAlias + "." + secondJunctionColumn.propertyPath + " = " + inverseSideTableAlias + "." + inverseJoinColumnName;
/* const firstJunctionColumnConditions = firstJunctionColumns.map(joinColumn => {
return `${junctionAlias}.${joinColumn.propertyPath} = :${joinColumn.propertyPath}`;
});
const secondJunctionColumnConditions = secondJunctionColumns.map(joinColumn => {
return `${junctionAlias}.${joinColumn.propertyPath} = :${joinColumn.propertyPath}`;
});*/
const qb = new QueryBuilder(this.connection, this.queryRunnerProvider)
.select(inverseSideTableAlias + "." + inverseJoinColumnName, "id")
.addSelect(junctionAlias + "." + firstJunctionColumn.propertyPath, "manyToManyId")
.fromTable(inverseSideTableName, inverseSideTableAlias)
.innerJoin(junctionTableName, junctionAlias, condition);
// apply condition (custom query builder factory)
if (relationIdAttr.queryBuilderFactory)
relationIdAttr.queryBuilderFactory(qb);
const relationIdRawResults: any[] = await qb.getRawMany();
const results: { id: any[], parentId: any, manyToManyId?: any }[] = [];
relationIdRawResults.forEach(rawResult => {
let result = results.find(result => result.manyToManyId === rawResult["manyToManyId"]);
if (!result) {
result = { id: [], parentId: "", manyToManyId: rawResult["manyToManyId"] };
results.push(result);
}
result.id.push(rawResult["id"]);
});
return {
relationIdAttribute: relationIdAttr,
results: results
};
}
});
return Promise.all(promises);
}
protected createIdMap(columns: ColumnMetadata[], parentAlias: string, rawEntity: any) {
return columns.reduce((idMap, primaryColumn) => {
idMap[primaryColumn.propertyName] = rawEntity[parentAlias + "_" + primaryColumn.databaseName];
return idMap;
}, {} as ObjectLiteral);
}
}

View File

@ -5,6 +5,7 @@ import {ColumnMetadata} from "../../metadata/ColumnMetadata";
import {Alias} from "../Alias";
import {JoinAttribute} from "../JoinAttribute";
import {RelationCountLoadResult} from "../relation-count/RelationCountLoadResult";
import {RelationMetadata} from "../../metadata/RelationMetadata";
/**
* Transforms raw sql results returned from the database into entity object.
@ -142,7 +143,9 @@ export class RawSqlResultsToEntityTransformer {
return;
const relation = rawRelationIdResult.relationIdAttribute.relation;
let idMap: any, referenceColumnValue: any;
let idMap: any;
let valueMap: ObjectLiteral;
if (relation.isManyToOne || relation.isOneToOneOwner) {
idMap = relation.entityMetadata.primaryColumns.reduce((idMap, primaryColumn) => {
idMap[primaryColumn.propertyName] = rawSqlResults[0][alias.name + "_" + primaryColumn.databaseName];
@ -150,26 +153,68 @@ export class RawSqlResultsToEntityTransformer {
}, {} as ObjectLiteral);
} else {
let referenceColumnName: string;
if (relation.isOneToMany || relation.isOneToOneNotOwner) { // todo: fix joinColumns[0]
referenceColumnName = relation.inverseRelation.joinColumns[0].referencedColumn!.databaseName;
if (relation.isOneToMany || relation.isOneToOneNotOwner) {
valueMap = this.createValueMapFromJoinColumns(relation, entity);
} else {
referenceColumnName = relation.isOwning ? relation.joinColumns[0].referencedColumn!.databaseName : relation.inverseRelation.joinColumns[0].referencedColumn!.databaseName;
valueMap = this.createValueMapFromJoinColumns(relation, entity);
}
referenceColumnValue = rawSqlResults[0][alias.name + "_" + referenceColumnName];
/* referenceColumnValue = rawSqlResults[0][alias.name + "_" + referenceColumnName];
if (referenceColumnValue === undefined || referenceColumnValue === null)
return;*/
if (valueMap === undefined || valueMap === null)
return;
}
rawRelationIdResult.results.forEach(result => {
if (result.parentId && !alias.metadata.compareIds(result.parentId, idMap))
const referencedColumnResults = rawRelationIdResult.results.map(result => {
/* if (result.parentId && !alias.metadata.compareIds(result.parentId, idMap))
return;
if (result.manyToManyId && result.manyToManyId !== referenceColumnValue)
return;*/
const entityPrimaryIds = this.extractEntityPrimaryIds(relation, result);
if (!alias.metadata.compareIds(entityPrimaryIds, valueMap))
return;
entity[rawRelationIdResult.relationIdAttribute.mapToPropertyPropertyName] = result.id;
hasData = true;
});
let joinColumns: ColumnMetadata[];
if (relation.isOneToMany || relation.isOneToOneNotOwner) {
joinColumns = relation.inverseEntityMetadata.primaryColumns.map(joinColumn => joinColumn);
} else {
if (relation.isManyToManyOwner) {
joinColumns = relation.inverseJoinColumns.map(joinColumn => joinColumn);
} else {
joinColumns = relation.inverseRelation.joinColumns.map(joinColumn => joinColumn);
}
}
return joinColumns.reduce((referencedColumnResult, joinColumn) => {
if (joinColumns.length > 1) {
if (relation.isOneToMany || relation.isOneToOneNotOwner) {
referencedColumnResult[joinColumn.propertyName] = result[joinColumn.databaseName];
} else {
referencedColumnResult[joinColumn.referencedColumn!.propertyName] = result[joinColumn.databaseName];
}
} else {
referencedColumnResult = result[joinColumn.databaseName];
}
return referencedColumnResult;
}, {} as ObjectLiteral);
}).filter(result => result);
const properties = rawRelationIdResult.relationIdAttribute.mapToPropertyPropertyPath.split(".");
const mapToProperty = (properties: string[], map: ObjectLiteral, value: any): any => {
const property = properties.shift();
if (property && properties.length === 0) {
map[property] = value;
return map;
} else if (property && properties.length > 0) {
mapToProperty(properties, map[property], value);
} else {
return map;
}
};
mapToProperty(properties, entity, referencedColumnResults);
hasData = true;
});
return hasData;
}
@ -204,4 +249,39 @@ export class RawSqlResultsToEntityTransformer {
return hasData;
}
private createValueMapFromJoinColumns(relation: RelationMetadata, entity: ObjectLiteral): ObjectLiteral {
let joinColumns: ColumnMetadata[];
if (relation.isOneToMany || relation.isOneToOneNotOwner) {
joinColumns = relation.inverseRelation.joinColumns.map(joinColumn => joinColumn);
} else {
if (relation.isOwning) {
joinColumns = relation.joinColumns.map(joinColumn => joinColumn);
} else {
joinColumns = relation.inverseRelation.inverseJoinColumns.map(joinColumn => joinColumn);
}
}
return joinColumns.reduce((valueMap, joinColumn) => {
valueMap[joinColumn.databaseName] = joinColumn.referencedColumn!.getEntityValue(entity);
return valueMap;
}, {} as ObjectLiteral);
}
private extractEntityPrimaryIds(relation: RelationMetadata, relationIdRawResult: any) {
let joinColumns: ColumnMetadata[];
if (relation.isOneToMany || relation.isOneToOneNotOwner) {
joinColumns = relation.inverseRelation.joinColumns.map(joinColumn => joinColumn);
} else {
if (relation.isOwning) {
joinColumns = relation.joinColumns.map(joinColumn => joinColumn);
} else {
joinColumns = relation.inverseRelation.inverseJoinColumns.map(joinColumn => joinColumn);
}
}
return joinColumns.reduce((data, joinColumn) => {
data[joinColumn.databaseName] = relationIdRawResult[joinColumn.databaseName];
return data;
}, {} as ObjectLiteral);
}
}

View File

@ -6,6 +6,7 @@ import {Subject} from "../persistence/Subject";
import {RelationMetadata} from "../metadata/RelationMetadata";
import {ColumnMetadata} from "../metadata/ColumnMetadata";
import {QueryBuilder} from "../query-builder/QueryBuilder";
import {OrmUtils} from "../util/OrmUtils";
/**
* Repository for more specific operations.
@ -439,6 +440,8 @@ export class SpecificRepository<Entity extends ObjectLiteral> {
const ec = (column: string) => this.connection.driver.escapeColumnName(column);
let ids: any[] = [];
console.log("entityOrEntities:", entityOrEntities);
// console.log("entityIds:", entityIds);
const promises = (entityIds as any[]).map((entityId: any) => {
const qb = new QueryBuilder(this.connection, this.queryRunnerProvider);
inverseEntityColumnNames.forEach(columnName => {
@ -460,12 +463,14 @@ export class SpecificRepository<Entity extends ObjectLiteral> {
// if (notInIds && notInIds.length > 0)
// qb.andWhere(ea("junction") + "." + ec(inverseEntityColumnNames.fullName) + " NOT IN (:notInIds)", {notInIds: notInIds});
// console.log(qb.getSql());
return qb.getRawMany()
.then((results: any[]) => {
// console.log(results);
results.forEach(result => {
ids.push(Object.keys(result).reduce((id, key) => {
const junctionColumnName = inverseEntityColumns.find(joinColumn => joinColumn.databaseName === key)!;
id[junctionColumnName.referencedColumn!.propertyName] = result[key];
OrmUtils.mergeDeep(id, junctionColumnName.referencedColumn!.createValueMap(result[key]));
return id;
}, {} as ObjectLiteral));
}); // todo: prepare result?
@ -490,7 +495,7 @@ export class SpecificRepository<Entity extends ObjectLiteral> {
} else {
if (entityOrEntities instanceof Object) {
return columns.reduce((ids, column) => {
ids[column.databaseName] = entityOrEntities[column.propertyName];
ids[column.databaseName] = column.getEntityValue(entityOrEntities);
return ids;
}, {} as ObjectLiteral);
} else {

View File

@ -0,0 +1,39 @@
import "reflect-metadata";
import * as chai from "chai";
import {Connection} from "../../../src/connection/Connection";
import {closeTestingConnections, createTestingConnections, reloadTestingDatabases} from "../../utils/test-utils";
import {Post} from "./entity/Post";
const should = chai.should();
describe.skip("benchmark > bulk-save", () => {
let connections: Connection[];
before(async () => connections = await createTestingConnections({
entities: [__dirname + "/entity/*{.js,.ts}"],
schemaCreate: true,
dropSchemaOnConnection: true,
}));
beforeEach(() => reloadTestingDatabases(connections));
after(() => closeTestingConnections(connections));
it("testing bulk save of 100 objects", () => Promise.all(connections.map(async connection => {
const posts: Post[] = [];
for(let i = 1; i <= 100; i++) {
const post = new Post();
post.title = `Post #${i}`;
post.text = `Post #${i} text`;
post.likesCount = i;
post.commentsCount = i;
post.watchesCount = i;
posts.push(post);
}
await connection.manager.persist(posts);
})));
});

View File

@ -0,0 +1,26 @@
import {Entity} from "../../../../src/decorator/entity/Entity";
import {PrimaryGeneratedColumn} from "../../../../src/decorator/columns/PrimaryGeneratedColumn";
import {Column} from "../../../../src/decorator/columns/Column";
@Entity()
export class Post {
@PrimaryGeneratedColumn()
id: number;
@Column()
title: string;
@Column({ type: "text" })
text: string;
@Column({ type: "int" })
likesCount: number;
@Column({ type: "int" })
commentsCount: number;
@Column({ type: "int" })
watchesCount: number;
}

View File

@ -24,27 +24,27 @@ describe("decorators > relation-id-decorator > many-to-many", () => {
const category1 = new Category();
category1.name = "kids";
await connection.entityManager.persist(category1);
await connection.manager.persist(category1);
const category2 = new Category();
category2.name = "future";
await connection.entityManager.persist(category2);
await connection.manager.persist(category2);
const category3 = new Category();
category3.name = "cars";
await connection.entityManager.persist(category3);
await connection.manager.persist(category3);
const post = new Post();
post.title = "about kids";
post.categories = [category1, category2];
await connection.entityManager.persist(post);
await connection.manager.persist(post);
const post2 = new Post();
post2.title = "about BMW";
post2.categories = [category3];
await connection.entityManager.persist(post2);
await connection.manager.persist(post2);
let loadedPosts = await connection.entityManager
let loadedPosts = await connection.manager
.createQueryBuilder(Post, "post")
.getMany();
@ -54,7 +54,7 @@ describe("decorators > relation-id-decorator > many-to-many", () => {
expect(loadedPosts![1].categoryIds).to.not.be.empty;
expect(loadedPosts![1].categoryIds[0]).to.be.equal(3);
let loadedPost = await connection.entityManager
let loadedPost = await connection.manager
.createQueryBuilder(Post, "post")
.where("post.id = :id", { id: post.id })
.getOne();
@ -69,29 +69,29 @@ describe("decorators > relation-id-decorator > many-to-many", () => {
const category1 = new Category();
category1.name = "kids";
await connection.entityManager.persist(category1);
await connection.manager.persist(category1);
const category2 = new Category();
category2.name = "future";
category2.isRemoved = true;
await connection.entityManager.persist(category2);
await connection.manager.persist(category2);
const category3 = new Category();
category3.name = "cars";
category3.isRemoved = true;
await connection.entityManager.persist(category3);
await connection.manager.persist(category3);
const post = new Post();
post.title = "about kids";
post.categories = [category1, category2];
await connection.entityManager.persist(post);
await connection.manager.persist(post);
const post2 = new Post();
post2.title = "about BMW";
post2.categories = [category3];
await connection.entityManager.persist(post2);
await connection.manager.persist(post2);
let loadedPosts = await connection.entityManager
let loadedPosts = await connection.manager
.createQueryBuilder(Post, "post")
.getMany();
@ -101,7 +101,7 @@ describe("decorators > relation-id-decorator > many-to-many", () => {
expect(loadedPosts![1].removedCategoryIds).to.not.be.empty;
expect(loadedPosts![1].removedCategoryIds[0]).to.be.equal(3);
let loadedPost = await connection.entityManager
let loadedPost = await connection.manager
.createQueryBuilder(Post, "post")
.where("post.id = :id", { id: 1 })
.getOne();
@ -116,18 +116,18 @@ describe("decorators > relation-id-decorator > many-to-many", () => {
const category1 = new Category();
category1.name = "kids";
await connection.entityManager.persist(category1);
await connection.manager.persist(category1);
const category2 = new Category();
category2.name = "future";
await connection.entityManager.persist(category2);
await connection.manager.persist(category2);
const post = new Post();
post.title = "about kids";
post.subcategories = [category1, category2];
await connection.entityManager.persist(post);
await connection.manager.persist(post);
let loadedPost = await connection.entityManager
let loadedPost = await connection.manager
.createQueryBuilder(Post, "post")
.where("post.id = :id", { id: post.id })
.getOne();
@ -142,19 +142,19 @@ describe("decorators > relation-id-decorator > many-to-many", () => {
const category1 = new Category();
category1.name = "kids";
await connection.entityManager.persist(category1);
await connection.manager.persist(category1);
const category2 = new Category();
category2.name = "future";
category2.isRemoved = true;
await connection.entityManager.persist(category2);
await connection.manager.persist(category2);
const post = new Post();
post.title = "about kids";
post.subcategories = [category1, category2];
await connection.entityManager.persist(post);
await connection.manager.persist(post);
let loadedPost = await connection.entityManager
let loadedPost = await connection.manager
.createQueryBuilder(Post, "post")
.where("post.id = :id", { id: post.id })
.getOne();
@ -169,19 +169,19 @@ describe("decorators > relation-id-decorator > many-to-many", () => {
const category = new Category();
category.name = "cars";
await connection.entityManager.persist(category);
await connection.manager.persist(category);
const post1 = new Post();
post1.title = "about BMW";
post1.categories = [category];
await connection.entityManager.persist(post1);
await connection.manager.persist(post1);
const post2 = new Post();
post2.title = "about Audi";
post2.categories = [category];
await connection.entityManager.persist(post2);
await connection.manager.persist(post2);
let loadedCategory = await connection.entityManager
let loadedCategory = await connection.manager
.createQueryBuilder(Category, "category")
.where("category.id = :id", { id: category.id })
.getOne();
@ -196,20 +196,20 @@ describe("decorators > relation-id-decorator > many-to-many", () => {
const category = new Category();
category.name = "cars";
await connection.entityManager.persist(category);
await connection.manager.persist(category);
const post1 = new Post();
post1.title = "about BMW";
post1.categories = [category];
await connection.entityManager.persist(post1);
await connection.manager.persist(post1);
const post2 = new Post();
post2.title = "about Audi";
post2.isRemoved = true;
post2.categories = [category];
await connection.entityManager.persist(post2);
await connection.manager.persist(post2);
let loadedCategory = await connection.entityManager
let loadedCategory = await connection.manager
.createQueryBuilder(Category, "category")
.where("category.id = :id", { id: category.id })
.getOne();
@ -224,41 +224,41 @@ describe("decorators > relation-id-decorator > many-to-many", () => {
const image1 = new Image();
image1.name = "photo1";
await connection.entityManager.persist(image1);
await connection.manager.persist(image1);
const image2 = new Image();
image2.name = "photo2";
await connection.entityManager.persist(image2);
await connection.manager.persist(image2);
const image3 = new Image();
image3.name = "photo2";
await connection.entityManager.persist(image3);
await connection.manager.persist(image3);
const category1 = new Category();
category1.name = "cars";
category1.images = [image1, image2];
await connection.entityManager.persist(category1);
await connection.manager.persist(category1);
const category2 = new Category();
category2.name = "BMW";
await connection.entityManager.persist(category2);
await connection.manager.persist(category2);
const category3 = new Category();
category3.name = "Audi";
category3.images = [image3];
await connection.entityManager.persist(category3);
await connection.manager.persist(category3);
const post = new Post();
post.title = "about BMW";
post.categories = [category1, category2];
await connection.entityManager.persist(post);
await connection.manager.persist(post);
const post2 = new Post();
post2.title = "about Audi";
post2.categories = [category3];
await connection.entityManager.persist(post2);
await connection.manager.persist(post2);
let loadedPosts = await connection.entityManager
let loadedPosts = await connection.manager
.createQueryBuilder(Post, "post")
.leftJoinAndSelect("post.categories", "categories")
.addOrderBy("post.id, categories.id")
@ -281,7 +281,7 @@ describe("decorators > relation-id-decorator > many-to-many", () => {
expect(loadedPosts![1].categories[0].imageIds.length).to.be.equal(1);
expect(loadedPosts![1].categories[0].imageIds[0]).to.be.equal(3);
let loadedPost = await connection.entityManager
let loadedPost = await connection.manager
.createQueryBuilder(Post, "post")
.leftJoinAndSelect("post.categories", "categories")
.addOrderBy("post.id, categories.id")
@ -304,23 +304,23 @@ describe("decorators > relation-id-decorator > many-to-many", () => {
const image1 = new Image();
image1.name = "photo1";
await connection.entityManager.persist(image1);
await connection.manager.persist(image1);
const image2 = new Image();
image2.name = "photo2";
await connection.entityManager.persist(image2);
await connection.manager.persist(image2);
const category1 = new Category();
category1.name = "cars";
category1.images = [image1, image2];
await connection.entityManager.persist(category1);
await connection.manager.persist(category1);
const post = new Post();
post.title = "about BMW";
post.categories = [category1];
await connection.entityManager.persist(post);
await connection.manager.persist(post);
let loadedPost = await connection.entityManager
let loadedPost = await connection.manager
.createQueryBuilder(Post, "post")
.leftJoinAndSelect("post.categories", "categories", "categories.id = :categoryId")
.where("post.id = :id", { id: post.id })
@ -336,45 +336,45 @@ describe("decorators > relation-id-decorator > many-to-many", () => {
const image1 = new Image();
image1.name = "photo1";
await connection.entityManager.persist(image1);
await connection.manager.persist(image1);
const image2 = new Image();
image2.name = "photo2";
image2.isRemoved = true;
await connection.entityManager.persist(image2);
await connection.manager.persist(image2);
const image3 = new Image();
image3.name = "photo2";
image3.isRemoved = true;
await connection.entityManager.persist(image3);
await connection.manager.persist(image3);
const category1 = new Category();
category1.name = "cars";
category1.images = [image1, image2];
await connection.entityManager.persist(category1);
await connection.manager.persist(category1);
const category2 = new Category();
category2.name = "BMW";
category2.isRemoved = true;
await connection.entityManager.persist(category2);
await connection.manager.persist(category2);
const category3 = new Category();
category3.name = "BMW";
category3.isRemoved = true;
category3.images = [image3];
await connection.entityManager.persist(category3);
await connection.manager.persist(category3);
const post = new Post();
post.title = "about BMW";
post.categories = [category1, category2];
await connection.entityManager.persist(post);
await connection.manager.persist(post);
const post2 = new Post();
post2.title = "about BMW";
post2.categories = [category3];
await connection.entityManager.persist(post2);
await connection.manager.persist(post2);
let loadedPosts = await connection.entityManager
let loadedPosts = await connection.manager
.createQueryBuilder(Post, "post")
.leftJoinAndSelect("post.categories", "categories")
.addOrderBy("post.id, categories.id")
@ -395,7 +395,7 @@ describe("decorators > relation-id-decorator > many-to-many", () => {
expect(loadedPosts![1].categories[0].removedImageIds.length).to.be.equal(1);
expect(loadedPosts![1].categories[0].removedImageIds[0]).to.be.equal(3);
let loadedPost = await connection.entityManager
let loadedPost = await connection.manager
.createQueryBuilder(Post, "post")
.leftJoinAndSelect("post.categories", "categories")
.addOrderBy("post.id, categories.id")

View File

@ -1,7 +1,7 @@
import "reflect-metadata";
import * as chai from "chai";
import {expect} from "chai";
import {createTestingConnections, closeTestingConnections, reloadTestingDatabases} from "../../../../utils/test-utils";
import {closeTestingConnections, createTestingConnections, reloadTestingDatabases} from "../../../../utils/test-utils";
import {Connection} from "../../../../../src/connection/Connection";
import {Post} from "./entity/Post";
import {Category} from "./entity/Category";
@ -23,33 +23,33 @@ describe("decorators > relation-id-decorator > many-to-one", () => {
const category1 = new Category();
category1.name = "cars";
await connection.entityManager.persist(category1);
await connection.manager.persist(category1);
const category2 = new Category();
category2.name = "airplanes";
await connection.entityManager.persist(category2);
await connection.manager.persist(category2);
const categoryByName1 = new Category();
categoryByName1.name = "BMW";
await connection.entityManager.persist(categoryByName1);
await connection.manager.persist(categoryByName1);
const categoryByName2 = new Category();
categoryByName2.name = "Boeing";
await connection.entityManager.persist(categoryByName2);
await connection.manager.persist(categoryByName2);
const post1 = new Post();
post1.title = "about BWM";
post1.category = category1;
post1.categoryByName = categoryByName1;
await connection.entityManager.persist(post1);
await connection.manager.persist(post1);
const post2 = new Post();
post2.title = "about Boeing";
post2.category = category2;
post2.categoryByName = categoryByName2;
await connection.entityManager.persist(post2);
await connection.manager.persist(post2);
let loadedPosts = await connection.entityManager
let loadedPosts = await connection.manager
.createQueryBuilder(Post, "post")
.getMany();
@ -62,7 +62,7 @@ describe("decorators > relation-id-decorator > many-to-one", () => {
expect(loadedPosts![1].categoryName).to.not.be.empty;
expect(loadedPosts![1].categoryName).to.be.equal("Boeing");
let loadedPost = await connection.entityManager
let loadedPost = await connection.manager
.createQueryBuilder(Post, "post")
.where("post.id = :id", { id: 1 })
.getOne();

View File

@ -1,7 +1,7 @@
import "reflect-metadata";
import * as chai from "chai";
import {expect} from "chai";
import {createTestingConnections, closeTestingConnections, reloadTestingDatabases} from "../../../../utils/test-utils";
import {closeTestingConnections, createTestingConnections, reloadTestingDatabases} from "../../../../utils/test-utils";
import {Connection} from "../../../../../src/connection/Connection";
import {Category} from "./entity/Category";
import {Post} from "./entity/Post";
@ -23,28 +23,28 @@ describe("decorators > relation-id > one-to-many", () => {
const category = new Category();
category.name = "cars";
await connection.entityManager.persist(category);
await connection.manager.persist(category);
const category2 = new Category();
category2.name = "airplanes";
await connection.entityManager.persist(category2);
await connection.manager.persist(category2);
const post1 = new Post();
post1.title = "about BMW";
post1.category = category;
await connection.entityManager.persist(post1);
await connection.manager.persist(post1);
const post2 = new Post();
post2.title = "about Audi";
post2.category = category;
await connection.entityManager.persist(post2);
await connection.manager.persist(post2);
const post3 = new Post();
post3.title = "about Boeing";
post3.category = category2;
await connection.entityManager.persist(post3);
await connection.manager.persist(post3);
let loadedCategories = await connection.entityManager
let loadedCategories = await connection.manager
.createQueryBuilder(Category, "category")
.getMany();
@ -54,7 +54,7 @@ describe("decorators > relation-id > one-to-many", () => {
expect(loadedCategories![1].postIds.length).to.be.equal(1);
expect(loadedCategories![1].postIds[0]).to.be.equal(3);
let loadedCategory = await connection.entityManager
let loadedCategory = await connection.manager
.createQueryBuilder(Category, "category")
.where("category.id = :id", { id: 1 })
.getOne();
@ -68,30 +68,30 @@ describe("decorators > relation-id > one-to-many", () => {
const category = new Category();
category.name = "cars";
await connection.entityManager.persist(category);
await connection.manager.persist(category);
const category2 = new Category();
category2.name = "airplanes";
await connection.entityManager.persist(category2);
await connection.manager.persist(category2);
const post1 = new Post();
post1.title = "about BMW";
post1.category = category;
await connection.entityManager.persist(post1);
await connection.manager.persist(post1);
const post2 = new Post();
post2.title = "about Audi";
post2.category = category;
post2.isRemoved = true;
await connection.entityManager.persist(post2);
await connection.manager.persist(post2);
const post3 = new Post();
post3.title = "about Boeing";
post3.category = category2;
post3.isRemoved = true;
await connection.entityManager.persist(post3);
await connection.manager.persist(post3);
let loadedCategories = await connection.entityManager
let loadedCategories = await connection.manager
.createQueryBuilder(Category, "category")
.getMany();
@ -100,7 +100,7 @@ describe("decorators > relation-id > one-to-many", () => {
expect(loadedCategories![0].removedPostIds[0]).to.be.equal(2);
expect(loadedCategories![1].removedPostIds[0]).to.be.equal(3);
let loadedCategory = await connection.entityManager
let loadedCategory = await connection.manager
.createQueryBuilder(Category, "category")
.where("category.id = :id", { id: 1 })
.getOne();

View File

@ -1,7 +1,7 @@
import "reflect-metadata";
import * as chai from "chai";
import {expect} from "chai";
import {createTestingConnections, closeTestingConnections, reloadTestingDatabases} from "../../../../utils/test-utils";
import {closeTestingConnections, createTestingConnections, reloadTestingDatabases} from "../../../../utils/test-utils";
import {Connection} from "../../../../../src/connection/Connection";
import {Category} from "./entity/Category";
import {Post} from "./entity/Post";
@ -23,33 +23,33 @@ describe("decorators > relation-id > one-to-one", () => {
const category1 = new Category();
category1.name = "cars";
await connection.entityManager.persist(category1);
await connection.manager.persist(category1);
const category2 = new Category();
category2.name = "airplanes";
await connection.entityManager.persist(category2);
await connection.manager.persist(category2);
const categoryByName1 = new Category();
categoryByName1.name = "BMW";
await connection.entityManager.persist(categoryByName1);
await connection.manager.persist(categoryByName1);
const categoryByName2 = new Category();
categoryByName2.name = "Boeing";
await connection.entityManager.persist(categoryByName2);
await connection.manager.persist(categoryByName2);
const post1 = new Post();
post1.title = "about BMW";
post1.category = category1;
post1.categoryByName = categoryByName1;
await connection.entityManager.persist(post1);
await connection.manager.persist(post1);
const post2 = new Post();
post2.title = "about Boeing";
post2.category = category2;
post2.categoryByName = categoryByName2;
await connection.entityManager.persist(post2);
await connection.manager.persist(post2);
let loadedPosts = await connection.entityManager
let loadedPosts = await connection.manager
.createQueryBuilder(Post, "post")
.addOrderBy("post.id")
.getMany();
@ -63,7 +63,7 @@ describe("decorators > relation-id > one-to-one", () => {
expect(loadedPosts![1].categoryName).to.not.be.empty;
expect(loadedPosts![1].categoryName).to.be.equal("Boeing");
let loadedPost = await connection.entityManager
let loadedPost = await connection.manager
.createQueryBuilder(Post, "post")
.where("post.id = :id", { id: 1 })
.getOne();
@ -78,23 +78,23 @@ describe("decorators > relation-id > one-to-one", () => {
const category1 = new Category();
category1.name = "cars";
await connection.entityManager.persist(category1);
await connection.manager.persist(category1);
const category2 = new Category();
category2.name = "airplanes";
await connection.entityManager.persist(category2);
await connection.manager.persist(category2);
const post1 = new Post();
post1.title = "about BMW";
post1.category2 = category1;
await connection.entityManager.persist(post1);
await connection.manager.persist(post1);
const post2 = new Post();
post2.title = "about Boeing";
post2.category2 = category2;
await connection.entityManager.persist(post2);
await connection.manager.persist(post2);
let loadedCategories = await connection.entityManager
let loadedCategories = await connection.manager
.createQueryBuilder(Category, "category")
.addOrderBy("category.id")
.getMany();
@ -104,7 +104,7 @@ describe("decorators > relation-id > one-to-one", () => {
expect(loadedCategories![1].postId).to.not.be.empty;
expect(loadedCategories![1].postId).to.be.equal(2);
let loadedCategory = await connection.entityManager
let loadedCategory = await connection.manager
.createQueryBuilder(Category, "category")
.where("category.id = :id", { id: 1 })
.getOne();

View File

@ -20,7 +20,7 @@ describe.skip("embedded > embedded-many-to-many-case3", () => {
describe("owner side", () => {
it("should insert, load, update and remove entities with embeddeds when embedded entity having ManyToMany relation with multiple primary keys (one PK en each embed)", () => Promise.all(connections.map(async connection => {
it("should insert, load, update and remove entities with embeddeds when embedded entity having ManyToMany relation with multiple primary keys (one PK in each embed)", () => Promise.all(connections.map(async connection => {
const user1 = new User();
user1.name = "Alice";
@ -197,7 +197,7 @@ describe.skip("embedded > embedded-many-to-many-case3", () => {
describe("inverse side", () => {
it("should insert, load, update and remove entities with embeddeds when embedded entity having ManyToMany relation with multiple primary keys (one PK en each embed)", () => Promise.all(connections.map(async connection => {
it("should insert, load, update and remove entities with embeddeds when embedded entity having ManyToMany relation with multiple primary keys (one PK in each embed)", () => Promise.all(connections.map(async connection => {
const post1 = new Post();
post1.id = 1;

View File

@ -20,7 +20,7 @@ describe("embedded > embedded-many-to-one-case3", () => {
describe("owner side", () => {
it("should insert, load, update and remove entities with embeddeds when embedded entity having ManyToOne relation with multiple primary keys (one PK en each embed)", () => Promise.all(connections.map(async connection => {
it("should insert, load, update and remove entities with embeddeds when embedded entity having ManyToOne relation with multiple primary keys (one PK in each embed)", () => Promise.all(connections.map(async connection => {
const user1 = new User();
user1.name = "Alice";
@ -168,7 +168,7 @@ describe("embedded > embedded-many-to-one-case3", () => {
describe("inverse side", () => {
it("should insert, load, update and remove entities with embeddeds when embedded entity having ManyToOne relation with multiple primary keys (one PK en each embed)", () => Promise.all(connections.map(async connection => {
it("should insert, load, update and remove entities with embeddeds when embedded entity having ManyToOne relation with multiple primary keys (one PK in each embed)", () => Promise.all(connections.map(async connection => {
const post1 = new Post();
post1.id = 1;

View File

@ -0,0 +1,389 @@
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-case5", () => {
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 with multiple primary keys (multiple keys in both sides)", () => Promise.all(connections.map(async connection => {
const user1 = new User();
user1.id = 1;
user1.personId = 1;
user1.name = "Alice";
await connection.getRepository(User).persist(user1);
const user2 = new User();
user2.id = 2;
user2.personId = 2;
user2.name = "Bob";
await connection.getRepository(User).persist(user2);
const user3 = new User();
user3.id = 3;
user3.personId = 3;
user3.name = "Clara";
await connection.getRepository(User).persist(user3);
const postRepository = connection.getRepository(Post);
const post1 = new Post();
post1.id = 1;
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.id = 2;
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);
let loadedPosts = await connection.manager
.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, personId: 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, personId: 2, name: "Bob" },
subcounters: {
version: 1,
watches: 10
}
}
}
));
let loadedPost = await connection.manager
.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, personId: 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!);
loadedPost = await connection.manager
.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: 3,
likes: 3,
likedUser: { id: 3, personId: 3, name: "Clara" },
subcounters: {
version: 1,
watches: 6
}
}
}
));
await postRepository.remove(loadedPost!);
loadedPosts = (await postRepository.find())!;
expect(loadedPosts.length).to.be.equal(1);
expect(loadedPosts[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 with multiple primary keys (multiple keys in both sides)", () => Promise.all(connections.map(async connection => {
const post1 = new Post();
post1.id = 1;
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.id = 2;
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.id = 3;
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.id = 1;
user1.personId = 1;
user1.name = "Alice";
user1.likedPosts = [post1, post2];
await connection.getRepository(User).persist(user1);
const user2 = new User();
user2.id = 2;
user2.personId = 2;
user2.name = "Bob";
user2.likedPosts = [post3];
await connection.getRepository(User).persist(user2);
let loadedUsers = await connection.manager
.createQueryBuilder(User, "user")
.leftJoinAndSelect("user.likedPosts", "likedPost")
.orderBy("user.id, likedPost.id")
.getMany();
expect(loadedUsers[0].should.be.eql(
{
id: 1,
personId: 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,
personId: 2,
name: "Bob",
likedPosts: [
{
id: 3,
title: "About horses",
counters: {
code: 3,
comments: 5,
favorites: 10,
likes: 15,
subcounters: {
version: 1,
watches: 30
}
}
}
]
}
));
let loadedUser = await connection.manager
.createQueryBuilder(User, "user")
.leftJoinAndSelect("user.likedPosts", "likedPost")
.orderBy("likedPost.id")
.where("user.id = :id", { id: 1 })
.getOne();
expect(loadedUser!.should.be.eql(
{
id: 1,
personId: 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!);
loadedUser = await connection.manager
.createQueryBuilder(User, "user")
.leftJoinAndSelect("user.likedPosts", "likedPost")
.orderBy("likedPost.id")
.where("user.id = :id", { id: 1 })
.getOne();
expect(loadedUser!.should.be.eql(
{
id: 1,
personId: 1,
name: "Anna",
likedPosts: [
{
id: 1,
title: "About cars",
counters: {
code: 1,
comments: 1,
favorites: 2,
likes: 3,
subcounters: {
version: 1,
watches: 5
}
}
}
]
}
));
const loadedPost = await connection.manager
.createQueryBuilder(Post, "post")
.leftJoinAndSelect("post.counters.likedUser", "likedUser")
.where("post.id = :id", { id: 2 })
.getOne();
expect(loadedPost!.counters.likedUser).to.be.empty;
})));
});
});

View File

@ -0,0 +1,30 @@
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 {PrimaryColumn} from "../../../../../src/decorator/columns/PrimaryColumn";
import {User} from "./User";
import {Subcounters} from "./Subcounters";
export class Counters {
@PrimaryColumn()
code: number;
@Column()
likes: number;
@Column()
comments: number;
@Column()
favorites: number;
@Embedded(() => Subcounters)
subcounters: Subcounters;
@ManyToOne(type => User)
@JoinColumn()
likedUser: User;
}

View File

@ -0,0 +1,21 @@
import {Entity} from "../../../../../src/decorator/entity/Entity";
import {Column} from "../../../../../src/decorator/columns/Column";
import {Embedded} from "../../../../../src/decorator/Embedded";
import {Counters} from "./Counters";
import {Index} from "../../../../../src/decorator/Index";
import {PrimaryColumn} from "../../../../../src/decorator/columns/PrimaryColumn";
@Entity()
@Index(["id", "counters.code", "counters.subcounters.version"])
export class Post {
@PrimaryColumn()
id: number;
@Column()
title: string;
@Embedded(() => Counters)
counters: Counters;
}

View File

@ -0,0 +1,12 @@
import {Column} from "../../../../../src/decorator/columns/Column";
import {PrimaryColumn} from "../../../../../src/decorator/columns/PrimaryColumn";
export class Subcounters {
@PrimaryColumn()
version: number;
@Column()
watches: number;
}

View File

@ -0,0 +1,24 @@
import {Entity} from "../../../../../src/decorator/entity/Entity";
import {OneToMany} from "../../../../../src/decorator/relations/OneToMany";
import {Index} from "../../../../../src/decorator/Index";
import {PrimaryColumn} from "../../../../../src/decorator/columns/PrimaryColumn";
import {Column} from "../../../../../src/decorator/columns/Column";
import {Post} from "./Post";
@Entity()
@Index(["id", "personId"])
export class User {
@PrimaryColumn()
id: number;
@PrimaryColumn()
personId: number;
@Column()
name: string;
@OneToMany(type => Post, post => post.counters.likedUser)
likedPosts: Post[];
}

View File

@ -216,7 +216,7 @@ describe("embedded > embedded-one-to-one", () => {
user2.likedPost = post2;
await connection.getRepository(User).persist(user2);
const loadedUsers = await connection.manager
let loadedUsers = await connection.manager
.createQueryBuilder(User, "user")
.leftJoinAndSelect("user.likedPost", "likedPost")
.orderBy("user.id")
@ -263,7 +263,7 @@ describe("embedded > embedded-one-to-one", () => {
}
));
const loadedUser = await connection.manager
let loadedUser = await connection.manager
.createQueryBuilder(User, "user")
.leftJoinAndSelect("user.likedPost", "likedPost")
.where("user.id = :id", { id: 1 })
@ -292,15 +292,16 @@ describe("embedded > embedded-one-to-one", () => {
loadedUser!.name = "Anna";
loadedUser!.likedPost = post3;
console.log(loadedUser);
await connection.getRepository(User).persist(loadedUser!);
const loadedUser2 = await connection.manager
loadedUser = await connection.manager
.createQueryBuilder(User, "user")
.leftJoinAndSelect("user.likedPost", "likedPost")
.where("user.id = :id", { id: 1 })
.getOne();
expect(loadedUser2!.should.be.eql(
expect(loadedUser!.should.be.eql(
{
id: 1,
name: "Anna",
@ -321,11 +322,11 @@ describe("embedded > embedded-one-to-one", () => {
}
));
await connection.getRepository(User).remove(loadedUser2!);
await connection.getRepository(User).remove(loadedUser!);
const loadedUsers2 = (await connection.getRepository(User).find())!;
expect(loadedUsers2.length).to.be.equal(1);
expect(loadedUsers2[0].name).to.be.equal("Bob");
loadedUsers = (await connection.getRepository(User).find())!;
expect(loadedUsers.length).to.be.equal(1);
expect(loadedUsers[0].name).to.be.equal("Bob");
})));
});
});

View File

@ -0,0 +1,80 @@
import "reflect-metadata";
import {Post} from "./entity/Post";
import {Counters} from "./entity/Counters";
import {Connection} from "../../../../src/connection/Connection";
import {closeTestingConnections, createTestingConnections, reloadTestingDatabases} from "../../../utils/test-utils";
import {Subcounters} from "../embedded-many-to-one-case2/entity/Subcounters";
describe.skip("embedded > embedded-with-special-columns", () => {
let connections: Connection[];
before(async () => connections = await createTestingConnections({
entities: [__dirname + "/entity/*{.js,.ts}"],
schemaCreate: true,
dropSchemaOnConnection: true,
}));
beforeEach(() => reloadTestingDatabases(connections));
after(() => closeTestingConnections(connections));
it("should insert, load, update and remove entities with embeddeds when embeds contains special columns (e.g. CreateDateColumn, UpdateDateColumn, VersionColumn", () => Promise.all(connections.map(async connection => {
const post1 = new Post();
post1.id = 1;
post1.title = "About cars";
post1.counters = new Counters();
post1.counters.comments = 1;
post1.counters.favorites = 2;
post1.counters.likes = 3;
post1.counters.subcounters = new Subcounters();
post1.counters.subcounters.watches = 5;
await connection.getRepository(Post).persist(post1);
const post2 = new Post();
post2.id = 2;
post2.title = "About airplanes";
post2.counters = new Counters();
post2.counters.comments = 2;
post2.counters.favorites = 3;
post2.counters.likes = 4;
post2.counters.subcounters = new Subcounters();
post2.counters.subcounters.watches = 10;
await connection.getRepository(Post).persist(post2);
let loadedPosts = await connection.manager
.createQueryBuilder(Post, "post")
.orderBy("post.id")
.getMany();
console.log(loadedPosts);
/*expect(loadedPosts[0].should.be.eql(
{
id: 1,
title: "About cars",
counters: {
comments: 1,
favorites: 2,
likes: 3,
subcounters: {
version: 1,
watches: 5
}
}
}
));
expect(loadedPosts[1].should.be.eql(
{
id: 2,
title: "About airplanes",
counters: {
comments: 2,
favorites: 3,
likes: 4,
subcounters: {
version: 1,
watches: 10
}
}
}
));*/
})));
});

View File

@ -0,0 +1,27 @@
import {Column} from "../../../../../src/decorator/columns/Column";
import {Embedded} from "../../../../../src/decorator/Embedded";
import {CreateDateColumn} from "../../../../../src/decorator/columns/CreateDateColumn";
import {UpdateDateColumn} from "../../../../../src/decorator/columns/UpdateDateColumn";
import {Subcounters} from "./Subcounters";
export class Counters {
@Column()
likes: number;
@Column()
comments: number;
@Column()
favorites: number;
@Embedded(() => Subcounters)
subcounters: Subcounters;
@CreateDateColumn()
createdDate: Date;
@UpdateDateColumn()
updatedDate: Date;
}

View File

@ -0,0 +1,19 @@
import {Entity} from "../../../../../src/decorator/entity/Entity";
import {PrimaryGeneratedColumn} from "../../../../../src/decorator/columns/PrimaryGeneratedColumn";
import {Column} from "../../../../../src/decorator/columns/Column";
import {Embedded} from "../../../../../src/decorator/Embedded";
import {Counters} from "./Counters";
@Entity()
export class Post {
@PrimaryGeneratedColumn()
id: number;
@Column()
title: string;
@Embedded(() => Counters)
counters: Counters;
}

View File

@ -0,0 +1,12 @@
import {Column} from "../../../../../src/decorator/columns/Column";
import {VersionColumn} from "../../../../../src/decorator/columns/VersionColumn";
export class Subcounters {
@VersionColumn()
version: number;
@Column()
watches: number;
}

View File

@ -1,8 +1,12 @@
import "reflect-metadata";
import * as chai from "chai";
import {expect} from "chai";
import {createTestingConnections, closeTestingConnections, reloadTestingDatabases} from "../../../../utils/test-utils";
import {Connection} from "../../../../../src/connection/Connection";
import {
closeTestingConnections,
createTestingConnections,
reloadTestingDatabases
} from "../../../../../utils/test-utils";
import {Connection} from "../../../../../../src/connection/Connection";
import {Tag} from "./entity/Tag";
import {Post} from "./entity/Post";
import {Category} from "./entity/Category";
@ -10,7 +14,7 @@ import {Image} from "./entity/Image";
const should = chai.should();
describe("query builder > load-relation-id-and-map > many-to-many", () => {
describe("query builder > relation-id > many-to-many > basic-functionality", () => {
let connections: Connection[];
before(async () => connections = await createTestingConnections({
@ -25,33 +29,33 @@ describe("query builder > load-relation-id-and-map > many-to-many", () => {
const tag = new Tag();
tag.name = "kids";
await connection.entityManager.persist(tag);
await connection.manager.persist(tag);
const category1 = new Category();
category1.name = "kids";
await connection.entityManager.persist(category1);
await connection.manager.persist(category1);
const category2 = new Category();
category2.name = "future";
await connection.entityManager.persist(category2);
await connection.manager.persist(category2);
const category3 = new Category();
category3.name = "cars";
await connection.entityManager.persist(category3);
await connection.manager.persist(category3);
const post = new Post();
post.title = "about kids";
post.categories = [category1, category2];
post.tag = tag;
await connection.entityManager.persist(post);
await connection.manager.persist(post);
const post2 = new Post();
post2.title = "about BMW";
post2.categories = [category3];
post2.tag = tag;
await connection.entityManager.persist(post2);
await connection.manager.persist(post2);
let loadedPosts = await connection.entityManager
let loadedPosts = await connection.manager
.createQueryBuilder(Post, "post")
.leftJoinAndSelect("post.tag", "tag")
.leftJoinAndSelect("post.categories", "categories")
@ -67,7 +71,7 @@ describe("query builder > load-relation-id-and-map > many-to-many", () => {
expect(loadedPosts![1].categories).to.not.be.empty;
expect(loadedPosts![1].categoryIds).to.be.empty;
let loadedPost = await connection.entityManager
let loadedPost = await connection.manager
.createQueryBuilder(Post, "post")
.leftJoinAndSelect("post.tag", "tag")
.leftJoinAndSelect("post.categories", "categories")
@ -86,23 +90,23 @@ describe("query builder > load-relation-id-and-map > many-to-many", () => {
const category1 = new Category();
category1.name = "kids";
await connection.entityManager.persist(category1);
await connection.manager.persist(category1);
const category2 = new Category();
category2.name = "future";
await connection.entityManager.persist(category2);
await connection.manager.persist(category2);
const post = new Post();
post.title = "about kids";
post.categories = [category1, category2];
await connection.entityManager.persist(post);
await connection.manager.persist(post);
const post2 = new Post();
post2.title = "about kids";
post2.categories = [category1, category2];
await connection.entityManager.persist(post2);
await connection.manager.persist(post2);
let loadedPosts = await connection.entityManager
let loadedPosts = await connection.manager
.createQueryBuilder(Post, "post")
.loadRelationIdAndMap("post.categoryIds", "post.categories")
.getMany();
@ -114,7 +118,7 @@ describe("query builder > load-relation-id-and-map > many-to-many", () => {
expect(loadedPosts![1].categoryIds[0]).to.be.equal(1);
expect(loadedPosts![1].categoryIds[1]).to.be.equal(2);
let loadedPost = await connection.entityManager
let loadedPost = await connection.manager
.createQueryBuilder(Post, "post")
.loadRelationIdAndMap("post.categoryIds", "post.categories")
.where("post.id = :id", { id: post.id })
@ -135,16 +139,16 @@ describe("query builder > load-relation-id-and-map > many-to-many", () => {
category2.name = "future";
await Promise.all([
connection.entityManager.persist(category1),
connection.entityManager.persist(category2)
connection.manager.persist(category1),
connection.manager.persist(category2)
]);
const post = new Post();
post.title = "about kids";
post.subcategories = [category1, category2];
await connection.entityManager.persist(post);
await connection.manager.persist(post);
let loadedPost = await connection.entityManager
let loadedPost = await connection.manager
.createQueryBuilder(Post, "post")
.loadRelationIdAndMap("post.categoryIds", "post.subcategories")
.where("post.id = :id", { id: post.id })
@ -160,7 +164,7 @@ describe("query builder > load-relation-id-and-map > many-to-many", () => {
const category = new Category();
category.name = "cars";
await connection.entityManager.persist(category);
await connection.manager.persist(category);
const post1 = new Post();
post1.title = "about BMW";
@ -171,11 +175,11 @@ describe("query builder > load-relation-id-and-map > many-to-many", () => {
post2.categories = [category];
await Promise.all([
connection.entityManager.persist(post1),
connection.entityManager.persist(post2)
connection.manager.persist(post1),
connection.manager.persist(post2)
]);
let loadedCategory = await connection.entityManager
let loadedCategory = await connection.manager
.createQueryBuilder(Category, "category")
.loadRelationIdAndMap("category.postIds", "category.posts")
.where("category.id = :id", { id: category.id })
@ -196,16 +200,16 @@ describe("query builder > load-relation-id-and-map > many-to-many", () => {
category2.name = "future";
await Promise.all([
connection.entityManager.persist(category1),
connection.entityManager.persist(category2)
connection.manager.persist(category1),
connection.manager.persist(category2)
]);
const post = new Post();
post.title = "about kids";
post.categories = [category1, category2];
await connection.entityManager.persist(post);
await connection.manager.persist(post);
let loadedPost = await connection.entityManager
let loadedPost = await connection.manager
.createQueryBuilder(Post, "post")
.loadRelationIdAndMap("post.categoryIds", "post.categories", "categories", qb => qb.andWhere("categories.id = :categoryId", { categoryId: 1 }))
.getOne();
@ -225,16 +229,16 @@ describe("query builder > load-relation-id-and-map > many-to-many", () => {
category2.name = "future";
await Promise.all([
connection.entityManager.persist(category1),
connection.entityManager.persist(category2)
connection.manager.persist(category1),
connection.manager.persist(category2)
]);
const post = new Post();
post.title = "about kids";
post.subcategories = [category1, category2];
await connection.entityManager.persist(post);
await connection.manager.persist(post);
let loadedPost = await connection.entityManager
let loadedPost = await connection.manager
.createQueryBuilder(Post, "post")
.loadRelationIdAndMap("post.categoryIds", "post.subcategories", "subCategories", qb => qb.andWhere("subCategories.id = :categoryId", { categoryId: 1 }))
.getOne();
@ -249,7 +253,7 @@ describe("query builder > load-relation-id-and-map > many-to-many", () => {
const category = new Category();
category.name = "cars";
await connection.entityManager.persist(category);
await connection.manager.persist(category);
const post1 = new Post();
post1.title = "about BMW";
@ -260,11 +264,11 @@ describe("query builder > load-relation-id-and-map > many-to-many", () => {
post2.categories = [category];
await Promise.all([
connection.entityManager.persist(post1),
connection.entityManager.persist(post2)
connection.manager.persist(post1),
connection.manager.persist(post2)
]);
let loadedCategory = await connection.entityManager
let loadedCategory = await connection.manager
.createQueryBuilder(Category, "category")
.loadRelationIdAndMap("category.postIds", "category.posts", "posts", qb => qb.andWhere("posts.id = :postId", { postId: 1 }))
.where("category.id = :id", { id: category.id })
@ -285,25 +289,25 @@ describe("query builder > load-relation-id-and-map > many-to-many", () => {
image2.name = "photo2";
await Promise.all([
connection.entityManager.persist(image1),
connection.entityManager.persist(image2)
connection.manager.persist(image1),
connection.manager.persist(image2)
]);
const category1 = new Category();
category1.name = "cars";
category1.images = [image1, image2];
await connection.entityManager.persist(category1);
await connection.manager.persist(category1);
const category2 = new Category();
category2.name = "BMW";
await connection.entityManager.persist(category2);
await connection.manager.persist(category2);
const post = new Post();
post.title = "about BMW";
post.categories = [category1, category2];
await connection.entityManager.persist(post);
await connection.manager.persist(post);
let loadedPost = await connection.entityManager
let loadedPost = await connection.manager
.createQueryBuilder(Post, "post")
.leftJoinAndSelect("post.categories", "categories")
.loadRelationIdAndMap("post.categoryIds", "post.categories")
@ -333,25 +337,25 @@ describe("query builder > load-relation-id-and-map > many-to-many", () => {
image2.name = "photo2";
await Promise.all([
connection.entityManager.persist(image1),
connection.entityManager.persist(image2)
connection.manager.persist(image1),
connection.manager.persist(image2)
]);
const category1 = new Category();
category1.name = "cars";
category1.images = [image1, image2];
await connection.entityManager.persist(category1);
await connection.manager.persist(category1);
const category2 = new Category();
category2.name = "BMW";
await connection.entityManager.persist(category2);
await connection.manager.persist(category2);
const post = new Post();
post.title = "about BMW";
post.categories = [category1, category2];
await connection.entityManager.persist(post);
await connection.manager.persist(post);
let loadedPost = await connection.entityManager
let loadedPost = await connection.manager
.createQueryBuilder(Post, "post")
.leftJoinAndSelect("post.categories", "categories")
.loadRelationIdAndMap("post.categoryIds", "post.categories", "categories2", qb => qb.andWhere("categories2.id = :categoryId", { categoryId: 1 }))
@ -379,21 +383,21 @@ describe("query builder > load-relation-id-and-map > many-to-many", () => {
image2.name = "photo2";
await Promise.all([
connection.entityManager.persist(image1),
connection.entityManager.persist(image2)
connection.manager.persist(image1),
connection.manager.persist(image2)
]);
const category1 = new Category();
category1.name = "cars";
category1.images = [image1, image2];
await connection.entityManager.persist(category1);
await connection.manager.persist(category1);
const post = new Post();
post.title = "about BMW";
post.categories = [category1];
await connection.entityManager.persist(post);
await connection.manager.persist(post);
let loadedPost = await connection.entityManager
let loadedPost = await connection.manager
.createQueryBuilder(Post, "post")
.leftJoinAndSelect("post.categories", "categories", "categories.id = :categoryId")
.loadRelationIdAndMap("categories.imageIds", "categories.images")

View File

@ -0,0 +1,29 @@
import {Entity} from "../../../../../../../src/decorator/entity/Entity";
import {PrimaryGeneratedColumn} from "../../../../../../../src/decorator/columns/PrimaryGeneratedColumn";
import {Column} from "../../../../../../../src/decorator/columns/Column";
import {ManyToMany} from "../../../../../../../src/decorator/relations/ManyToMany";
import {JoinTable} from "../../../../../../../src/decorator/relations/JoinTable";
import {Post} from "./Post";
import {Image} from "./Image";
@Entity()
export class Category {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@ManyToMany(type => Post, post => post.categories)
posts: Post[];
@ManyToMany(type => Image)
@JoinTable()
images: Image[];
imageIds: number[];
postIds: number[];
}

View File

@ -0,0 +1,14 @@
import {Entity} from "../../../../../../../src/decorator/entity/Entity";
import {PrimaryGeneratedColumn} from "../../../../../../../src/decorator/columns/PrimaryGeneratedColumn";
import {Column} from "../../../../../../../src/decorator/columns/Column";
@Entity()
export class Image {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
}

View File

@ -0,0 +1,34 @@
import {ManyToMany} from "../../../../../../../src/decorator/relations/ManyToMany";
import {Entity} from "../../../../../../../src/decorator/entity/Entity";
import {PrimaryGeneratedColumn} from "../../../../../../../src/decorator/columns/PrimaryGeneratedColumn";
import {Column} from "../../../../../../../src/decorator/columns/Column";
import {ManyToOne} from "../../../../../../../src/decorator/relations/ManyToOne";
import {JoinTable} from "../../../../../../../src/decorator/relations/JoinTable";
import {Category} from "./Category";
import {Tag} from "./Tag";
@Entity()
export class Post {
@PrimaryGeneratedColumn()
id: number;
@Column()
title: string;
@ManyToOne(type => Tag)
tag: Tag;
tagId: number;
@ManyToMany(type => Category, category => category.posts)
@JoinTable()
categories: Category[];
@ManyToMany(type => Category)
@JoinTable()
subcategories: Category[];
categoryIds: number[];
}

View File

@ -0,0 +1,14 @@
import {Entity} from "../../../../../../../src/decorator/entity/Entity";
import {PrimaryGeneratedColumn} from "../../../../../../../src/decorator/columns/PrimaryGeneratedColumn";
import {Column} from "../../../../../../../src/decorator/columns/Column";
@Entity()
export class Tag {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
}

View File

@ -0,0 +1,96 @@
import "reflect-metadata";
import * as chai from "chai";
import {expect} from "chai";
import {
closeTestingConnections,
createTestingConnections,
reloadTestingDatabases
} from "../../../../../utils/test-utils";
import {Connection} from "../../../../../../src/connection/Connection";
import {Post} from "./entity/Post";
import {Category} from "./entity/Category";
import {Counters} from "./entity/Counters";
const should = chai.should();
describe("query builder > relation-id > many-to-many > embedded", () => {
let connections: Connection[];
before(async () => connections = await createTestingConnections({
entities: [__dirname + "/entity/*{.js,.ts}"],
schemaCreate: true,
dropSchemaOnConnection: true,
}));
beforeEach(() => reloadTestingDatabases(connections));
after(() => closeTestingConnections(connections));
it("should load ids when RelationId decorator used in embedded table", () => Promise.all(connections.map(async connection => {
const category1 = new Category();
category1.name = "cars";
await connection.manager.persist(category1);
const category2 = new Category();
category2.name = "BMW";
await connection.manager.persist(category2);
const category3 = new Category();
category3.name = "airplanes";
await connection.manager.persist(category3);
const category4 = new Category();
category4.name = "Boeing";
await connection.manager.persist(category4);
const post1 = new Post();
post1.title = "About BMW";
post1.counters = new Counters();
post1.counters.likes = 1;
post1.counters.comments = 2;
post1.counters.favorites = 3;
post1.counters.categories = [category1, category2];
await connection.manager.persist(post1);
const post2 = new Post();
post2.title = "About Boeing";
post2.counters = new Counters();
post2.counters.likes = 3;
post2.counters.comments = 4;
post2.counters.favorites = 5;
post2.counters.categories = [category3, category4];
await connection.manager.persist(post2);
const loadedPosts = await connection.manager
.createQueryBuilder(Post, "post")
.loadRelationIdAndMap("post.counters.categoryIds", "post.counters.categories")
.orderBy("post.id")
.getMany();
expect(loadedPosts[0].should.be.eql(
{
id: 1,
title: "About BMW",
counters: {
likes: 1,
comments: 2,
favorites: 3,
categoryIds: [1, 2]
}
}
));
expect(loadedPosts[1].should.be.eql(
{
id: 2,
title: "About Boeing",
counters: {
likes: 3,
comments: 4,
favorites: 5,
categoryIds: [3, 4]
}
}
));
})));
});

View File

@ -0,0 +1,21 @@
import {Entity} from "../../../../../../../src/decorator/entity/Entity";
import {Column} from "../../../../../../../src/decorator/columns/Column";
import {ManyToMany} from "../../../../../../../src/decorator/relations/ManyToMany";
import {PrimaryGeneratedColumn} from "../../../../../../../src/decorator/columns/PrimaryGeneratedColumn";
import {Post} from "./Post";
@Entity()
export class Category {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@ManyToMany(type => Post, post => post.counters.categories)
posts: Post[];
postIds: number[];
}

View File

@ -0,0 +1,23 @@
import {Column} from "../../../../../../../src/decorator/columns/Column";
import {ManyToMany} from "../../../../../../../src/decorator/relations/ManyToMany";
import {JoinTable} from "../../../../../../../src/decorator/relations/JoinTable";
import {Category} from "./Category";
export class Counters {
@Column()
likes: number;
@Column()
comments: number;
@Column()
favorites: number;
@ManyToMany(type => Category, category => category.posts)
@JoinTable()
categories: Category[];
categoryIds: number[];
}

View File

@ -0,0 +1,19 @@
import {Entity} from "../../../../../../../src/decorator/entity/Entity";
import {Column} from "../../../../../../../src/decorator/columns/Column";
import {PrimaryGeneratedColumn} from "../../../../../../../src/decorator/columns/PrimaryGeneratedColumn";
import {Embedded} from "../../../../../../../src/decorator/Embedded";
import {Counters} from "./Counters";
@Entity()
export class Post {
@PrimaryGeneratedColumn()
id: number;
@Column()
title: string;
@Embedded(() => Counters)
counters: Counters;
}

View File

@ -0,0 +1,37 @@
import {Entity} from "../../../../../../../src/decorator/entity/Entity";
import {Column} from "../../../../../../../src/decorator/columns/Column";
import {ManyToMany} from "../../../../../../../src/decorator/relations/ManyToMany";
import {PrimaryColumn} from "../../../../../../../src/decorator/columns/PrimaryColumn";
import {Index} from "../../../../../../../src/decorator/Index";
import {JoinTable} from "../../../../../../../src/decorator/relations/JoinTable";
import {Post} from "./Post";
import {Image} from "./Image";
@Entity()
@Index(["id", "code"])
export class Category {
@PrimaryColumn()
id: number;
@PrimaryColumn()
code: number;
@Column()
name: string;
@Column()
isRemoved: boolean = false;
@ManyToMany(type => Post, post => post.categories)
posts: Post[];
@ManyToMany(type => Image, image => image.categories)
@JoinTable()
images: Image[];
postIds: number[];
imageIds: number[];
}

View File

@ -0,0 +1,21 @@
import {Entity} from "../../../../../../../src/decorator/entity/Entity";
import {PrimaryGeneratedColumn} from "../../../../../../../src/decorator/columns/PrimaryGeneratedColumn";
import {Column} from "../../../../../../../src/decorator/columns/Column";
import {ManyToMany} from "../../../../../../../src/decorator/relations/ManyToMany";
import {Category} from "./Category";
@Entity()
export class Image {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@ManyToMany(type => Category, category => category.images)
categories: Category[];
categoryIds: number[];
}

View File

@ -0,0 +1,35 @@
import {ManyToMany} from "../../../../../../../src/decorator/relations/ManyToMany";
import {Entity} from "../../../../../../../src/decorator/entity/Entity";
import {Column} from "../../../../../../../src/decorator/columns/Column";
import {JoinTable} from "../../../../../../../src/decorator/relations/JoinTable";
import {PrimaryColumn} from "../../../../../../../src/decorator/columns/PrimaryColumn";
import {Index} from "../../../../../../../src/decorator/Index";
import {Category} from "./Category";
@Entity()
@Index(["id", "authorId"])
export class Post {
@PrimaryColumn()
id: number;
@PrimaryColumn()
authorId: number;
@Column()
title: string;
@Column()
isRemoved: boolean = false;
@ManyToMany(type => Category, category => category.posts)
@JoinTable()
categories: Category[];
@ManyToMany(type => Category)
@JoinTable()
subcategories: Category[];
categoryIds: number[];
}

View File

@ -0,0 +1,731 @@
import "reflect-metadata";
import * as chai from "chai";
import {expect} from "chai";
import {
closeTestingConnections,
createTestingConnections,
reloadTestingDatabases
} from "../../../../../utils/test-utils";
import {Connection} from "../../../../../../src/connection/Connection";
import {Post} from "./entity/Post";
import {Category} from "./entity/Category";
import {Image} from "./entity/Image";
const should = chai.should();
describe("query builder > relation-id > many-to-many > multiple-pk", () => {
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 load ids when both entities have multiple primary keys", () => Promise.all(connections.map(async connection => {
const category1 = new Category();
category1.id = 1;
category1.code = 1;
category1.name = "cars";
await connection.manager.persist(category1);
const category2 = new Category();
category2.id = 2;
category2.code = 2;
category2.name = "BMW";
await connection.manager.persist(category2);
const category3 = new Category();
category3.id = 3;
category3.code = 1;
category3.name = "airplanes";
await connection.manager.persist(category3);
const category4 = new Category();
category4.id = 4;
category4.code = 2;
category4.name = "Boeing";
await connection.manager.persist(category4);
const post1 = new Post();
post1.id = 1;
post1.authorId = 1;
post1.title = "About BMW";
post1.categories = [category1, category2];
await connection.manager.persist(post1);
const post2 = new Post();
post2.id = 2;
post2.authorId = 1;
post2.title = "About Boeing";
post2.categories = [category3, category4];
await connection.manager.persist(post2);
const loadedPosts = await connection.manager
.createQueryBuilder(Post, "post")
.loadRelationIdAndMap("post.categoryIds", "post.categories")
.getMany();
expect(loadedPosts[0].categoryIds).to.not.be.empty;
expect(loadedPosts[0].categoryIds[0]).to.be.eql({ id: 1, code: 1 });
expect(loadedPosts[0].categoryIds[1]).to.be.eql({ id: 2, code: 2 });
expect(loadedPosts[1].categoryIds).to.not.be.empty;
expect(loadedPosts[1].categoryIds[0]).to.be.eql({ id: 3, code: 1 });
expect(loadedPosts[1].categoryIds[1]).to.be.eql({ id: 4, code: 2 });
const loadedPost = await connection.manager
.createQueryBuilder(Post, "post")
.loadRelationIdAndMap("post.categoryIds", "post.categories")
.where("post.id = :id", { id: 1 })
.andWhere("post.authorId = :authorId", { authorId: 1 })
.getOne();
expect(loadedPost!.categoryIds).to.not.be.empty;
expect(loadedPost!.categoryIds[0]).to.be.eql({ id: 1, code: 1 });
expect(loadedPost!.categoryIds[1]).to.be.eql({ id: 2, code: 2 });
})));
it("should load ids when only one entity have multiple primary keys", () => Promise.all(connections.map(async connection => {
const image1 = new Image();
image1.name = "Image #1";
await connection.manager.persist(image1);
const image2 = new Image();
image2.name = "Image #2";
await connection.manager.persist(image2);
const image3 = new Image();
image3.name = "Image #3";
await connection.manager.persist(image3);
const image4 = new Image();
image4.name = "Image #4";
await connection.manager.persist(image4);
const category1 = new Category();
category1.id = 1;
category1.code = 1;
category1.name = "cars";
category1.images = [image1, image2];
await connection.manager.persist(category1);
const category2 = new Category();
category2.id = 2;
category2.code = 2;
category2.name = "airplanes";
category2.images = [image3, image4];
await connection.manager.persist(category2);
const loadedCategories = await connection.manager
.createQueryBuilder(Category, "category")
.loadRelationIdAndMap("category.imageIds", "category.images")
.getMany();
expect(loadedCategories[0].imageIds).to.not.be.empty;
expect(loadedCategories[0].imageIds[0]).to.be.equal(1);
expect(loadedCategories[0].imageIds[1]).to.be.equal(2);
expect(loadedCategories[1].imageIds).to.not.be.empty;
expect(loadedCategories[1].imageIds[0]).to.be.equal(3);
expect(loadedCategories[1].imageIds[1]).to.be.equal(4);
const loadedCategory = await connection.manager
.createQueryBuilder(Category, "category")
.loadRelationIdAndMap("category.imageIds", "category.images")
.where("category.id = :id", { id: 1 })
.andWhere("category.code = :code", { code: 1 })
.getOne();
expect(loadedCategory!.imageIds).to.not.be.empty;
expect(loadedCategory!.imageIds[0]).to.be.equal(1);
expect(loadedCategory!.imageIds[1]).to.be.equal(2);
})));
it("should load ids when both entities have multiple primary keys and additional condition used", () => Promise.all(connections.map(async connection => {
const category1 = new Category();
category1.id = 1;
category1.code = 1;
category1.name = "cars";
await connection.manager.persist(category1);
const category2 = new Category();
category2.id = 2;
category2.code = 1;
category2.isRemoved = true;
category2.name = "BMW";
await connection.manager.persist(category2);
const category3 = new Category();
category3.id = 3;
category3.code = 1;
category3.name = "airplanes";
await connection.manager.persist(category3);
const category4 = new Category();
category4.id = 4;
category4.code = 2;
category4.isRemoved = true;
category4.name = "Boeing";
await connection.manager.persist(category4);
const post1 = new Post();
post1.id = 1;
post1.authorId = 1;
post1.title = "About BMW";
post1.categories = [category1, category2];
await connection.manager.persist(post1);
const post2 = new Post();
post2.id = 2;
post2.authorId = 1;
post2.title = "About Boeing";
post2.categories = [category3, category4];
await connection.manager.persist(post2);
let loadedPosts = await connection.manager
.createQueryBuilder(Post, "post")
.loadRelationIdAndMap("post.categoryIds", "post.categories", "category", qb => qb.andWhere("category.id = :id AND category.code = :code", { id: 1, code: 1 }))
.getMany();
expect(loadedPosts[0].categoryIds).to.not.be.empty;
expect(loadedPosts[0].categoryIds[0]).to.be.eql({ id: 1, code: 1 });
expect(loadedPosts[1].categoryIds).to.be.empty;
loadedPosts = await connection.manager
.createQueryBuilder(Post, "post")
.loadRelationIdAndMap("post.categoryIds", "post.categories", "category", qb => qb.andWhere("category.code = :code", { code: 1 }))
.getMany();
expect(loadedPosts[0].categoryIds).to.not.be.empty;
expect(loadedPosts[0].categoryIds[0]).to.be.eql({ id: 1, code: 1 });
expect(loadedPosts[0].categoryIds[1]).to.be.eql({ id: 2, code: 1 });
expect(loadedPosts[1].categoryIds).to.not.be.empty;
expect(loadedPosts[1].categoryIds[0]).to.be.eql({ id: 3, code: 1 });
loadedPosts = await connection.manager
.createQueryBuilder(Post, "post")
.loadRelationIdAndMap("post.categoryIds", "post.categories", "category", qb => qb.andWhere("category.isRemoved = :isRemoved", { isRemoved: true }))
.getMany();
expect(loadedPosts[0].categoryIds).to.not.be.empty;
expect(loadedPosts[0].categoryIds[0]).to.be.eql({ id: 2, code: 1 });
expect(loadedPosts[1].categoryIds).to.not.be.empty;
expect(loadedPosts[1].categoryIds[0]).to.be.eql({ id: 4, code: 2 });
const loadedPost = await connection.manager
.createQueryBuilder(Post, "post")
.loadRelationIdAndMap("post.categoryIds", "post.categories", "category", qb => qb.andWhere("category.isRemoved = :isRemoved", { isRemoved: true }))
.where("post.id = :id", { id: 1 })
.andWhere("post.authorId = :authorId", { authorId: 1 })
.getOne();
expect(loadedPost!.categoryIds).to.not.be.empty;
expect(loadedPost!.categoryIds[0]).to.be.eql({ id: 2, code: 1 });
})));
it("should load ids when both entities have multiple primary keys and related entity does not have inverse side", () => Promise.all(connections.map(async connection => {
const category1 = new Category();
category1.id = 1;
category1.code = 1;
category1.name = "cars";
await connection.manager.persist(category1);
const category2 = new Category();
category2.id = 2;
category2.code = 2;
category2.name = "BMW";
await connection.manager.persist(category2);
const category3 = new Category();
category3.id = 3;
category3.code = 1;
category3.name = "airplanes";
await connection.manager.persist(category3);
const category4 = new Category();
category4.id = 4;
category4.code = 2;
category4.name = "Boeing";
await connection.manager.persist(category4);
const post1 = new Post();
post1.id = 1;
post1.authorId = 1;
post1.title = "About BMW";
post1.subcategories = [category1, category2];
await connection.manager.persist(post1);
const post2 = new Post();
post2.id = 2;
post2.authorId = 1;
post2.title = "About Boeing";
post2.subcategories = [category3, category4];
await connection.manager.persist(post2);
const loadedPosts = await connection.manager
.createQueryBuilder(Post, "post")
.loadRelationIdAndMap("post.categoryIds", "post.subcategories")
.getMany();
expect(loadedPosts[0].categoryIds).to.not.be.empty;
expect(loadedPosts[0].categoryIds[0]).to.be.eql({ id: 1, code: 1 });
expect(loadedPosts[0].categoryIds[1]).to.be.eql({ id: 2, code: 2 });
expect(loadedPosts[1].categoryIds).to.not.be.empty;
expect(loadedPosts[1].categoryIds[0]).to.be.eql({ id: 3, code: 1 });
expect(loadedPosts[1].categoryIds[1]).to.be.eql({ id: 4, code: 2 });
const loadedPost = await connection.manager
.createQueryBuilder(Post, "post")
.loadRelationIdAndMap("post.categoryIds", "post.subcategories")
.where("post.id = :id", { id: 1 })
.andWhere("post.authorId = :authorId", { authorId: 1 })
.getOne();
expect(loadedPost!.categoryIds).to.not.be.empty;
expect(loadedPost!.categoryIds[0]).to.be.eql({ id: 1, code: 1 });
expect(loadedPost!.categoryIds[1]).to.be.eql({ id: 2, code: 2 });
})));
it("should load ids when loadRelationIdAndMap used on nested relation", () => Promise.all(connections.map(async connection => {
const image1 = new Image();
image1.name = "Image #1";
await connection.manager.persist(image1);
const image2 = new Image();
image2.name = "Image #2";
await connection.manager.persist(image2);
const image3 = new Image();
image3.name = "Image #3";
await connection.manager.persist(image3);
const image4 = new Image();
image4.name = "Image #4";
await connection.manager.persist(image4);
const image5 = new Image();
image5.name = "Image #5";
await connection.manager.persist(image5);
const category1 = new Category();
category1.id = 1;
category1.code = 1;
category1.name = "cars";
category1.images = [image1, image2, image3];
await connection.manager.persist(category1);
const category2 = new Category();
category2.id = 2;
category2.code = 1;
category2.name = "airplanes";
category2.images = [image4, image5];
await connection.manager.persist(category2);
const category3 = new Category();
category3.id = 3;
category3.code = 2;
category3.name = "Boeing";
category3.images = [image5];
await connection.manager.persist(category3);
const post1 = new Post();
post1.id = 1;
post1.authorId = 1;
post1.title = "About BMW";
post1.categories = [category1];
await connection.manager.persist(post1);
const post2 = new Post();
post2.id = 2;
post2.authorId = 1;
post2.title = "About Boeing";
post2.categories = [category2, category3];
await connection.manager.persist(post2);
const loadedPosts = await connection.manager
.createQueryBuilder(Post, "post")
.loadRelationIdAndMap("post.categoryIds", "post.categories")
.leftJoinAndSelect("post.categories", "category")
.loadRelationIdAndMap("category.imageIds", "category.images")
.getMany();
expect(loadedPosts[0].categoryIds).to.not.be.empty;
expect(loadedPosts[0].categoryIds.length).to.be.equal(1);
expect(loadedPosts[0].categoryIds[0]).to.be.eql({ id: 1, code: 1 });
expect(loadedPosts[0].categories).to.not.be.empty;
expect(loadedPosts[0].categories[0].imageIds).to.not.be.empty;
expect(loadedPosts[0].categories[0].imageIds.length).to.be.equal(3);
expect(loadedPosts[0].categories[0].imageIds[0]).to.be.equal(1);
expect(loadedPosts[0].categories[0].imageIds[1]).to.be.equal(2);
expect(loadedPosts[0].categories[0].imageIds[2]).to.be.equal(3);
expect(loadedPosts[1].categoryIds).to.not.be.empty;
expect(loadedPosts[1].categoryIds.length).to.be.equal(2);
expect(loadedPosts[1].categoryIds[0]).to.be.eql({ id: 2, code: 1 });
expect(loadedPosts[1].categoryIds[1]).to.be.eql({ id: 3, code: 2 });
expect(loadedPosts[1].categories).to.not.be.empty;
expect(loadedPosts[1].categories.length).to.be.equal(2);
expect(loadedPosts[1].categories[0].imageIds).to.not.be.empty;
expect(loadedPosts[1].categories[0].imageIds.length).to.be.equal(2);
expect(loadedPosts[1].categories[0].imageIds[0]).to.be.equal(4);
expect(loadedPosts[1].categories[0].imageIds[1]).to.be.equal(5);
expect(loadedPosts[1].categories[1].imageIds).to.not.be.empty;
expect(loadedPosts[1].categories[1].imageIds.length).to.be.equal(1);
expect(loadedPosts[1].categories[1].imageIds[0]).to.be.equal(5);
const loadedPost = await connection.manager
.createQueryBuilder(Post, "post")
.loadRelationIdAndMap("post.categoryIds", "post.categories")
.leftJoinAndSelect("post.categories", "category")
.loadRelationIdAndMap("category.imageIds", "category.images")
.where("post.id = :id", { id: 1 })
.andWhere("post.authorId = :authorId", { authorId: 1 })
.getOne();
expect(loadedPost!.categoryIds).to.not.be.empty;
expect(loadedPost!.categoryIds[0]).to.be.eql({ id: 1, code: 1 });
expect(loadedPost!.categories).to.not.be.empty;
expect(loadedPost!.categories[0].imageIds).to.not.be.empty;
expect(loadedPost!.categories[0].imageIds.length).to.be.equal(3);
expect(loadedPost!.categories[0].imageIds[0]).to.be.equal(1);
expect(loadedPost!.categories[0].imageIds[1]).to.be.equal(2);
expect(loadedPost!.categories[0].imageIds[2]).to.be.equal(3);
})));
});
describe("inverse side", () => {
it("should load ids when both entities have multiple primary keys", () => Promise.all(connections.map(async connection => {
const post1 = new Post();
post1.id = 1;
post1.authorId = 1;
post1.title = "About BMW";
await connection.manager.persist(post1);
const post2 = new Post();
post2.id = 2;
post2.authorId = 2;
post2.title = "About Audi";
await connection.manager.persist(post2);
const post3 = new Post();
post3.id = 3;
post3.authorId = 1;
post3.title = "About Boeing";
await connection.manager.persist(post3);
const post4 = new Post();
post4.id = 4;
post4.authorId = 2;
post4.title = "About Airbus";
await connection.manager.persist(post4);
const category1 = new Category();
category1.id = 1;
category1.code = 1;
category1.name = "cars";
category1.posts = [post1, post2];
await connection.manager.persist(category1);
const category2 = new Category();
category2.id = 2;
category2.code = 1;
category2.name = "airplanes";
category2.posts = [post3, post4];
await connection.manager.persist(category2);
const loadedCategories = await connection.manager
.createQueryBuilder(Category, "category")
.loadRelationIdAndMap("category.postIds", "category.posts")
.getMany();
expect(loadedCategories[0].postIds).to.not.be.empty;
expect(loadedCategories[0].postIds[0]).to.be.eql({ id: 1, authorId: 1 });
expect(loadedCategories[0].postIds[1]).to.be.eql({ id: 2, authorId: 2 });
expect(loadedCategories[1].postIds).to.not.be.empty;
expect(loadedCategories[1].postIds[0]).to.be.eql({ id: 3, authorId: 1 });
expect(loadedCategories[1].postIds[1]).to.be.eql({ id: 4, authorId: 2 });
const loadedCategory = await connection.manager
.createQueryBuilder(Category, "category")
.loadRelationIdAndMap("category.postIds", "category.posts")
.where("category.id = :id", { id: 1 })
.andWhere("category.code = :code", { code: 1 })
.getOne();
expect(loadedCategory!.postIds).to.not.be.empty;
expect(loadedCategory!.postIds[0]).to.be.eql({ id: 1, authorId: 1 });
expect(loadedCategory!.postIds[1]).to.be.eql({ id: 2, authorId: 2 });
})));
it("should load ids when only one entity have multiple primary keys", () => Promise.all(connections.map(async connection => {
const category1 = new Category();
category1.id = 1;
category1.code = 1;
category1.name = "category #1";
await connection.manager.persist(category1);
const category2 = new Category();
category2.id = 2;
category2.code = 1;
category2.name = "category #2";
await connection.manager.persist(category2);
const category3 = new Category();
category3.id = 3;
category3.code = 2;
category3.name = "category #3";
await connection.manager.persist(category3);
const category4 = new Category();
category4.id = 4;
category4.code = 2;
category4.name = "category #4";
await connection.manager.persist(category4);
const image1 = new Image();
image1.name = "Image #1";
image1.categories = [category1, category2];
await connection.manager.persist(image1);
const image2 = new Image();
image2.name = "Image #2";
image2.categories = [category3, category4];
await connection.manager.persist(image2);
const loadedImages = await connection.manager
.createQueryBuilder(Image, "image")
.loadRelationIdAndMap("image.categoryIds", "image.categories")
.getMany();
expect(loadedImages[0].categoryIds).to.not.be.empty;
expect(loadedImages[0].categoryIds[0]).to.be.eql({ id: 1, code: 1 });
expect(loadedImages[0].categoryIds[1]).to.be.eql({ id: 2, code: 1 });
expect(loadedImages[1].categoryIds).to.not.be.empty;
expect(loadedImages[1].categoryIds[0]).to.be.eql({ id: 3, code: 2 });
expect(loadedImages[1].categoryIds[1]).to.be.eql({ id: 4, code: 2 });
const loadedImage = await connection.manager
.createQueryBuilder(Image, "image")
.loadRelationIdAndMap("image.categoryIds", "image.categories")
.where("image.id = :id", { id: 1 })
.getOne();
expect(loadedImage!.categoryIds).to.not.be.empty;
expect(loadedImage!.categoryIds[0]).to.be.eql({ id: 1, code: 1 });
expect(loadedImage!.categoryIds[1]).to.be.eql({ id: 2, code: 1 });
})));
it("should load ids when both entities have multiple primary keys and additional condition used", () => Promise.all(connections.map(async connection => {
const post1 = new Post();
post1.id = 1;
post1.authorId = 1;
post1.title = "About BMW";
await connection.manager.persist(post1);
const post2 = new Post();
post2.id = 2;
post2.authorId = 1;
post2.isRemoved = true;
post2.title = "About Audi";
await connection.manager.persist(post2);
const post3 = new Post();
post3.id = 3;
post3.authorId = 1;
post3.title = "About Boeing";
await connection.manager.persist(post3);
const post4 = new Post();
post4.id = 4;
post4.authorId = 2;
post4.isRemoved = true;
post4.title = "About Airbus";
await connection.manager.persist(post4);
const category1 = new Category();
category1.id = 1;
category1.code = 1;
category1.name = "cars";
category1.posts = [post1, post2];
await connection.manager.persist(category1);
const category2 = new Category();
category2.id = 2;
category2.code = 1;
category2.name = "airplanes";
category2.posts = [post3, post4];
await connection.manager.persist(category2);
let loadedCategories = await connection.manager
.createQueryBuilder(Category, "category")
.loadRelationIdAndMap("category.postIds", "category.posts", "post", qb => qb.andWhere("post.id = :id AND post.authorId = :authorId", { id: 1, authorId: 1 }))
.getMany();
expect(loadedCategories[0].postIds).to.not.be.empty;
expect(loadedCategories[0].postIds[0]).to.be.eql({ id: 1, authorId: 1 });
expect(loadedCategories[1].postIds).to.be.empty;
loadedCategories = await connection.manager
.createQueryBuilder(Category, "category")
.loadRelationIdAndMap("category.postIds", "category.posts", "post", qb => qb.andWhere("post.authorId = :authorId", { authorId: 1 }))
.getMany();
expect(loadedCategories[0].postIds).to.not.be.empty;
expect(loadedCategories[0].postIds[0]).to.be.eql({ id: 1, authorId: 1 });
expect(loadedCategories[0].postIds[1]).to.be.eql({ id: 2, authorId: 1 });
expect(loadedCategories[1].postIds).to.not.be.empty;
expect(loadedCategories[1].postIds[0]).to.be.eql({ id: 3, authorId: 1 });
loadedCategories = await connection.manager
.createQueryBuilder(Category, "category")
.loadRelationIdAndMap("category.postIds", "category.posts", "post", qb => qb.andWhere("post.isRemoved = :isRemoved", { isRemoved: true }))
.getMany();
expect(loadedCategories[0].postIds).to.not.be.empty;
expect(loadedCategories[0].postIds[0]).to.be.eql({ id: 2, authorId: 1 });
expect(loadedCategories[1].postIds).to.not.be.empty;
expect(loadedCategories[1].postIds[0]).to.be.eql({ id: 4, authorId: 2 });
const loadedCategory = await connection.manager
.createQueryBuilder(Category, "category")
.loadRelationIdAndMap("category.postIds", "category.posts", "post", qb => qb.andWhere("post.isRemoved = :isRemoved", { isRemoved: true }))
.where("category.id = :id", { id: 1 })
.andWhere("category.code = :code", { code: 1 })
.getOne();
expect(loadedCategory!.postIds).to.not.be.empty;
expect(loadedCategory!.postIds[0]).to.be.eql({ id: 2, authorId: 1 });
})));
it("should load ids when loadRelationIdAndMap used on nested relation", () => Promise.all(connections.map(async connection => {
const post1 = new Post();
post1.id = 1;
post1.authorId = 1;
post1.title = "About BMW";
await connection.manager.persist(post1);
const post2 = new Post();
post2.id = 2;
post2.authorId = 1;
post2.title = "About Audi";
await connection.manager.persist(post2);
const post3 = new Post();
post3.id = 3;
post3.authorId = 1;
post3.title = "About Boeing";
await connection.manager.persist(post3);
const post4 = new Post();
post4.id = 4;
post4.authorId = 2;
post4.title = "About Airbus";
await connection.manager.persist(post4);
const category1 = new Category();
category1.id = 1;
category1.code = 1;
category1.name = "cars";
category1.posts = [post1, post2];
await connection.manager.persist(category1);
const category2 = new Category();
category2.id = 2;
category2.code = 1;
category2.name = "BMW";
category2.posts = [post1];
await connection.manager.persist(category2);
const category3 = new Category();
category3.id = 3;
category3.code = 2;
category3.name = "airplanes";
category3.posts = [post3, post4];
await connection.manager.persist(category3);
const image1 = new Image();
image1.name = "Image #1";
image1.categories = [category1, category2];
await connection.manager.persist(image1);
const image2 = new Image();
image2.name = "Image #2";
image2.categories = [category3];
await connection.manager.persist(image2);
const loadedImages = await connection.manager
.createQueryBuilder(Image, "image")
.loadRelationIdAndMap("image.categoryIds", "image.categories")
.leftJoinAndSelect("image.categories", "category")
.loadRelationIdAndMap("category.postIds", "category.posts")
.getMany();
expect(loadedImages[0].categoryIds).to.not.be.empty;
expect(loadedImages[0].categoryIds.length).to.be.equal(2);
expect(loadedImages[0].categoryIds[0]).to.be.eql({ id: 1, code: 1 });
expect(loadedImages[0].categoryIds[1]).to.be.eql({ id: 2, code: 1 });
expect(loadedImages[0].categories).to.not.be.empty;
expect(loadedImages[0].categories.length).to.be.equal(2);
expect(loadedImages[0].categories[0].postIds).to.not.be.empty;
expect(loadedImages[0].categories[0].postIds.length).to.be.equal(2);
expect(loadedImages[0].categories[0].postIds[0]).to.be.eql({ id: 1, authorId: 1 });
expect(loadedImages[0].categories[0].postIds[1]).to.be.eql({ id: 2, authorId: 1 });
expect(loadedImages[0].categories[1].postIds).to.not.be.empty;
expect(loadedImages[0].categories[1].postIds.length).to.be.equal(1);
expect(loadedImages[0].categories[1].postIds[0]).to.be.eql({ id: 1, authorId: 1 });
expect(loadedImages[1].categoryIds).to.not.be.empty;
expect(loadedImages[1].categoryIds.length).to.be.equal(1);
expect(loadedImages[1].categoryIds[0]).to.be.eql({ id: 3, code: 2 });
expect(loadedImages[1].categories).to.not.be.empty;
expect(loadedImages[1].categories[0].postIds).to.not.be.empty;
expect(loadedImages[1].categories[0].postIds.length).to.be.equal(2);
expect(loadedImages[1].categories[0].postIds[0]).to.be.eql({ id: 3, authorId: 1 });
expect(loadedImages[1].categories[0].postIds[1]).to.be.eql({ id: 4, authorId: 2 });
const loadedImage = await connection.manager
.createQueryBuilder(Image, "image")
.loadRelationIdAndMap("image.categoryIds", "image.categories")
.leftJoinAndSelect("image.categories", "category")
.loadRelationIdAndMap("category.postIds", "category.posts")
.where("image.id = :id", { id: 1 })
.getOne();
expect(loadedImage!.categoryIds).to.not.be.empty;
expect(loadedImage!.categoryIds.length).to.be.equal(2);
expect(loadedImage!.categoryIds[0]).to.be.eql({ id: 1, code: 1 });
expect(loadedImage!.categoryIds[1]).to.be.eql({ id: 2, code: 1 });
expect(loadedImage!.categories).to.not.be.empty;
expect(loadedImage!.categories.length).to.be.equal(2);
expect(loadedImage!.categories[0].postIds).to.not.be.empty;
expect(loadedImage!.categories[0].postIds.length).to.be.equal(2);
expect(loadedImage!.categories[0].postIds[0]).to.be.eql({ id: 1, authorId: 1 });
expect(loadedImage!.categories[0].postIds[1]).to.be.eql({ id: 2, authorId: 1 });
expect(loadedImage!.categories[1].postIds).to.not.be.empty;
expect(loadedImage!.categories[1].postIds.length).to.be.equal(1);
expect(loadedImage!.categories[1].postIds[0]).to.be.eql({ id: 1, authorId: 1 });
})));
});
});

View File

@ -0,0 +1,240 @@
import "reflect-metadata";
import * as chai from "chai";
import {expect} from "chai";
import {
closeTestingConnections,
createTestingConnections,
reloadTestingDatabases
} from "../../../../../utils/test-utils";
import {Connection} from "../../../../../../src/connection/Connection";
import {Category} from "./entity/Category";
import {Post} from "./entity/Post";
import {Image} from "./entity/Image";
const should = chai.should();
describe.only("query builder > load-relation-id-and-map > one-to-many", () => {
let connections: Connection[];
before(async () => connections = await createTestingConnections({
entities: [__dirname + "/entity/*{.js,.ts}"],
schemaCreate: true,
dropSchemaOnConnection: true,
}));
beforeEach(() => reloadTestingDatabases(connections));
after(() => closeTestingConnections(connections));
it("should load id when loadRelationIdAndMap used with OneToMany relation", () => Promise.all(connections.map(async connection => {
const category1 = new Category();
category1.name = "cars";
await connection.manager.persist(category1);
const category2 = new Category();
category2.name = "BMW";
await connection.manager.persist(category2);
const category3 = new Category();
category3.name = "airplanes";
await connection.manager.persist(category3);
const category4 = new Category();
category4.name = "Boeing";
await connection.manager.persist(category4);
const post1 = new Post();
post1.title = "about BMW";
post1.categories = [category1, category2];
await connection.manager.persist(post1);
const post2 = new Post();
post2.title = "about Audi";
post2.categories = [category3, category4];
await connection.manager.persist(post2);
const loadedPosts = await connection.manager
.createQueryBuilder(Post, "post")
.loadRelationIdAndMap("post.categoryIds", "post.categories")
.getMany();
expect(loadedPosts[0].categoryIds).to.not.be.empty;
expect(loadedPosts[0].categoryIds.length).to.be.equal(2);
expect(loadedPosts[0].categoryIds[0]).to.be.equal(1);
expect(loadedPosts[0].categoryIds[1]).to.be.equal(2);
expect(loadedPosts[1].categoryIds).to.not.be.empty;
expect(loadedPosts[1].categoryIds.length).to.be.equal(2);
expect(loadedPosts[1].categoryIds[0]).to.be.equal(3);
expect(loadedPosts[1].categoryIds[1]).to.be.equal(4);
const loadedPost = await connection.manager
.createQueryBuilder(Post, "post")
.loadRelationIdAndMap("post.categoryIds", "post.categories")
.where("post.id = :id", { id: 1 })
.getOne();
expect(loadedPost!.categoryIds).to.not.be.empty;
expect(loadedPost!.categoryIds.length).to.be.equal(2);
expect(loadedPost!.categoryIds[0]).to.be.equal(1);
expect(loadedPost!.categoryIds[1]).to.be.equal(2);
})));
it("should load id when loadRelationIdAndMap used with additional condition", () => Promise.all(connections.map(async connection => {
const category1 = new Category();
category1.name = "cars";
await connection.manager.persist(category1);
const category2 = new Category();
category2.name = "BMW";
category2.isRemoved = true;
await connection.manager.persist(category2);
const category3 = new Category();
category3.name = "airplanes";
await connection.manager.persist(category3);
const category4 = new Category();
category4.name = "Boeing";
category4.isRemoved = true;
await connection.manager.persist(category4);
const post1 = new Post();
post1.title = "about BMW";
post1.categories = [category1, category2];
await connection.manager.persist(post1);
const post2 = new Post();
post2.title = "about Audi";
post2.categories = [category3, category4];
await connection.manager.persist(post2);
const loadedPosts = await connection.manager
.createQueryBuilder(Post, "post")
.loadRelationIdAndMap("post.categoryIds", "post.categories", "category", qb => qb.andWhere("category.isRemoved = :isRemoved", { isRemoved: true }))
.getMany();
expect(loadedPosts[0].categoryIds).to.not.be.empty;
expect(loadedPosts[0].categoryIds.length).to.be.equal(1);
expect(loadedPosts[0].categoryIds[0]).to.be.equal(2);
expect(loadedPosts[1].categoryIds).to.not.be.empty;
expect(loadedPosts[1].categoryIds.length).to.be.equal(1);
expect(loadedPosts[1].categoryIds[0]).to.be.equal(4);
const loadedPost = await connection.manager
.createQueryBuilder(Post, "post")
.loadRelationIdAndMap("post.categoryIds", "post.categories", "category", qb => qb.andWhere("category.id = :categoryId", { categoryId: 1 }))
.where("post.id = :id", { id: 1 })
.getOne();
expect(loadedPost!.categoryIds).to.not.be.empty;
expect(loadedPost!.categoryIds.length).to.be.equal(1);
expect(loadedPost!.categoryIds[0]).to.be.equal(1);
})));
it("should load id when loadRelationIdAndMap used on nested relation", () => Promise.all(connections.map(async connection => {
const image1 = new Image();
image1.name = "Image #1";
await connection.manager.persist(image1);
const image2 = new Image();
image2.name = "Image #2";
await connection.manager.persist(image2);
const image3 = new Image();
image3.name = "Image #3";
await connection.manager.persist(image3);
const image4 = new Image();
image4.name = "Image #4";
await connection.manager.persist(image4);
const image5 = new Image();
image5.name = "Image #5";
await connection.manager.persist(image5);
const category1 = new Category();
category1.name = "cars";
category1.images = [image1, image2];
await connection.manager.persist(category1);
const category2 = new Category();
category2.name = "BMW";
category2.images = [image3];
await connection.manager.persist(category2);
const category3 = new Category();
category3.name = "airplanes";
category3.images = [image4, image5];
await connection.manager.persist(category3);
const category4 = new Category();
category4.name = "Boeing";
await connection.manager.persist(category4);
const post1 = new Post();
post1.title = "about BMW";
post1.categories = [category1, category2];
await connection.manager.persist(post1);
const post2 = new Post();
post2.title = "about Audi";
post2.categories = [category3, category4];
await connection.manager.persist(post2);
const loadedPosts = await connection.manager
.createQueryBuilder(Post, "post")
.loadRelationIdAndMap("post.categoryIds", "post.categories")
.leftJoinAndSelect("post.categories", "category")
.loadRelationIdAndMap("category.imageIds", "category.images")
.getMany();
expect(loadedPosts[0].categoryIds).to.not.be.empty;
expect(loadedPosts[0].categoryIds.length).to.be.equal(2);
expect(loadedPosts[0].categoryIds[0]).to.be.equal(1);
expect(loadedPosts[0].categoryIds[1]).to.be.equal(2);
expect(loadedPosts[0].categories).to.not.be.empty;
expect(loadedPosts[0].categories.length).to.be.equal(2);
expect(loadedPosts[0].categories[0].imageIds).to.not.be.empty;
expect(loadedPosts[0].categories[0].imageIds.length).to.be.equal(2);
expect(loadedPosts[0].categories[0].imageIds[0]).to.be.equal(1);
expect(loadedPosts[0].categories[0].imageIds[1]).to.be.equal(2);
expect(loadedPosts[0].categories[1].imageIds).to.not.be.empty;
expect(loadedPosts[0].categories[1].imageIds.length).to.be.equal(1);
expect(loadedPosts[0].categories[1].imageIds[0]).to.be.equal(3);
expect(loadedPosts[1].categoryIds).to.not.be.empty;
expect(loadedPosts[1].categoryIds.length).to.be.equal(2);
expect(loadedPosts[1].categoryIds[0]).to.be.equal(3);
expect(loadedPosts[1].categoryIds[1]).to.be.equal(4);
expect(loadedPosts[1].categories).to.not.be.empty;
expect(loadedPosts[1].categories.length).to.be.equal(2);
expect(loadedPosts[1].categories[0].imageIds).to.not.be.empty;
expect(loadedPosts[1].categories[0].imageIds.length).to.be.equal(2);
expect(loadedPosts[1].categories[0].imageIds[0]).to.be.equal(4);
expect(loadedPosts[1].categories[0].imageIds[1]).to.be.equal(5);
expect(loadedPosts[1].categories[1].imageIds).to.be.empty;
const loadedPost = await connection.manager
.createQueryBuilder(Post, "post")
.loadRelationIdAndMap("post.categoryIds", "post.categories")
.leftJoinAndSelect("post.categories", "category")
.loadRelationIdAndMap("category.imageIds", "category.images")
.where("post.id = :id", { id: 1 })
.getOne();
expect(loadedPost!.categoryIds).to.not.be.empty;
expect(loadedPost!.categoryIds.length).to.be.equal(2);
expect(loadedPost!.categoryIds[0]).to.be.equal(1);
expect(loadedPost!.categoryIds[1]).to.be.equal(2);
expect(loadedPost!.categories).to.not.be.empty;
expect(loadedPost!.categories.length).to.be.equal(2);
expect(loadedPost!.categories[0].imageIds).to.not.be.empty;
expect(loadedPost!.categories[0].imageIds.length).to.be.equal(2);
expect(loadedPost!.categories[0].imageIds[0]).to.be.equal(1);
expect(loadedPost!.categories[0].imageIds[1]).to.be.equal(2);
expect(loadedPost!.categories[1].imageIds).to.not.be.empty;
expect(loadedPost!.categories[1].imageIds.length).to.be.equal(1);
expect(loadedPost!.categories[1].imageIds[0]).to.be.equal(3);
})));
});

View File

@ -0,0 +1,31 @@
import {Entity} from "../../../../../../../src/decorator/entity/Entity";
import {PrimaryGeneratedColumn} from "../../../../../../../src/decorator/columns/PrimaryGeneratedColumn";
import {Column} from "../../../../../../../src/decorator/columns/Column";
import {ManyToOne} from "../../../../../../../src/decorator/relations/ManyToOne";
import {OneToMany} from "../../../../../../../src/decorator/relations/OneToMany";
import {Image} from "./Image";
import {Post} from "./Post";
@Entity()
export class Category {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@Column()
isRemoved: boolean = false;
@OneToMany(type => Image, image => image.category)
images: Image[];
imageIds: number[];
@ManyToOne(type => Post, post => post.categories)
post: Post;
postId: number;
}

View File

@ -0,0 +1,21 @@
import {Entity} from "../../../../../../../src/decorator/entity/Entity";
import {PrimaryGeneratedColumn} from "../../../../../../../src/decorator/columns/PrimaryGeneratedColumn";
import {Column} from "../../../../../../../src/decorator/columns/Column";
import {ManyToOne} from "../../../../../../../src/decorator/relations/ManyToOne";
import {Category} from "./Category";
@Entity()
export class Image {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@ManyToOne(type => Category, category => category.images)
category: Category;
categoryId: number;
}

View File

@ -0,0 +1,21 @@
import {Entity} from "../../../../../../../src/decorator/entity/Entity";
import {PrimaryGeneratedColumn} from "../../../../../../../src/decorator/columns/PrimaryGeneratedColumn";
import {Column} from "../../../../../../../src/decorator/columns/Column";
import {OneToMany} from "../../../../../../../src/decorator/relations/OneToMany";
import {Category} from "./Category";
@Entity()
export class Post {
@PrimaryGeneratedColumn()
id: number;
@Column()
title: string;
@OneToMany(type => Category, category => category.post)
categories: Category[];
categoryIds: number[];
}

View File

@ -0,0 +1,26 @@
import {Entity} from "../../../../../../../src/decorator/entity/Entity";
import {Column} from "../../../../../../../src/decorator/columns/Column";
import {Post} from "./Post";
import {Index} from "../../../../../../../src/decorator/Index";
import {PrimaryColumn} from "../../../../../../../src/decorator/columns/PrimaryColumn";
import {ManyToOne} from "../../../../../../../src/decorator/relations/ManyToOne";
@Entity()
@Index(["id", "code"])
export class Category {
@PrimaryColumn()
id: number;
@PrimaryColumn()
code: number;
@Column()
name: string;
@ManyToOne(type => Post, post => post.categories)
post: Post;
postId: number;
}

View File

@ -0,0 +1,26 @@
import {Entity} from "../../../../../../../src/decorator/entity/Entity";
import {Column} from "../../../../../../../src/decorator/columns/Column";
import {Index} from "../../../../../../../src/decorator/Index";
import {PrimaryColumn} from "../../../../../../../src/decorator/columns/PrimaryColumn";
import {Category} from "./Category";
import {OneToMany} from "../../../../../../../src/decorator/relations/OneToMany";
@Entity()
@Index(["id", "authorId"])
export class Post {
@PrimaryColumn()
id: number;
@PrimaryColumn()
authorId: number;
@Column()
title: string;
@OneToMany(type => Category, category => category.post)
categories: Category[];
categoryIds: number[];
}

View File

@ -0,0 +1,91 @@
import "reflect-metadata";
import * as chai from "chai";
import {expect} from "chai";
import {
closeTestingConnections,
createTestingConnections,
reloadTestingDatabases
} from "../../../../../utils/test-utils";
import {Connection} from "../../../../../../src/connection/Connection";
import {Post} from "./entity/Post";
import {Category} from "./entity/Category";
const should = chai.should();
describe("query builder > relation-id > one-to-many > multiple-pk", () => {
let connections: Connection[];
before(async () => connections = await createTestingConnections({
entities: [__dirname + "/entity/*{.js,.ts}"],
schemaCreate: true,
dropSchemaOnConnection: true,
}));
beforeEach(() => reloadTestingDatabases(connections));
after(() => closeTestingConnections(connections));
it("should load ids when both entities have multiple primary keys", () => Promise.all(connections.map(async connection => {
const category1 = new Category();
category1.id = 1;
category1.code = 1;
category1.name = "cars";
await connection.manager.persist(category1);
const category2 = new Category();
category2.id = 2;
category2.code = 2;
category2.name = "BMW";
await connection.manager.persist(category2);
const category3 = new Category();
category3.id = 3;
category3.code = 1;
category3.name = "airplanes";
await connection.manager.persist(category3);
const category4 = new Category();
category4.id = 4;
category4.code = 2;
category4.name = "Boeing";
await connection.manager.persist(category4);
const post1 = new Post();
post1.id = 1;
post1.authorId = 1;
post1.title = "About BMW";
post1.categories = [category1, category2];
await connection.manager.persist(post1);
const post2 = new Post();
post2.id = 2;
post2.authorId = 1;
post2.title = "About Boeing";
post2.categories = [category3, category4];
await connection.manager.persist(post2);
const loadedPosts = await connection.manager
.createQueryBuilder(Post, "post")
.loadRelationIdAndMap("post.categoryIds", "post.categories")
.getMany();
expect(loadedPosts[0].categoryIds).to.not.be.empty;
expect(loadedPosts[0].categoryIds[0]).to.be.eql({ id: 1, code: 1 });
expect(loadedPosts[0].categoryIds[1]).to.be.eql({ id: 2, code: 2 });
expect(loadedPosts[1].categoryIds).to.not.be.empty;
expect(loadedPosts[1].categoryIds[0]).to.be.eql({ id: 3, code: 1 });
expect(loadedPosts[1].categoryIds[1]).to.be.eql({ id: 4, code: 2 });
const loadedPost = await connection.manager
.createQueryBuilder(Post, "post")
.loadRelationIdAndMap("post.categoryIds", "post.categories")
.where("post.id = :id", { id: 1 })
.andWhere("post.authorId = :authorId", { authorId: 1 })
.getOne();
expect(loadedPost!.categoryIds).to.not.be.empty;
expect(loadedPost!.categoryIds[0]).to.be.eql({ id: 1, code: 1 });
expect(loadedPost!.categoryIds[1]).to.be.eql({ id: 2, code: 2 });
})));
});

View File

@ -1,29 +0,0 @@
import {Entity} from "../../../../../../src/decorator/entity/Entity";
import {PrimaryGeneratedColumn} from "../../../../../../src/decorator/columns/PrimaryGeneratedColumn";
import {Column} from "../../../../../../src/decorator/columns/Column";
import {ManyToMany} from "../../../../../../src/decorator/relations/ManyToMany";
import {JoinTable} from "../../../../../../src/decorator/relations/JoinTable";
import {Post} from "./Post";
import {Image} from "./Image";
@Entity()
export class Category {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@ManyToMany(type => Post, post => post.categories)
posts: Post[];
@ManyToMany(type => Image)
@JoinTable()
images: Image[];
imageIds: number[];
postIds: number[];
}

View File

@ -1,14 +0,0 @@
import {Entity} from "../../../../../../src/decorator/entity/Entity";
import {PrimaryGeneratedColumn} from "../../../../../../src/decorator/columns/PrimaryGeneratedColumn";
import {Column} from "../../../../../../src/decorator/columns/Column";
@Entity()
export class Image {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
}

View File

@ -1,34 +0,0 @@
import {ManyToMany} from "../../../../../../src/decorator/relations/ManyToMany";
import {Entity} from "../../../../../../src/decorator/entity/Entity";
import {PrimaryGeneratedColumn} from "../../../../../../src/decorator/columns/PrimaryGeneratedColumn";
import {Column} from "../../../../../../src/decorator/columns/Column";
import {ManyToOne} from "../../../../../../src/decorator/relations/ManyToOne";
import {JoinTable} from "../../../../../../src/decorator/relations/JoinTable";
import {Category} from "./Category";
import {Tag} from "./Tag";
@Entity()
export class Post {
@PrimaryGeneratedColumn()
id: number;
@Column()
title: string;
@ManyToOne(type => Tag)
tag: Tag;
tagId: number;
@ManyToMany(type => Category, category => category.posts)
@JoinTable()
categories: Category[];
@ManyToMany(type => Category)
@JoinTable()
subcategories: Category[];
categoryIds: number[];
}

View File

@ -1,14 +0,0 @@
import {Entity} from "../../../../../../src/decorator/entity/Entity";
import {PrimaryGeneratedColumn} from "../../../../../../src/decorator/columns/PrimaryGeneratedColumn";
import {Column} from "../../../../../../src/decorator/columns/Column";
@Entity()
export class Tag {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
}

View File

@ -1,7 +1,7 @@
import "reflect-metadata";
import * as chai from "chai";
import {expect} from "chai";
import {createTestingConnections, closeTestingConnections, reloadTestingDatabases} from "../../../../utils/test-utils";
import {closeTestingConnections, createTestingConnections, reloadTestingDatabases} from "../../../../utils/test-utils";
import {Connection} from "../../../../../src/connection/Connection";
import {Post} from "./entity/Post";
import {Category} from "./entity/Category";
@ -25,33 +25,33 @@ describe("query builder > load-relation-id-and-map > many-to-one", () => {
const category1 = new Category();
category1.name = "cars";
await connection.entityManager.persist(category1);
await connection.manager.persist(category1);
const category2 = new Category();
category2.name = "airplanes";
await connection.entityManager.persist(category2);
await connection.manager.persist(category2);
const categoryByName1 = new Category();
categoryByName1.name = "BMW";
await connection.entityManager.persist(categoryByName1);
await connection.manager.persist(categoryByName1);
const categoryByName2 = new Category();
categoryByName2.name = "Boeing";
await connection.entityManager.persist(categoryByName2);
await connection.manager.persist(categoryByName2);
const post1 = new Post();
post1.title = "about BWM";
post1.category = category1;
post1.categoryByName = categoryByName1;
await connection.entityManager.persist(post1);
await connection.manager.persist(post1);
const post2 = new Post();
post2.title = "about Boeing";
post2.category = category2;
post2.categoryByName = categoryByName2;
await connection.entityManager.persist(post2);
await connection.manager.persist(post2);
let loadedPosts = await connection.entityManager
let loadedPosts = await connection.manager
.createQueryBuilder(Post, "post")
.loadRelationIdAndMap("post.categoryId", "post.category")
.loadRelationIdAndMap("post.categoryName", "post.categoryByName")
@ -66,7 +66,7 @@ describe("query builder > load-relation-id-and-map > many-to-one", () => {
expect(loadedPosts![1].categoryName).to.not.be.empty;
expect(loadedPosts![1].categoryName).to.be.equal("Boeing");
let loadedPost = await connection.entityManager
let loadedPost = await connection.manager
.createQueryBuilder(Post, "post")
.loadRelationIdAndMap("post.categoryId", "post.category")
.loadRelationIdAndMap("post.categoryName", "post.categoryByName")
@ -83,18 +83,18 @@ describe("query builder > load-relation-id-and-map > many-to-one", () => {
const category = new Category();
category.name = "cars";
await connection.entityManager.persist(category);
await connection.manager.persist(category);
const post = new Post();
post.title = "about cars";
await connection.entityManager.persist(post);
await connection.manager.persist(post);
const postCategory = new PostCategory();
postCategory.category = category;
postCategory.post = post;
await connection.entityManager.persist(postCategory);
await connection.manager.persist(postCategory);
let loadedPostCategory = await connection.entityManager
let loadedPostCategory = await connection.manager
.createQueryBuilder(PostCategory, "postCategory")
.loadRelationIdAndMap("postCategory.postId", "postCategory.post")
.loadRelationIdAndMap("postCategory.categoryId", "postCategory.category")
@ -110,23 +110,23 @@ describe("query builder > load-relation-id-and-map > many-to-one", () => {
const category = new Category();
category.name = "cars";
await connection.entityManager.persist(category);
await connection.manager.persist(category);
const post = new Post();
post.title = "about cars";
await connection.entityManager.persist(post);
await connection.manager.persist(post);
const image = new Image();
image.name = "image #1";
await connection.entityManager.persist(image);
await connection.manager.persist(image);
const postCategory = new PostCategory();
postCategory.category = category;
postCategory.post = post;
postCategory.image = image;
await connection.entityManager.persist(postCategory);
await connection.manager.persist(postCategory);
let loadedPostCategory = await connection.entityManager
let loadedPostCategory = await connection.manager
.createQueryBuilder(PostCategory, "postCategory")
.loadRelationIdAndMap("postCategory.imageId", "postCategory.image")
.getOne();

View File

@ -1,21 +0,0 @@
import {Entity} from "../../../../../../src/decorator/entity/Entity";
import {PrimaryGeneratedColumn} from "../../../../../../src/decorator/columns/PrimaryGeneratedColumn";
import {Column} from "../../../../../../src/decorator/columns/Column";
import {OneToMany} from "../../../../../../src/decorator/relations/OneToMany";
import {Post} from "./Post";
@Entity()
export class Category {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@OneToMany(type => Post, post => post.category)
posts: Post[];
postIds: number[];
}

View File

@ -1,21 +0,0 @@
import {Entity} from "../../../../../../src/decorator/entity/Entity";
import {PrimaryGeneratedColumn} from "../../../../../../src/decorator/columns/PrimaryGeneratedColumn";
import {Column} from "../../../../../../src/decorator/columns/Column";
import {ManyToOne} from "../../../../../../src/decorator/relations/ManyToOne";
import {Category} from "./Category";
@Entity()
export class Post {
@PrimaryGeneratedColumn()
id: number;
@Column()
title: string;
@ManyToOne(type => Category, category => category.posts)
category: Category;
categoryId: number;
}

View File

@ -1,75 +0,0 @@
import "reflect-metadata";
import * as chai from "chai";
import {expect} from "chai";
import {createTestingConnections, closeTestingConnections, reloadTestingDatabases} from "../../../../utils/test-utils";
import {Connection} from "../../../../../src/connection/Connection";
import {Category} from "./entity/Category";
import {Post} from "./entity/Post";
const should = chai.should();
describe("query builder > load-relation-id-and-map > one-to-many", () => {
let connections: Connection[];
before(async () => connections = await createTestingConnections({
entities: [__dirname + "/entity/*{.js,.ts}"],
schemaCreate: true,
dropSchemaOnConnection: true,
}));
beforeEach(() => reloadTestingDatabases(connections));
after(() => closeTestingConnections(connections));
it("should load id when loadRelationIdAndMap used with OneToMany relation", () => Promise.all(connections.map(async connection => {
const category = new Category();
category.name = "cars";
await connection.entityManager.persist(category);
const post1 = new Post();
post1.title = "about BMW";
post1.category = category;
await connection.entityManager.persist(post1);
const post2 = new Post();
post2.title = "about Audi";
post2.category = category;
await connection.entityManager.persist(post2);
let loadedCategory = await connection.entityManager
.createQueryBuilder(Category, "category")
.loadRelationIdAndMap("category.postIds", "category.posts")
.getOne();
expect(loadedCategory!.postIds).to.not.be.empty;
expect(loadedCategory!.postIds.length).to.be.equal(2);
expect(loadedCategory!.postIds[0]).to.be.equal(1);
expect(loadedCategory!.postIds[1]).to.be.equal(2);
})));
it("should load id when loadRelationIdAndMap used with OneToMany relation and additional condition", () => Promise.all(connections.map(async connection => {
const category = new Category();
category.name = "cars";
await connection.entityManager.persist(category);
const post1 = new Post();
post1.title = "about BMW";
post1.category = category;
await connection.entityManager.persist(post1);
const post2 = new Post();
post2.title = "about Audi";
post2.category = category;
await connection.entityManager.persist(post2);
let loadedCategory = await connection.entityManager
.createQueryBuilder(Category, "category")
.loadRelationIdAndMap("category.postIds", "category.posts", "posts", qb => qb.andWhere("posts.id = :postId", { postId: 1 }))
.getOne();
expect(loadedCategory!.postIds).to.not.be.empty;
expect(loadedCategory!.postIds.length).to.be.equal(1);
expect(loadedCategory!.postIds[0]).to.be.equal(1);
})));
});

View File

@ -1,7 +1,7 @@
import "reflect-metadata";
import * as chai from "chai";
import {expect} from "chai";
import {createTestingConnections, closeTestingConnections, reloadTestingDatabases} from "../../../../utils/test-utils";
import {closeTestingConnections, createTestingConnections, reloadTestingDatabases} from "../../../../utils/test-utils";
import {Connection} from "../../../../../src/connection/Connection";
import {Category} from "./entity/Category";
import {Post} from "./entity/Post";
@ -24,23 +24,23 @@ describe("query builder > load-relation-id-and-map > one-to-one", () => {
const category = new Category();
category.name = "kids";
await connection.entityManager.persist(category);
await connection.manager.persist(category);
const post = new Post();
post.title = "about kids";
post.category = category;
await connection.entityManager.persist(post);
await connection.manager.persist(post);
const category2 = new Category();
category2.name = "cars";
await connection.entityManager.persist(category2);
await connection.manager.persist(category2);
const post2 = new Post();
post2.title = "about cars";
post2.category = category2;
await connection.entityManager.persist(post2);
await connection.manager.persist(post2);
let loadedPosts = await connection.entityManager
let loadedPosts = await connection.manager
.createQueryBuilder(Post, "post")
.loadRelationIdAndMap("post.categoryId", "post.category")
.getMany();
@ -50,7 +50,7 @@ describe("query builder > load-relation-id-and-map > one-to-one", () => {
expect(loadedPosts![1].categoryId).to.not.be.empty;
expect(loadedPosts![1].categoryId).to.be.equal(2);
let loadedPost = await connection.entityManager
let loadedPost = await connection.manager
.createQueryBuilder(Post, "post")
.loadRelationIdAndMap("post.categoryId", "post.category")
.where("post.id = :id", { id: post.id })
@ -64,23 +64,23 @@ describe("query builder > load-relation-id-and-map > one-to-one", () => {
const category = new Category();
category.name = "kids";
await connection.entityManager.persist(category);
await connection.manager.persist(category);
const post = new Post();
post.title = "about kids";
post.category2 = category;
await connection.entityManager.persist(post);
await connection.manager.persist(post);
const category2 = new Category();
category2.name = "cars";
await connection.entityManager.persist(category2);
await connection.manager.persist(category2);
const post2 = new Post();
post2.title = "about cars";
post2.category2 = category2;
await connection.entityManager.persist(post2);
await connection.manager.persist(post2);
let loadedCategories = await connection.entityManager
let loadedCategories = await connection.manager
.createQueryBuilder(Category, "category")
.loadRelationIdAndMap("category.postId", "category.post")
.getMany();
@ -90,7 +90,7 @@ describe("query builder > load-relation-id-and-map > one-to-one", () => {
expect(loadedCategories![1].postId).to.not.be.empty;
expect(loadedCategories![1].postId).to.be.equal(2);
let loadedCategory = await connection.entityManager
let loadedCategory = await connection.manager
.createQueryBuilder(Category, "category")
.loadRelationIdAndMap("category.postId", "category.post")
.getOne();

View File

@ -191,4 +191,17 @@ export function setupConnection(callback: (connection: Connection) => any, entit
return connection;
});
};
}
/**
* Generates random text array with custom length.
*/
export function generateRandomText(length: number): string {
let text = "";
const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
for(let i = 0; i <= length; i++ )
text += characters.charAt(Math.floor(Math.random() * characters.length));
return text;
}