working on RelationId tests;

This commit is contained in:
Zotov Dmitry 2017-05-16 13:57:00 +05:00
parent 1b5502c143
commit 83b8f0e418
15 changed files with 1120 additions and 17 deletions

View File

@ -383,7 +383,9 @@ export class RelationMetadata {
this.inverseJoinColumns = this.foreignKeys[1] ? this.foreignKeys[1].columns : [];
this.isOwning = this.isManyToOne || ((this.isManyToMany || this.isOneToOne) && this.joinColumns.length > 0);
this.isOneToOneOwner = this.isOneToOne && this.isOwning;
this.isOneToOneNotOwner = this.isOneToOne && !this.isOwning;
this.isManyToManyOwner = this.isManyToMany && this.isOwning;
this.isManyToManyNotOwner = this.isManyToMany && !this.isOwning;
this.isWithJoinColumn = this.isManyToOne || this.isOneToOneOwner;
}

View File

@ -1,5 +1,4 @@
import {RelationIdAttribute} from "./RelationIdAttribute";
import {ColumnMetadata} from "../../metadata/ColumnMetadata";
import {QueryBuilder} from "../QueryBuilder";
import {Connection} from "../../connection/Connection";
import {QueryRunnerProvider} from "../../query-runner/QueryRunnerProvider";
@ -27,11 +26,11 @@ export class RelationIdLoader {
if (relationIdAttr.relation.isManyToOne || relationIdAttr.relation.isOneToOneOwner) {
// example: Post and Tag
// loadRelationIdAndMap("post.tagId", "post.tag") post_tag
// loadRelationIdAndMap("post.tagId", "post.tag")
// we expect it to load id of tag
if (relationIdAttr.queryBuilderFactory)
throw new Error(""); // todo: fix
throw new Error("Additional condition can not be used with ManyToOne or OneToOne owner relations.");
const results = rawEntities.map(rawEntity => {
const result: ObjectLiteral = {};
@ -168,11 +167,4 @@ export class RelationIdLoader {
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

@ -199,6 +199,7 @@ export class RawSqlResultsToEntityTransformer {
}
hasData = true;
});
return hasData;
}

View File

@ -36,4 +36,19 @@ describe.skip("benchmark > bulk-save", () => {
})));
/**
* Before getters refactoring
*
testing bulk save of 1000 objects (3149ms)
testing bulk save of 1000 objects (2008ms)
testing bulk save of 1000 objects (1893ms)
testing bulk save of 1000 objects (1744ms)
testing bulk save of 1000 objects (1836ms)
testing bulk save of 1000 objects (1787ms)
testing bulk save of 1000 objects (1904ms)
testing bulk save of 1000 objects (1848ms)
testing bulk save of 1000 objects (1947ms)
testing bulk save of 1000 objects (2004ms)
*/
});

View File

@ -0,0 +1,38 @@
import {Entity} from "../../../../../../../src/decorator/entity/Entity";
import {Column} from "../../../../../../../src/decorator/columns/Column";
import {PrimaryColumn} from "../../../../../../../src/decorator/columns/PrimaryColumn";
import {Index} from "../../../../../../../src/decorator/Index";
import {JoinTable} from "../../../../../../../src/decorator/relations/JoinTable";
import {OneToMany} from "../../../../../../../src/decorator/relations/OneToMany";
import {ManyToOne} from "../../../../../../../src/decorator/relations/ManyToOne";
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;
@OneToMany(type => Post, post => post.category)
posts: Post[];
@ManyToOne(type => Image, image => image.categories)
@JoinTable()
image: Image;
postIds: number[];
imageId: 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 Image {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@OneToMany(type => Category, category => category.image)
categories: Category[];
categoryIds: number[];
}

View File

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

View File

