fixes #242 - bug with indices from columns are not being inherited from parent entity

This commit is contained in:
Umed Khudoiberdiev 2017-01-28 11:15:15 +05:00
parent 32dc7af1c2
commit 04e41ea6db
24 changed files with 9 additions and 1530 deletions

View File

@ -1,5 +1,7 @@
# 0.0.9 (next)
* fixed bug with indices from columns are not being inherited from parent entity [#242](https://github.com/typeorm/typeorm/issues/242)
# 0.0.8 (latest)
* added complete babel support

View File

@ -1,7 +1,7 @@
{
"name": "typeorm",
"private": true,
"version": "0.0.8",
"version": "0.0.9-alpha.1",
"description": "Data-Mapper ORM for TypeScript, ES7, ES6, ES5. Supports MySQL, PostgreSQL, MariaDB, SQLite, MS SQL Server, Oracle, WebSQL databases.",
"license": "MIT",
"readmeFilename": "README.md",

View File

@ -12,4 +12,8 @@ export class BasePost {
@Column({ unique: true })
text: string;
@Index()
@Column()
extra: string;
}

View File

@ -1,4 +1,4 @@
import {PrimaryGeneratedColumn, Column, Entity} from "../../../src/index";
import {Column, Entity} from "../../../src/index";
import {Index} from "../../../src/decorator/Index";
import {BasePost} from "./BasePost";
@ -6,9 +6,6 @@ import {BasePost} from "./BasePost";
@Index("my_index_with_id_and_title", (post: Post) => [post.id, post.title])
export class Post extends BasePost {
@PrimaryGeneratedColumn()
id: number;
@Column()
@Index()
title: string;

View File

@ -120,7 +120,7 @@ export class MetadataArgsStorage {
metadatasFromAbstract.indices
.toArray()
.filter(index => { // make sure we don't have index with such name already
return !indices.toArray().find(existIndex => existIndex.name === index.name);
return !index.name || !indices.toArray().find(existIndex => existIndex.name === index.name);
})
.forEach(index => indices.add(index));

View File

@ -1,27 +0,0 @@
import {Entity} from "../../../../../src/decorator/entity/Entity";
import {PrimaryGeneratedColumn} from "../../../../../src/decorator/columns/PrimaryGeneratedColumn";
import {Column} from "../../../../../src/decorator/columns/Column";
import {Category} from "./Category";
import {ManyToMany} from "../../../../../src/decorator/relations/ManyToMany";
import {JoinTable} from "../../../../../src/decorator/relations/JoinTable";
@Entity()
export class Blog {
@PrimaryGeneratedColumn()
id: number;
@Column()
title: string;
@Column()
text: string;
@ManyToMany(type => Category)
@JoinTable()
categories: Category[];
@Column()
counter: number = 0;
}

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 Category {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
}

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 Post {
@PrimaryGeneratedColumn()
id: number|undefined|null|string;
@Column()
title: string;
}

View File

@ -1,20 +0,0 @@
export default {
name: "Question",
table: {
name: "question"
},
columns: {
id: {
type: "int",
primary: true,
generated: true
},
title: {
type: "string",
nullable: false
}
},
target: function Question() {
this.type = "question";
}
};

View File

@ -1,5 +0,0 @@
export interface Question {
id?: number;
title?: string;
type: string;
}

View File

@ -1,5 +0,0 @@
export interface User {
id?: number|null;
firstName?: string;
secondName?: string;
}

View File

@ -1,452 +0,0 @@
import "reflect-metadata";
import {createTestingConnections, closeTestingConnections, reloadTestingDatabases} from "../../../utils/test-utils";
import {Connection} from "../../../../src/connection/Connection";
import {Post} from "./entity/Post";
import {QueryBuilder} from "../../../../src/query-builder/QueryBuilder";
import {User} from "./model/User";
import questionSchema from "./model-schema/QuestionSchema";
import {Question} from "./model/Question";
import {Blog} from "./entity/Blog";
import {Category} from "./entity/Category";
describe("repository > basic methods", () => {
let userSchema: any;
try {
const resourceDir = __dirname + "/../../../../../../test/functional/repository/basic-methods/";
userSchema = require(resourceDir + "schema/user.json");
} catch (err) {
const resourceDir = __dirname + "/";
userSchema = require(resourceDir + "schema/user.json");
}
let connections: Connection[];
before(async () => connections = await createTestingConnections({
entities: [Post, Blog, Category],
entitySchemas: [userSchema, questionSchema],
schemaCreate: true,
dropSchemaOnConnection: true
}));
beforeEach(() => reloadTestingDatabases(connections));
after(() => closeTestingConnections(connections));
describe("target", function() {
it("should return instance of the object it manages", () => connections.forEach(connection => {
const postRepository = connection.getRepository(Post);
postRepository.target.should.be.equal(Post);
const userRepository = connection.getRepository<User>("User");
userRepository.target.should.be.equal("User");
const questionRepository = connection.getRepository<Question>("Question");
questionRepository.target.should.be.instanceOf(Function);
}));
});
describe("hasId", function() {
it("should return true if entity has an id", () => connections.forEach(connection => {
const postRepository = connection.getRepository(Post);
const userRepository = connection.getRepository("User");
const postWithId = new Post();
postWithId.id = 1;
postWithId.title = "Hello post";
postRepository.hasId(postWithId).should.be.equal(true);
const postWithZeroId = new Post();
postWithZeroId.id = 0;
postWithZeroId.title = "Hello post";
postRepository.hasId(postWithZeroId).should.be.equal(true);
const userWithId: User = {
id: 1,
firstName: "Jonh",
secondName: "Doe"
};
userRepository.hasId(userWithId).should.be.equal(true);
const userWithZeroId: User = {
id: 1,
firstName: "Jonh",
secondName: "Doe"
};
userRepository.hasId(userWithZeroId).should.be.equal(true);
}));
it("should return false if entity does not have an id", () => connections.forEach(connection => {
const postRepository = connection.getRepository(Post);
const userRepository = connection.getRepository("User");
postRepository.hasId(null as any).should.be.equal(false);
postRepository.hasId(undefined as any).should.be.equal(false);
const postWithoutId = new Post();
postWithoutId.title = "Hello post";
postRepository.hasId(postWithoutId).should.be.equal(false);
const postWithUndefinedId = new Post();
postWithUndefinedId.id = undefined;
postWithUndefinedId.title = "Hello post";
postRepository.hasId(postWithUndefinedId).should.be.equal(false);
const postWithNullId = new Post();
postWithNullId.id = null;
postWithNullId.title = "Hello post";
postRepository.hasId(postWithNullId).should.be.equal(false);
const postWithEmptyId = new Post();
postWithEmptyId.id = "";
postWithEmptyId.title = "Hello post";
postRepository.hasId(postWithEmptyId).should.be.equal(false);
const userWithoutId: User = {
firstName: "Jonh",
secondName: "Doe"
};
userRepository.hasId(userWithoutId).should.be.equal(false);
const userWithNullId: User = {
id: null,
firstName: "Jonh",
secondName: "Doe"
};
userRepository.hasId(userWithNullId).should.be.equal(false);
const userWithUndefinedId: User = {
id: undefined,
firstName: "Jonh",
secondName: "Doe"
};
userRepository.hasId(userWithUndefinedId).should.be.equal(false);
}));
});
describe("createQueryBuilder", function() {
it("should create a new query builder with the given alias", () => connections.forEach(connection => {
const postRepository = connection.getRepository(Post);
const postQb = postRepository.createQueryBuilder("post");
postQb.should.be.instanceOf(QueryBuilder);
postQb.alias.should.be.equal("post");
const userRepository = connection.getRepository("User");
const userQb = userRepository.createQueryBuilder("user");
userQb.should.be.instanceOf(QueryBuilder);
userQb.alias.should.be.equal("user");
const questionRepository = connection.getRepository("Question");
const questionQb = questionRepository.createQueryBuilder("question");
questionQb.should.be.instanceOf(QueryBuilder);
questionQb.alias.should.be.equal("question");
}));
});
describe("create", function() {
it("should create a new instance of the object we are working with", () => connections.forEach(connection => {
const repository = connection.getRepository(Post);
repository.create().should.be.instanceOf(Post);
}));
it("should create a new empty object if entity schema is used", () => connections.forEach(connection => {
const repository = connection.getRepository("User");
repository.create().should.be.eql({});
}));
it("should create a new empty object if entity schema with a target is used", () => connections.forEach(connection => {
const repository = connection.getRepository<Question>("Question");
repository.create().should.not.be.empty;
repository.create().type.should.be.equal("question"); // make sure this is our Question function
}));
it("should create an entity and copy to it all properties of the given plain object if its given", () => connections.forEach(connection => {
const postRepository = connection.getRepository(Post);
const userRepository = connection.getRepository<User>("User");
const questionRepository = connection.getRepository<Question>("Question");
const plainPost = { id: 2, title: "Hello post" };
const post = postRepository.create(plainPost);
post.should.be.instanceOf(Post);
(post.id as number).should.be.equal(2);
post.title.should.be.equal("Hello post");
const plainUser = { id: 3, firstName: "John", secondName: "Doe" };
const user = userRepository.create(plainUser);
(user.id as number).should.be.equal(3);
(user.firstName as string).should.be.equal("John");
(user.secondName as string).should.be.equal("Doe");
const plainQuestion = { id: 3, title: "What is better?" };
const question = questionRepository.create(plainQuestion);
(question.id as number).should.be.equal(3);
(question.title as string).should.be.equal("What is better?");
}));
});
describe("createMany", function() {
it("should create entities and copy to them all properties of the given plain object if its given", () => connections.forEach(connection => {
const postRepository = connection.getRepository(Post);
const plainPosts = [{ id: 2, title: "Hello post" }, { id: 3, title: "Bye post" }];
const posts = postRepository.create(plainPosts);
posts.length.should.be.equal(2);
posts[0].should.be.instanceOf(Post);
(posts[0].id as number).should.be.equal(2);
posts[0].title.should.be.equal("Hello post");
posts[1].should.be.instanceOf(Post);
(posts[1].id as number).should.be.equal(3);
posts[1].title.should.be.equal("Bye post");
}));
});
describe("preload", function() {
it("should preload entity from the given object with only id", () => Promise.all(connections.map(async connection => {
const blogRepository = connection.getRepository(Blog);
const categoryRepository = connection.getRepository(Category);
// save the category
const category = new Category();
category.name = "people";
await categoryRepository.persist(category);
// save the blog
const blog = new Blog();
blog.title = "About people";
blog.text = "Blog about good people";
blog.categories = [category];
await blogRepository.persist(blog);
// and preload it
const plainBlogWithId = { id: 1 };
const preloadedBlog = await blogRepository.preload(plainBlogWithId);
preloadedBlog.should.be.instanceOf(Blog);
preloadedBlog.id.should.be.equal(1);
preloadedBlog.title.should.be.equal("About people");
preloadedBlog.text.should.be.equal("Blog about good people");
})));
it("should preload entity and all relations given in the object", () => Promise.all(connections.map(async connection => {
const blogRepository = connection.getRepository(Blog);
const categoryRepository = connection.getRepository(Category);
// save the category
const category = new Category();
category.name = "people";
await categoryRepository.persist(category);
// save the blog
const blog = new Blog();
blog.title = "About people";
blog.text = "Blog about good people";
blog.categories = [category];
await blogRepository.persist(blog);
// and preload it
const plainBlogWithId = { id: 1, categories: [{ id: 1 }] };
const preloadedBlog = await blogRepository.preload(plainBlogWithId);
preloadedBlog.should.be.instanceOf(Blog);
preloadedBlog.id.should.be.equal(1);
preloadedBlog.title.should.be.equal("About people");
preloadedBlog.text.should.be.equal("Blog about good people");
preloadedBlog.categories[0].id.should.be.equal(1);
preloadedBlog.categories[0].name.should.be.equal("people");
})));
});
describe("merge", function() {
it("should merge multiple entities", () => Promise.all(connections.map(async connection => {
const blogRepository = connection.getRepository(Blog);
// first entity
const blog1 = new Blog();
blog1.title = "First Blog";
// second entity
const blog2 = new Blog();
blog2.text = "text is from second blog";
// third entity
const category = new Category();
category.name = "category from third blog";
const blog3 = new Blog();
blog3.categories = [category];
const mergedBlog = blogRepository.merge(blog1, blog2, blog3);
mergedBlog.should.be.instanceOf(Blog);
mergedBlog.should.not.be.equal(blog1);
mergedBlog.should.not.be.equal(blog2);
mergedBlog.should.not.be.equal(blog3);
mergedBlog.title.should.be.equal("First Blog");
mergedBlog.text.should.be.equal("text is from second blog");
mergedBlog.categories[0].name.should.be.equal("category from third blog");
})));
it("should merge both entities and plain objects", () => Promise.all(connections.map(async connection => {
const blogRepository = connection.getRepository(Blog);
// first entity
const blog1 = { title: "First Blog" };
// second entity
const blog2 = { text: "text is from second blog" };
// third entity
const blog3 = new Blog();
blog3.categories = [{ name: "category from third blog" } as Category];
const mergedBlog = blogRepository.merge(blog1, blog2, blog3);
mergedBlog.should.be.instanceOf(Blog);
mergedBlog.should.not.be.equal(blog1);
mergedBlog.should.not.be.equal(blog2);
mergedBlog.should.not.be.equal(blog3);
mergedBlog.title.should.be.equal("First Blog");
mergedBlog.text.should.be.equal("text is from second blog");
mergedBlog.categories[0].name.should.be.equal("category from third blog");
})));
});
describe("using preload and merge together", function() {
it("if we preload entity from the plain object and merge preloaded object with plain object we'll have an object from the db with the replaced properties by a plain object's properties", () => Promise.all(connections.map(async connection => {
const blogRepository = connection.getRepository(Blog);
const categoryRepository = connection.getRepository(Category);
// save first category
const firstCategory = new Category();
firstCategory.name = "people";
await categoryRepository.persist(firstCategory);
// save second category
const secondCategory = new Category();
secondCategory.name = "animals";
await categoryRepository.persist(secondCategory);
// save the blog
const blog = new Blog();
blog.title = "About people";
blog.text = "Blog about good people";
blog.categories = [firstCategory, secondCategory];
await blogRepository.persist(blog);
// and preload it
const plainBlogWithId = { id: 1, title: "changed title about people", categories: [ { id: 1 }, { id: 2, name: "insects" } ] };
const preloadedBlog = await blogRepository.preload(plainBlogWithId);
const mergedBlog = blogRepository.merge(preloadedBlog, plainBlogWithId);
mergedBlog.should.be.instanceOf(Blog);
mergedBlog.id.should.be.equal(1);
mergedBlog.title.should.be.equal("changed title about people");
mergedBlog.text.should.be.equal("Blog about good people");
mergedBlog.categories[0].id.should.be.equal(1);
mergedBlog.categories[0].name.should.be.equal("people");
mergedBlog.categories[1].id.should.be.equal(2);
mergedBlog.categories[1].name.should.be.equal("insects");
})));
});
describe("query", function() {
it("should execute the query natively and it should return the result", () => Promise.all(connections.map(async connection => {
const repository = connection.getRepository(Blog);
const promises: Promise<Blog>[] = [];
for (let i = 0; i < 5; i++) { // todo: should pass with 50 items. find the problem
const blog = new Blog();
blog.title = "hello blog";
blog.text = "hello blog #" + i;
blog.counter = i * 100;
promises.push(repository.persist(blog));
}
await Promise.all(promises);
// such simple query should work on all platforms, isn't it? If no - make requests specifically to platforms
const result = await repository.query("SELECT MAX(blog.counter) as max from blog blog");
result[0].should.not.be.empty;
result[0].max.should.not.be.empty;
})));
});
describe.skip("transaction", function() {
it("executed queries must success", () => Promise.all(connections.map(async connection => {
const repository = connection.getRepository(Blog);
let blogs = await repository.find();
blogs.should.be.eql([]);
const blog = new Blog();
blog.title = "hello blog title";
blog.text = "hello blog text";
await repository.persist(blog);
blogs.should.be.eql([]);
blogs = await repository.find();
blogs.length.should.be.equal(1);
await repository.transaction(async () => {
const promises: Promise<Blog>[] = [];
for (let i = 0; i < 100; i++) {
const blog = new Blog();
blog.title = "hello blog";
blog.text = "hello blog #" + i;
blog.counter = i * 100;
promises.push(repository.persist(blog));
}
await Promise.all(promises);
blogs = await repository.find();
blogs.length.should.be.equal(101);
});
blogs = await repository.find();
blogs.length.should.be.equal(101);
})));
it("executed queries must rollback in the case if error in transaction", () => Promise.all(connections.map(async connection => {
const repository = connection.getRepository(Blog);
let blogs = await repository.find();
blogs.should.be.eql([]);
const blog = new Blog();
blog.title = "hello blog title";
blog.text = "hello blog text";
await repository.persist(blog);
blogs.should.be.eql([]);
blogs = await repository.find();
blogs.length.should.be.equal(1);
await repository.transaction(async () => {
const promises: Promise<Blog>[] = [];
for (let i = 0; i < 100; i++) {
const blog = new Blog();
blog.title = "hello blog";
blog.text = "hello blog #" + i;
blog.counter = i * 100;
promises.push(repository.persist(blog));
}
await Promise.all(promises);
blogs = await repository.find();
blogs.length.should.be.equal(101);
// now send the query that will crash all for us
throw new Error("this error will cancel all persist operations");
}).should.be.rejected;
blogs = await repository.find();
blogs.length.should.be.equal(1);
})));
});
});

View File

@ -1,21 +0,0 @@
{
"name": "User",
"table": {
"name": "user"
},
"columns": {
"id": {
"type": "int",
"primary": true,
"generated": true
},
"firstName": {
"type": "string",
"nullable": false
},
"secondName": {
"type": "string",
"nullable": false
}
}
}

View File

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

View File

@ -1,68 +0,0 @@
import "reflect-metadata";
import {expect} from "chai";
import {createTestingConnections, closeTestingConnections, reloadTestingDatabases} from "../../../utils/test-utils";
import {Connection} from "../../../../src/connection/Connection";
import {Post} from "./entity/Post";
describe("repository > clear method", () => {
let connections: Connection[];
before(async () => connections = await createTestingConnections({
entities: [Post],
schemaCreate: true,
dropSchemaOnConnection: true
}));
beforeEach(() => reloadTestingDatabases(connections));
after(() => closeTestingConnections(connections));
it("should remove everything", () => Promise.all(connections.map(async connection => {
// save dummy data
const promises: Promise<Post>[] = [];
for (let i = 0; i < 100; i++) {
const post = new Post();
post.id = i;
post.title = "post #" + i;
promises.push(connection.entityManager.persist(post));
}
await Promise.all(promises);
// check if they all are saved
const loadedPosts = await connection.entityManager.find(Post);
loadedPosts.should.be.instanceOf(Array);
loadedPosts.length.should.be.equal(100);
await connection.getRepository(Post).clear();
// check find method
const loadedPostsAfterClear = await connection.entityManager.find(Post);
loadedPostsAfterClear.should.be.instanceOf(Array);
loadedPostsAfterClear.length.should.be.equal(0);
})));
it("called from entity managed should remove everything as well", () => Promise.all(connections.map(async connection => {
// save dummy data
const promises: Promise<Post>[] = [];
for (let i = 0; i < 100; i++) {
const post = new Post();
post.id = i;
post.title = "post #" + i;
promises.push(connection.entityManager.persist(post));
}
await Promise.all(promises);
// check if they all are saved
const loadedPosts = await connection.entityManager.find(Post);
loadedPosts.should.be.instanceOf(Array);
loadedPosts.length.should.be.equal(100);
await connection.entityManager.clear(Post);
// check find method
const loadedPostsAfterClear = await connection.entityManager.find(Post);
loadedPostsAfterClear.should.be.instanceOf(Array);
loadedPostsAfterClear.length.should.be.equal(0);
})));
});

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 Post {
@PrimaryGeneratedColumn()
id: number;
@Column()
title: string;
}

View File

@ -1,96 +0,0 @@
import "reflect-metadata";
import {expect} from "chai";
import {Connection} from "../../../../src/connection/Connection";
import {Post} from "./entity/Post";
import {createTestingConnections, reloadTestingDatabases, closeTestingConnections} from "../../../utils/test-utils";
describe("repository > removeById and removeByIds methods", function() {
// -------------------------------------------------------------------------
// Configuration
// -------------------------------------------------------------------------
let connections: Connection[];
before(async () => connections = await createTestingConnections({
entities: [__dirname + "/entity/*{.js,.ts}"],
schemaCreate: true,
dropSchemaOnConnection: true
}));
beforeEach(() => reloadTestingDatabases(connections));
after(() => closeTestingConnections(connections));
// -------------------------------------------------------------------------
// Specifications
// -------------------------------------------------------------------------
it("remove using removeById method should delete successfully", () => Promise.all(connections.map(async connection => {
const postRepository = connection.getRepository(Post);
const specificPostRepository = connection.getSpecificRepository(Post);
// save a new posts
const newPost1 = postRepository.create();
newPost1.title = "Super post #1";
const newPost2 = postRepository.create();
newPost2.title = "Super post #2";
const newPost3 = postRepository.create();
newPost3.title = "Super post #3";
const newPost4 = postRepository.create();
newPost4.title = "Super post #4";
await Promise.all([
postRepository.persist(newPost1),
postRepository.persist(newPost2),
postRepository.persist(newPost3),
postRepository.persist(newPost4)
]);
// remove one
await specificPostRepository.removeById(1);
// load to check
const loadedPosts = await postRepository.find();
// assert
loadedPosts.length.should.be.equal(3);
expect(loadedPosts.find(p => p.id === 1)).to.be.empty;
expect(loadedPosts.find(p => p.id === 2)).not.to.be.empty;
expect(loadedPosts.find(p => p.id === 3)).not.to.be.empty;
expect(loadedPosts.find(p => p.id === 4)).not.to.be.empty;
})));
it("remove using removeByIds method should delete successfully", () => Promise.all(connections.map(async connection => {
const postRepository = connection.getRepository(Post);
const specificPostRepository = connection.getSpecificRepository(Post);
// save a new posts
const newPost1 = postRepository.create();
newPost1.title = "Super post #1";
const newPost2 = postRepository.create();
newPost2.title = "Super post #2";
const newPost3 = postRepository.create();
newPost3.title = "Super post #3";
const newPost4 = postRepository.create();
newPost4.title = "Super post #4";
await Promise.all([
postRepository.persist(newPost1),
postRepository.persist(newPost2),
postRepository.persist(newPost3),
postRepository.persist(newPost4)
]);
// remove multiple
await specificPostRepository.removeByIds([2, 3]);
// load to check
const loadedPosts = await postRepository.find();
// assert
loadedPosts.length.should.be.equal(2);
expect(loadedPosts.find(p => p.id === 1)).not.to.be.empty;
expect(loadedPosts.find(p => p.id === 2)).to.be.empty;
expect(loadedPosts.find(p => p.id === 3)).to.be.empty;
expect(loadedPosts.find(p => p.id === 4)).not.to.be.empty;
})));
});

View File

@ -1,20 +0,0 @@
import {Entity} from "../../../../../src/decorator/entity/Entity";
import {Column} from "../../../../../src/decorator/columns/Column";
import {PrimaryColumn} from "../../../../../src/decorator/columns/PrimaryColumn";
@Entity()
export class Post {
@PrimaryColumn("int")
id: number;
@Column()
title: string;
@Column()
categoryName: string;
@Column()
isNew: boolean = false;
}

View File

@ -1,5 +0,0 @@
export interface User {
id: number;
firstName: string;
secondName: string;
}

View File

@ -1,376 +0,0 @@
import "reflect-metadata";
import {expect} from "chai";
import {createTestingConnections, closeTestingConnections, reloadTestingDatabases} from "../../../utils/test-utils";
import {Connection} from "../../../../src/connection/Connection";
import {Post} from "./entity/Post";
import {FindOptions} from "../../../../src/find-options/FindOptions";
import {User} from "./model/User";
describe("repository > find methods", () => {
let userSchema: any;
try {
const resourceDir = __dirname + "/../../../../../../test/functional/repository/find-methods/";
userSchema = require(resourceDir + "schema/user.json");
} catch (err) {
const resourceDir = __dirname + "/";
userSchema = require(resourceDir + "schema/user.json");
}
let connections: Connection[];
before(async () => connections = await createTestingConnections({
entities: [Post],
entitySchemas: [userSchema],
schemaCreate: true,
dropSchemaOnConnection: true
}));
beforeEach(() => reloadTestingDatabases(connections));
after(() => closeTestingConnections(connections));
describe("find and findAndCount", function() {
it("should return everything when no criteria given", () => Promise.all(connections.map(async connection => {
const postRepository = connection.getRepository(Post);
const promises: Promise<Post>[] = [];
for (let i = 0; i < 100; i++) {
const post = new Post();
post.id = i;
post.title = "post #" + i;
post.categoryName = "other";
promises.push(postRepository.persist(post));
}
const savedPosts = await Promise.all(promises);
savedPosts.length.should.be.equal(100); // check if they all are saved
// check find method
const loadedPosts = await postRepository.find({ alias: "post", orderBy: { "post.id": "ASC" }});
loadedPosts.should.be.instanceOf(Array);
loadedPosts.length.should.be.equal(100);
loadedPosts[0].id.should.be.equal(0);
loadedPosts[0].title.should.be.equal("post #0");
loadedPosts[99].id.should.be.equal(99);
loadedPosts[99].title.should.be.equal("post #99");
// check findAndCount method
let [loadedPosts2, count] = await postRepository.findAndCount({ alias: "post", orderBy: { "post.id": "ASC" }});
count.should.be.equal(100);
loadedPosts2.should.be.instanceOf(Array);
loadedPosts2.length.should.be.equal(100);
loadedPosts2[0].id.should.be.equal(0);
loadedPosts2[0].title.should.be.equal("post #0");
loadedPosts2[99].id.should.be.equal(99);
loadedPosts2[99].title.should.be.equal("post #99");
})));
it("should return posts that match given criteria", () => Promise.all(connections.map(async connection => {
const postRepository = connection.getRepository(Post);
const promises: Promise<Post>[] = [];
for (let i = 1; i <= 100; i++) {
const post = new Post();
post.id = i;
post.title = "post #" + i;
post.categoryName = i % 2 === 0 ? "even" : "odd";
promises.push(postRepository.persist(post));
}
const savedPosts = await Promise.all(promises);
savedPosts.length.should.be.equal(100); // check if they all are saved
// check find method
const loadedPosts = await postRepository.find({ categoryName: "odd" }, { alias: "post", orderBy: { "post.id": "ASC" }});
loadedPosts.should.be.instanceOf(Array);
loadedPosts.length.should.be.equal(50);
loadedPosts[0].id.should.be.equal(1);
loadedPosts[0].title.should.be.equal("post #1");
loadedPosts[49].id.should.be.equal(99);
loadedPosts[49].title.should.be.equal("post #99");
// check findAndCount method
let [loadedPosts2, count] = await postRepository.findAndCount({ categoryName: "odd" }, { alias: "post", orderBy: { "post.id": "ASC" }});
count.should.be.equal(50);
loadedPosts2.should.be.instanceOf(Array);
loadedPosts2.length.should.be.equal(50);
loadedPosts2[0].id.should.be.equal(1);
loadedPosts2[0].title.should.be.equal("post #1");
loadedPosts2[49].id.should.be.equal(99);
loadedPosts2[49].title.should.be.equal("post #99");
})));
it("should return posts that match given multiple criteria", () => Promise.all(connections.map(async connection => {
const postRepository = connection.getRepository(Post);
const promises: Promise<Post>[] = [];
for (let i = 1; i <= 100; i++) {
const post = new Post();
post.id = i;
post.title = "post #" + i;
post.categoryName = i % 2 === 0 ? "even" : "odd";
post.isNew = i > 90;
promises.push(postRepository.persist(post));
}
const savedPosts = await Promise.all(promises);
savedPosts.length.should.be.equal(100); // check if they all are saved
// check find method
const loadedPosts = await postRepository.find({ categoryName: "odd", isNew: true }, { alias: "post", orderBy: { "post.id": "ASC" }});
loadedPosts.should.be.instanceOf(Array);
loadedPosts.length.should.be.equal(5);
loadedPosts[0].id.should.be.equal(91);
loadedPosts[0].title.should.be.equal("post #91");
loadedPosts[4].id.should.be.equal(99);
loadedPosts[4].title.should.be.equal("post #99");
// check findAndCount method
let [loadedPosts2, count] = await postRepository.findAndCount({ categoryName: "odd", isNew: true }, { alias: "post", orderBy: { "post.id": "ASC" }});
count.should.be.equal(5);
loadedPosts2.should.be.instanceOf(Array);
loadedPosts2.length.should.be.equal(5);
loadedPosts2[0].id.should.be.equal(91);
loadedPosts2[0].title.should.be.equal("post #91");
loadedPosts2[4].id.should.be.equal(99);
loadedPosts2[4].title.should.be.equal("post #99");
})));
it("should return posts that match given find options", () => Promise.all(connections.map(async connection => {
const postRepository = connection.getRepository(Post);
const promises: Promise<Post>[] = [];
for (let i = 1; i <= 100; i++) {
const post = new Post();
post.id = i;
post.isNew = i > 90;
post.title = post.isNew ? "new post #" + i : "post #" + i;
post.categoryName = i % 2 === 0 ? "even" : "odd";
promises.push(postRepository.persist(post));
}
const savedPosts = await Promise.all(promises);
savedPosts.length.should.be.equal(100); // check if they all are saved
const findOptions: FindOptions = {
alias: "post",
where: "post.title LIKE :likeTitle AND post.categoryName = :categoryName",
parameters: {
likeTitle: "new post #%",
categoryName: "even"
},
orderBy: {
"post.id": "ASC"
}
};
// check find method
const loadedPosts = await postRepository.find(findOptions);
loadedPosts.should.be.instanceOf(Array);
loadedPosts.length.should.be.equal(5);
loadedPosts[0].id.should.be.equal(92);
loadedPosts[0].title.should.be.equal("new post #92");
loadedPosts[4].id.should.be.equal(100);
loadedPosts[4].title.should.be.equal("new post #100");
// check findAndCount method
let [loadedPosts2, count] = await postRepository.findAndCount(findOptions);
count.should.be.equal(5);
loadedPosts2.should.be.instanceOf(Array);
loadedPosts2.length.should.be.equal(5);
loadedPosts2[0].id.should.be.equal(92);
loadedPosts2[0].title.should.be.equal("new post #92");
loadedPosts2[4].id.should.be.equal(100);
loadedPosts2[4].title.should.be.equal("new post #100");
})));
it("should return posts that match both criteria and find options", () => Promise.all(connections.map(async connection => {
const postRepository = connection.getRepository(Post);
const promises: Promise<Post>[] = [];
for (let i = 1; i <= 100; i++) {
const post = new Post();
post.id = i;
post.isNew = i > 90;
post.title = post.isNew ? "new post #" + i : "post #" + i;
post.categoryName = i % 2 === 0 ? "even" : "odd";
promises.push(postRepository.persist(post));
}
const savedPosts = await Promise.all(promises);
savedPosts.length.should.be.equal(100); // check if they all are saved
const findOptions: FindOptions = {
alias: "post",
firstResult: 1,
maxResults: 2,
orderBy: {
"post.id": "ASC"
}
};
// check find method
const loadedPosts = await postRepository.find({ categoryName: "even", isNew: true }, findOptions);
loadedPosts.should.be.instanceOf(Array);
loadedPosts.length.should.be.equal(2);
loadedPosts[0].id.should.be.equal(94);
loadedPosts[0].title.should.be.equal("new post #94");
loadedPosts[1].id.should.be.equal(96);
loadedPosts[1].title.should.be.equal("new post #96");
// check findAndCount method
let [loadedPosts2, count] = await postRepository.findAndCount({ categoryName: "even", isNew: true }, findOptions);
count.should.be.equal(5);
loadedPosts2.should.be.instanceOf(Array);
loadedPosts2.length.should.be.equal(2);
loadedPosts2[0].id.should.be.equal(94);
loadedPosts2[0].title.should.be.equal("new post #94");
loadedPosts2[1].id.should.be.equal(96);
loadedPosts2[1].title.should.be.equal("new post #96");
})));
});
describe("findOne", function() {
it("should return first when no criteria given", () => Promise.all(connections.map(async connection => {
const userRepository = connection.getRepository<User>("User");
const promises: Promise<User>[] = [];
for (let i = 0; i < 100; i++) {
const user: User = {
id: i,
firstName: "name #" + i,
secondName: "Doe"
};
promises.push(userRepository.persist(user));
}
const savedUsers = await Promise.all(promises);
savedUsers.length.should.be.equal(100); // check if they all are saved
const loadedUser = (await userRepository.findOne({ alias: "user", orderBy: { "user.id": "ASC" }}))!;
loadedUser.id.should.be.equal(0);
loadedUser.firstName.should.be.equal("name #0");
loadedUser.secondName.should.be.equal("Doe");
})));
it("should return when criteria given", () => Promise.all(connections.map(async connection => {
const userRepository = connection.getRepository<User>("User");
const promises: Promise<User>[] = [];
for (let i = 0; i < 100; i++) {
const user: User = {
id: i,
firstName: "name #" + i,
secondName: "Doe"
};
promises.push(userRepository.persist(user));
}
const savedUsers = await Promise.all(promises);
savedUsers.length.should.be.equal(100); // check if they all are saved
const loadedUser = (await userRepository.findOne({ firstName: "name #1" }, { alias: "user", orderBy: { "user.id": "ASC" }}))!;
loadedUser.id.should.be.equal(1);
loadedUser.firstName.should.be.equal("name #1");
loadedUser.secondName.should.be.equal("Doe");
})));
it("should return when find options given", () => Promise.all(connections.map(async connection => {
const userRepository = connection.getRepository<User>("User");
const promises: Promise<User>[] = [];
for (let i = 0; i < 100; i++) {
const user: User = {
id: i,
firstName: "name #" + i,
secondName: "Doe"
};
promises.push(userRepository.persist(user));
}
const savedUsers = await Promise.all(promises);
savedUsers.length.should.be.equal(100); // check if they all are saved
const findOptions: FindOptions = {
alias: "user",
where: "user.firstName=:firstName AND user.secondName =:secondName",
parameters: {
firstName: "name #99",
secondName: "Doe"
}
};
const loadedUser = (await userRepository.findOne(findOptions, { alias: "user", orderBy: { "user.id": "ASC" }}))!;
loadedUser.id.should.be.equal(99);
loadedUser.firstName.should.be.equal("name #99");
loadedUser.secondName.should.be.equal("Doe");
})));
});
describe("findOneById", function() {
it("should return entity by a given id", () => Promise.all(connections.map(async connection => {
const userRepository = connection.getRepository<User>("User");
const promises: Promise<User>[] = [];
for (let i = 0; i < 100; i++) {
const user: User = {
id: i,
firstName: "name #" + i,
secondName: "Doe"
};
promises.push(userRepository.persist(user));
}
const savedUsers = await Promise.all(promises);
savedUsers.length.should.be.equal(100); // check if they all are saved
let loadedUser = (await userRepository.findOneById(0))!;
loadedUser.id.should.be.equal(0);
loadedUser.firstName.should.be.equal("name #0");
loadedUser.secondName.should.be.equal("Doe");
loadedUser = (await userRepository.findOneById(1))!;
loadedUser.id.should.be.equal(1);
loadedUser.firstName.should.be.equal("name #1");
loadedUser.secondName.should.be.equal("Doe");
loadedUser = (await userRepository.findOneById(99))!;
loadedUser.id.should.be.equal(99);
loadedUser.firstName.should.be.equal("name #99");
loadedUser.secondName.should.be.equal("Doe");
})));
it("should return entity by a given id and find options", () => Promise.all(connections.map(async connection => {
const userRepository = connection.getRepository<User>("User");
const promises: Promise<User>[] = [];
for (let i = 0; i < 100; i++) {
const user: User = {
id: i,
firstName: "name #" + i,
secondName: "Doe"
};
promises.push(userRepository.persist(user));
}
const findOptions1: FindOptions = {
alias: "user",
whereConditions: {
secondName: "Doe"
}
};
const findOptions2: FindOptions = {
alias: "user",
whereConditions: {
secondName: "Dorian"
}
};
const savedUsers = await Promise.all(promises);
savedUsers.length.should.be.equal(100); // check if they all are saved
let loadedUser = await userRepository.findOneById(0, findOptions1);
loadedUser!.id.should.be.equal(0);
loadedUser!.firstName.should.be.equal("name #0");
loadedUser!.secondName.should.be.equal("Doe");
loadedUser = await userRepository.findOneById(1, findOptions2);
expect(loadedUser).to.be.undefined;
})));
});
});

View File

@ -1,20 +0,0 @@
{
"name": "User",
"table": {
"name": "user"
},
"columns": {
"id": {
"type": "int",
"primary": true
},
"firstName": {
"type": "string",
"nullable": false
},
"secondName": {
"type": "string",
"nullable": false
}
}
}

View File

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

View File

@ -1,25 +0,0 @@
import {Category} from "./Category";
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 {ManyToMany} from "../../../../../src/decorator/relations/ManyToMany";
import {JoinTable} from "../../../../../src/decorator/relations/JoinTable";
@Entity()
export class Post {
@PrimaryGeneratedColumn()
id: number;
@Column()
title: string;
@OneToMany(type => Category, category => category.post)
categories: Category[]|null;
@ManyToMany(type => Category, category => category.manyPosts)
@JoinTable()
manyCategories: Category[];
}

View File

@ -1,305 +0,0 @@
import "reflect-metadata";
import {expect} from "chai";
import {Connection} from "../../../../src/connection/Connection";
import {Post} from "./entity/Post";
import {Category} from "./entity/Category";
import {createTestingConnections, reloadTestingDatabases, closeTestingConnections} from "../../../utils/test-utils";
describe("repository > set/add/remove relation methods", function() {
// -------------------------------------------------------------------------
// Configuration
// -------------------------------------------------------------------------
let connections: Connection[];
before(async () => connections = await createTestingConnections({
entities: [__dirname + "/entity/*{.js,.ts}"],
schemaCreate: true,
dropSchemaOnConnection: true
}));
beforeEach(() => reloadTestingDatabases(connections));
after(() => closeTestingConnections(connections));
// -------------------------------------------------------------------------
// Specifications
// -------------------------------------------------------------------------
it("add elements to many-to-many from owner side", () => Promise.all(connections.map(async connection => {
const postRepository = connection.getRepository(Post);
const categoryRepository = connection.getRepository(Category);
const postSpecificRepository = connection.getSpecificRepository(Post);
// save a new category
const newCategory1 = categoryRepository.create();
newCategory1.name = "Animals";
await categoryRepository.persist(newCategory1);
// save a new category
const newCategory2 = categoryRepository.create();
newCategory2.name = "Kids";
await categoryRepository.persist(newCategory2);
// save a new post
const newPost = postRepository.create();
newPost.title = "Super post";
await postRepository.persist(newPost);
// add categories to a post
await postSpecificRepository.addToRelation(post => post.manyCategories, newPost.id, [newCategory1.id, newCategory2.id]);
// load a post, want to have categories count
const loadedPost = await postRepository
.findOneById(1, { alias: "post", leftJoinAndSelect: { manyCategories: "post.manyCategories" } });
expect(loadedPost!).not.to.be.empty;
expect(loadedPost!.manyCategories).not.to.be.empty;
expect(loadedPost!.manyCategories![0]).not.to.be.empty;
expect(loadedPost!.manyCategories![1]).not.to.be.empty;
})));
it("add elements to many-to-many from inverse side", () => Promise.all(connections.map(async connection => {
const postRepository = connection.getRepository(Post);
const categoryRepository = connection.getRepository(Category);
const categorySpecificRepository = connection.getSpecificRepository(Category);
// save a new post
const newPost1 = postRepository.create();
newPost1.title = "post #1";
await postRepository.persist(newPost1);
// save a new post
const newPost2 = postRepository.create();
newPost2.title = "post #2";
await postRepository.persist(newPost2);
// save a new category
const newCategory = categoryRepository.create();
newCategory.name = "Kids";
await categoryRepository.persist(newCategory);
// add categories to a post
await categorySpecificRepository.addToRelation(category => category.manyPosts, newCategory.id, [newPost1.id, newPost2.id]);
// load a post, want to have categories count
const loadedCategory = await categoryRepository.findOneById(1, {
alias: "category",
leftJoinAndSelect: { manyPosts: "category.manyPosts" } }
);
expect(loadedCategory).not.to.be.empty;
expect(loadedCategory!.manyPosts).not.to.be.empty;
expect(loadedCategory!.manyPosts![0]).not.to.be.empty;
expect(loadedCategory!.manyPosts![1]).not.to.be.empty;
})));
it("remove elements to many-to-many from owner side", () => Promise.all(connections.map(async connection => {
const postRepository = connection.getRepository(Post);
const categoryRepository = connection.getRepository(Category);
const postSpecificRepository = connection.getSpecificRepository(Post);
// save a new category
const newCategory1 = categoryRepository.create();
newCategory1.name = "Animals";
await categoryRepository.persist(newCategory1);
// save a new category
const newCategory2 = categoryRepository.create();
newCategory2.name = "Kids";
await categoryRepository.persist(newCategory2);
// save a new category
const newCategory3 = categoryRepository.create();
newCategory3.name = "Adults";
await categoryRepository.persist(newCategory3);
// save a new post with categories
const newPost = postRepository.create();
newPost.title = "Super post";
newPost.manyCategories = [newCategory1, newCategory2, newCategory3];
await postRepository.persist(newPost);
// add categories to a post
await postSpecificRepository.removeFromRelation(post => post.manyCategories, newPost.id, [newCategory1.id, newCategory3.id]);
// load a post, want to have categories count
const loadedPost = await postRepository.findOneById(1, {
alias: "post",
leftJoinAndSelect: { manyCategories: "post.manyCategories" }
});
expect(loadedPost!).not.to.be.empty;
expect(loadedPost!.manyCategories).not.to.be.empty;
loadedPost!.manyCategories.length.should.be.equal(1);
loadedPost!.manyCategories![0].name.should.be.equal("Kids");
})));
it("remove elements to many-to-many from inverse side", () => Promise.all(connections.map(async connection => {
const postRepository = connection.getRepository(Post);
const categoryRepository = connection.getRepository(Category);
const categorySpecificRepository = connection.getSpecificRepository(Category);
// save a new category
const newPost1 = postRepository.create();
newPost1.title = "post #1";
await postRepository.persist(newPost1);
// save a new category
const newPost2 = postRepository.create();
newPost2.title = "post #2";
await postRepository.persist(newPost2);
// save a new category
const newPost3 = postRepository.create();
newPost3.title = "post #3";
await postRepository.persist(newPost3);
// save a new post with categories
const newCategory = categoryRepository.create();
newCategory.name = "SuperCategory";
newCategory.manyPosts = [newPost1, newPost2, newPost3];
await categoryRepository.persist(newCategory);
// add categories to a post
await categorySpecificRepository.removeFromRelation(post => post.manyPosts, newCategory.id, [newPost1.id, newPost3.id]);
// load a post, want to have categories count
const loadedCategory = await categoryRepository.findOneById(1, {
alias: "category",
leftJoinAndSelect: { manyPosts: "category.manyPosts" }
});
expect(loadedCategory!).not.to.be.empty;
expect(loadedCategory!.manyPosts).not.to.be.empty;
loadedCategory!.manyPosts.length.should.be.equal(1);
loadedCategory!.manyPosts[0].title.should.be.equal("post #2");
})));
it("set element to one-to-many relation", () => Promise.all(connections.map(async connection => {
const postRepository = connection.getRepository(Post);
const categoryRepository = connection.getRepository(Category);
const postSpecificRepository = connection.getSpecificRepository(Post);
const categorySpecificRepository = connection.getSpecificRepository(Category);
// save a new category
const newCategory1 = categoryRepository.create();
newCategory1.name = "Animals";
await categoryRepository.persist(newCategory1);
// save a new post
const newPost = postRepository.create();
newPost.title = "Super post";
await postRepository.persist(newPost);
// add categories to a post
await postSpecificRepository.setRelation(post => post.categories, newPost.id, newCategory1.id);
// load a post, want to have categories count
const loadedPost = await postRepository.findOneById(1, {
alias: "post",
leftJoinAndSelect: { categories: "post.categories" }
});
expect(loadedPost!).not.to.be.empty;
expect(loadedPost!.categories).not.to.be.empty;
expect(loadedPost!.categories![0]).not.to.be.empty;
})));
it("set element to many-to-one relation", () => Promise.all(connections.map(async connection => {
const postRepository = connection.getRepository(Post);
const categoryRepository = connection.getRepository(Category);
const categorySpecificRepository = connection.getSpecificRepository(Category);
// save a new category
const newPost = postRepository.create();
newPost.title = "post #1";
await postRepository.persist(newPost);
// save a new category
const newCategory = categoryRepository.create();
newCategory.name = "Kids";
await categoryRepository.persist(newCategory);
// add categories to a post
await categorySpecificRepository.setRelation(category => category.post, newCategory.id, newPost.id);
// load a post, want to have categories count
const loadedCategory = await categoryRepository.findOneById(1, {
alias: "category",
leftJoinAndSelect: { post: "category.post" }
});
expect(loadedCategory!).not.to.be.empty;
expect(loadedCategory!.post).not.to.be.empty;
})));
it("set element to NULL in one-to-many relation", () => Promise.all(connections.map(async connection => {
const postRepository = connection.getRepository(Post);
const categoryRepository = connection.getRepository(Category);
const postSpecificRepository = connection.getSpecificRepository(Post);
// save a new category
const newCategory1 = categoryRepository.create();
newCategory1.name = "Animals";
await categoryRepository.persist(newCategory1);
// save a new post
const newPost = postRepository.create();
newPost.title = "Super post";
newPost.categories = [newCategory1];
await postRepository.persist(newPost);
// add categories to a post
await postSpecificRepository.setRelation(post => post.categories, newPost.id, null);
// load a post, want to have categories count
const loadedPost = await postRepository.findOneById(1, {
alias: "post",
leftJoinAndSelect: { categories: "post.categories" }
});
expect(loadedPost!).not.to.be.empty;
expect(loadedPost!.categories).to.be.empty;
})));
it("set element to NULL in many-to-one relation", () => Promise.all(connections.map(async connection => {
const postRepository = connection.getRepository(Post);
const categoryRepository = connection.getRepository(Category);
const categorySpecificRepository = connection.getSpecificRepository(Category);
// save a new category
const newPost = postRepository.create();
newPost.title = "post #1";
await postRepository.persist(newPost);
// save a new category
const newCategory = categoryRepository.create();
newCategory.name = "Kids";
newCategory.post = newPost;
await categoryRepository.persist(newCategory);
// add categories to a post
await categorySpecificRepository.setRelation(category => category.post, newCategory.id, null);
// load a post, want to have categories count
const loadedCategory = await categoryRepository.findOneById(1, {
alias: "category",
leftJoinAndSelect: { post: "category.post" }
});
expect(loadedCategory).not.to.be.empty;
expect(loadedCategory!.post).to.be.empty;
})));
});