@ -0,0 +1,244 @@
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-one > 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 = 1;
category2.name = "airplanes";
await connection.manager.persist(category2);
const post1 = new Post();
post1.id = 1;
post1.authorId = 1;
post1.title = "About BMW";
post1.category = category1;
await connection.manager.persist(post1);
const post2 = new Post();
post2.id = 2;
post2.authorId = 1;
post2.title = "About Audi";
post2.category = category1;
await connection.manager.persist(post2);
const post3 = new Post();
post3.id = 3;
post3.authorId = 2;
post3.title = "About Boeing";
post3.category = category2;
await connection.manager.persist(post3);
const loadedPosts = await connection.manager
.createQueryBuilder(Post, "post")
.loadRelationIdAndMap("post.categoryId", "post.category")
.getMany();
expect(loadedPosts[0].categoryId).to.be.eql({ id: 1, code: 1 });
expect(loadedPosts[1].categoryId).to.be.eql({ id: 1, code: 1 });
expect(loadedPosts[2].categoryId).to.be.eql({ id: 2, code: 1 });
const loadedPost = await connection.manager
.createQueryBuilder(Post, "post")
.loadRelationIdAndMap("post.categoryId", "post.category")
.where("post.id = :id", { id: 1 })
.andWhere("post.authorId = :authorId", { authorId: 1 })
.getOne();
expect(loadedPost!.categoryId).to.be.eql({ id: 1, code: 1 });
})));
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 category1 = new Category();
category1.id = 1;
category1.code = 1;
category1.name = "cars";
category1.image = image1;
await connection.manager.persist(category1);
const category2 = new Category();
category2.id = 2;
category2.code = 2;
category2.name = "airplanes";
category2.image = image2;
await connection.manager.persist(category2);
const loadedCategories = await connection.manager
.createQueryBuilder(Category, "category")
.loadRelationIdAndMap("category.imageId", "category.image")
.getMany();
expect(loadedCategories[0].imageId).to.be.equal(1);
expect(loadedCategories[1].imageId).to.be.equal(2);
const loadedCategory = await connection.manager
.createQueryBuilder(Category, "category")
.loadRelationIdAndMap("category.imageId", "category.image")
.where("category.id = :id", { id: 1 })
.andWhere("category.code = :code", { code: 1 })
.getOne();
expect(loadedCategory!.imageId).to.be.equal(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 = 1;
category2.name = "airplanes";
await connection.manager.persist(category2);
const post1 = new Post();
post1.id = 1;
post1.authorId = 1;
post1.title = "About BMW";
post1.subcategory = category1;
await connection.manager.persist(post1);
const post2 = new Post();
post2.id = 2;
post2.authorId = 1;
post2.title = "About Audi";
post2.subcategory = category1;
await connection.manager.persist(post2);
const post3 = new Post();
post3.id = 3;
post3.authorId = 2;
post3.title = "About Boeing";
post3.subcategory = category2;
await connection.manager.persist(post3);
const loadedPosts = await connection.manager
.createQueryBuilder(Post, "post")
.loadRelationIdAndMap("post.categoryId", "post.subcategory")
.getMany();
expect(loadedPosts[0].categoryId).to.be.eql({ id: 1, code: 1 });
expect(loadedPosts[1].categoryId).to.be.eql({ id: 1, code: 1 });
expect(loadedPosts[2].categoryId).to.be.eql({ id: 2, code: 1 });
const loadedPost = await connection.manager
.createQueryBuilder(Post, "post")
.loadRelationIdAndMap("post.categoryId", "post.subcategory")
.where("post.id = :id", { id: 1 })
.andWhere("post.authorId = :authorId", { authorId: 1 })
.getOne();
expect(loadedPost!.categoryId).to.be.eql({ id: 1, code: 1 });
})));
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 category1 = new Category();
category1.id = 1;
category1.code = 1;
category1.name = "cars";
category1.image = image1;
await connection.manager.persist(category1);
const category2 = new Category();
category2.id = 2;
category2.code = 1;
category2.name = "airplanes";
category2.image = image2;
await connection.manager.persist(category2);
const post1 = new Post();
post1.id = 1;
post1.authorId = 1;
post1.title = "About BMW";
post1.category = category1;
await connection.manager.persist(post1);
const post2 = new Post();
post2.id = 2;
post2.authorId = 1;
post2.title = "About Boeing";
post2.category = category2;
await connection.manager.persist(post2);
const loadedPosts = await connection.manager
.createQueryBuilder(Post, "post")
.loadRelationIdAndMap("post.categoryId", "post.category")
.leftJoinAndSelect("post.category", "category")
.loadRelationIdAndMap("category.imageId", "category.image")
.getMany();
expect(loadedPosts[0].categoryId).to.be.eql({ id: 1, code: 1 });
expect(loadedPosts[0].category.imageId).to.be.equal(1);
expect(loadedPosts[1].categoryId).to.be.eql({ id: 2, code: 1 });
expect(loadedPosts[1].category.imageId).to.be.equal(2);
const loadedPost = await connection.manager
.createQueryBuilder(Post, "post")
.loadRelationIdAndMap("post.categoryId", "post.category")
.leftJoinAndSelect("post.category", "category")
.loadRelationIdAndMap("category.imageId", "category.image")
.where("post.id = :id", { id: 1 })
.andWhere("post.authorId = :authorId", { authorId: 1 })
.getOne();
expect(loadedPost!.categoryId).to.be.eql({ id: 1, code: 1 });
expect(loadedPost!.category.imageId).to.be.equal(1);
})));
});

View File

@ -1,9 +1,11 @@
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";
import {OneToMany} from "../../../../../../../src/decorator/relations/OneToMany";
import {Post} from "./Post";
import {Image} from "./Image";
@Entity()
@Index(["id", "code"])
@ -18,9 +20,17 @@ export class Category {
@Column()
name: string;
@Column()
isRemoved: boolean = false;
@ManyToOne(type => Post, post => post.categories)
post: Post;
@OneToMany(type => Image, image => image.category)
images: Image[];
postId: 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 {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

@ -9,6 +9,7 @@ import {
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();
@ -69,22 +70,268 @@ describe("query builder > relation-id > one-to-many > multiple-pk", () => {
.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[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 });
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
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 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];
await connection.manager.persist(category1);
const category2 = new Category();
category2.id = 2;
category2.code = 1;
category2.name = "airplanes";
category2.images = [image3, image4];
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(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[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(3);
expect(loadedPosts[1].categories[0].imageIds[1]).to.be.equal(4);
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!.categoryIds[1]).to.be.eql({ id: 2, code: 2 });
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(2);
expect(loadedPost!.categories[0].imageIds[0]).to.be.equal(1);
expect(loadedPost!.categories[0].imageIds[1]).to.be.equal(2);
})));

View File

@ -0,0 +1,37 @@
import {Entity} from "../../../../../../../src/decorator/entity/Entity";
import {Column} from "../../../../../../../src/decorator/columns/Column";
import {PrimaryColumn} from "../../../../../../../src/decorator/columns/PrimaryColumn";
import {Index} from "../../../../../../../src/decorator/Index";
import {Post} from "./Post";
import {Image} from "./Image";
import {OneToOne} from "../../../../../../../src/decorator/relations/OneToOne";
import {JoinColumn} from "../../../../../../../src/decorator/relations/JoinColumn";
@Entity()
@Index(["id", "code"])
export class Category {
@PrimaryColumn()
id: number;
@PrimaryColumn()
code: number;
@Column()
name: string;
@Column()
isRemoved: boolean = false;
@OneToOne(type => Post, post => post.category)
post: Post;
@OneToOne(type => Image, image => image.category)
@JoinColumn()
image: Image;
postId: number;
imageId: 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 {OneToOne} from "../../../../../../../src/decorator/relations/OneToOne";
import {Category} from "./Category";
@Entity()
export class Image {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@OneToOne(type => Category, category => category.image)
category: Category;
categoryId: number;
}

View File

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

View File

@ -0,0 +1,387 @@
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 > one-to-one > 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 = 1;
category2.name = "airplanes";
await connection.manager.persist(category2);
const post1 = new Post();
post1.id = 1;
post1.authorId = 1;
post1.title = "About BMW";
post1.category = category1;
await connection.manager.persist(post1);
const post2 = new Post();
post2.id = 2;
post2.authorId = 1;
post2.title = "About Boeing";
post2.category = category2;
await connection.manager.persist(post2);
const loadedPosts = await connection.manager
.createQueryBuilder(Post, "post")
.loadRelationIdAndMap("post.categoryId", "post.category")
.getMany();
expect(loadedPosts[0].categoryId).to.be.eql({ id: 1, code: 1 });
expect(loadedPosts[1].categoryId).to.be.eql({ id: 2, code: 1 });
const loadedPost = await connection.manager
.createQueryBuilder(Post, "post")
.loadRelationIdAndMap("post.categoryId", "post.category")
.where("post.id = :id", { id: 1 })
.andWhere("post.authorId = :authorId", { authorId: 1 })
.getOne();
expect(loadedPost!.categoryId).to.be.eql({ id: 1, code: 1 });
})));
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 category1 = new Category();
category1.id = 1;
category1.code = 1;
category1.name = "cars";
category1.image = image1;
await connection.manager.persist(category1);
const category2 = new Category();
category2.id = 2;
category2.code = 1;
category2.name = "airplanes";
category2.image = image2;
await connection.manager.persist(category2);
const loadedCategories = await connection.manager
.createQueryBuilder(Category, "category")
.loadRelationIdAndMap("category.imageId", "category.image")
.getMany();
expect(loadedCategories[0].imageId).to.be.equal(1);
expect(loadedCategories[1].imageId).to.be.equal(2);
const loadedCategory = await connection.manager
.createQueryBuilder(Category, "category")
.loadRelationIdAndMap("category.imageId", "category.image")
.where("category.id = :id", { id: 1 })
.andWhere("category.code = :code", { code: 1 })
.getOne();
expect(loadedCategory!.imageId).to.be.equal(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 = 1;
category2.name = "airplanes";
await connection.manager.persist(category2);
const post1 = new Post();
post1.id = 1;
post1.authorId = 1;
post1.title = "About BMW";
post1.subcategory = category1;
await connection.manager.persist(post1);
const post2 = new Post();
post2.id = 2;
post2.authorId = 1;
post2.title = "About Boeing";
post2.subcategory = category2;
await connection.manager.persist(post2);
const loadedPosts = await connection.manager
.createQueryBuilder(Post, "post")
.loadRelationIdAndMap("post.categoryId", "post.subcategory")
.getMany();
expect(loadedPosts[0].categoryId).to.be.eql({ id: 1, code: 1 });
expect(loadedPosts[1].categoryId).to.be.eql({ id: 2, code: 1 });
const loadedPost = await connection.manager
.createQueryBuilder(Post, "post")
.loadRelationIdAndMap("post.categoryId", "post.subcategory")
.where("post.id = :id", { id: 1 })
.andWhere("post.authorId = :authorId", { authorId: 1 })
.getOne();
expect(loadedPost!.categoryId).to.be.eql({ id: 1, code: 1 });
})));
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 category1 = new Category();
category1.id = 1;
category1.code = 1;
category1.name = "cars";
category1.image = image1;
await connection.manager.persist(category1);
const category2 = new Category();
category2.id = 2;
category2.code = 1;
category2.name = "airplanes";
category2.image = image2;
await connection.manager.persist(category2);
const post1 = new Post();
post1.id = 1;
post1.authorId = 1;
post1.title = "About BMW";
post1.category = category1;
await connection.manager.persist(post1);
const post2 = new Post();
post2.id = 2;
post2.authorId = 1;
post2.title = "About Boeing";
post2.category = category2;
await connection.manager.persist(post2);
const loadedPosts = await connection.manager
.createQueryBuilder(Post, "post")
.loadRelationIdAndMap("post.categoryId", "post.category")
.leftJoinAndSelect("post.category", "category")
.loadRelationIdAndMap("category.imageId", "category.image")
.getMany();
expect(loadedPosts[0].categoryId).to.be.eql({ id: 1, code: 1 });
expect(loadedPosts[0].category.imageId).to.be.equal(1);
expect(loadedPosts[1].categoryId).to.be.eql({ id: 2, code: 1 });
expect(loadedPosts[1].category.imageId).to.be.equal(2);
const loadedPost = await connection.manager
.createQueryBuilder(Post, "post")
.loadRelationIdAndMap("post.categoryId", "post.category")
.leftJoinAndSelect("post.category", "category")
.loadRelationIdAndMap("category.imageId", "category.image")
.where("post.id = :id", { id: 1 })
.andWhere("post.authorId = :authorId", { authorId: 1 })
.getOne();
expect(loadedPost!.categoryId).to.be.eql({ id: 1, code: 1 });
expect(loadedPost!.category.imageId).to.be.equal(1);
})));
});
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 = 1;
post2.title = "About Boeing";
await connection.manager.persist(post2);
const category1 = new Category();
category1.id = 1;
category1.code = 1;
category1.name = "cars";
category1.post = post1;
await connection.manager.persist(category1);
const category2 = new Category();
category2.id = 2;
category2.code = 1;
category2.name = "airplanes";
category2.post = post2;
await connection.manager.persist(category2);
const loadedCategories = await connection.manager
.createQueryBuilder(Category, "category")
.loadRelationIdAndMap("category.postId", "category.post")
.getMany();
expect(loadedCategories[0].postId).to.be.eql({ id: 1, authorId: 1 });
expect(loadedCategories[1].postId).to.be.eql({ id: 2, authorId: 1 });
const loadedCategory = await connection.manager
.createQueryBuilder(Category, "category")
.loadRelationIdAndMap("category.postId", "category.post")
.where("category.id = :id", { id: 1 })
.andWhere("category.code = :code", { code: 1 })
.getOne();
expect(loadedCategory!.postId).to.be.eql({ id: 1, authorId: 1 });
})));
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 image1 = new Image();
image1.name = "Image #1";
image1.category = category1;
await connection.manager.persist(image1);
const image2 = new Image();
image2.name = "Image #2";
image2.category = category2;
await connection.manager.persist(image2);
const loadedImages = await connection.manager
.createQueryBuilder(Image, "image")
.loadRelationIdAndMap("image.categoryId", "image.category")
.getMany();
expect(loadedImages[0].categoryId).to.be.eql({ id: 1, code: 1 });
expect(loadedImages[1].categoryId).to.be.eql({ id: 2, code: 1 });
const loadedImage = await connection.manager
.createQueryBuilder(Image, "image")
.loadRelationIdAndMap("image.categoryId", "image.category")
.where("image.id = :id", { id: 1 })
.getOne();
expect(loadedImage!.categoryId).to.be.eql({ id: 1, code: 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 Boeing";
await connection.manager.persist(post2);
const category1 = new Category();
category1.id = 1;
category1.code = 1;
category1.name = "cars";
category1.post = post1;
await connection.manager.persist(category1);
const category2 = new Category();
category2.id = 2;
category2.code = 1;
category2.name = "BMW";
category2.post = post2;
await connection.manager.persist(category2);
const image1 = new Image();
image1.name = "Image #1";
image1.category = category1;
await connection.manager.persist(image1);
const image2 = new Image();
image2.name = "Image #2";
image2.category = category2;
await connection.manager.persist(image2);
const loadedImages = await connection.manager
.createQueryBuilder(Image, "image")
.loadRelationIdAndMap("image.categoryId", "image.category")
.leftJoinAndSelect("image.category", "category")
.loadRelationIdAndMap("category.postId", "category.post")
.getMany();
expect(loadedImages[0].categoryId).to.be.eql({ id: 1, code: 1 });
expect(loadedImages[0].category.postId).to.be.eql({ id: 1, authorId: 1 });
expect(loadedImages[1].categoryId).to.be.eql({ id: 2, code: 1 });
expect(loadedImages[1].category.postId).to.be.eql({ id: 2, authorId: 1 });
const loadedImage = await connection.manager
.createQueryBuilder(Image, "image")
.loadRelationIdAndMap("image.categoryId", "image.category")
.leftJoinAndSelect("image.category", "category")
.loadRelationIdAndMap("category.postId", "category.post")
.where("image.id = :id", { id: 1 })
.getOne();
expect(loadedImage!.categoryId).to.be.eql({ id: 1, code: 1 });
expect(loadedImage!.category.postId).to.be.eql({ id: 1, authorId: 1 });
})));
});
});