added pagination support

This commit is contained in:
Umed Khudoiberdiev 2016-03-21 23:29:28 +05:00
parent 9ebb128979
commit 3951852bbf
6 changed files with 290 additions and 11 deletions

View File

@ -0,0 +1,71 @@
import {createMysqlConnection} from "../../src/typeorm";
import {Post} from "./entity/Post";
import {PostCategory} from "./entity/PostCategory";
import {PostAuthor} from "./entity/PostAuthor";
// first create a connection
let options = {
host: "192.168.99.100",
port: 3306,
username: "root",
password: "admin",
database: "test",
autoSchemaCreate: true
};
createMysqlConnection(options, [__dirname + "/entity"]).then(connection => {
let postRepository = connection.getRepository(Post);
const posts: Post[] = [];
let author = new PostAuthor();
author.name = "Umed";
for (let i = 0; i < 100; i++) {
let category1 = new PostCategory();
category1.name = "post category #1";
let category2 = new PostCategory();
category2.name = "post category #2";
let post = new Post();
post.text = "Hello how are you?";
post.title = "hello";
post.categories.push(category1, category2);
post.author = author;
posts.push(post);
}
const qb = postRepository
.createQueryBuilder("p")
.leftJoinAndSelect("p.author", "author")
.leftJoinAndSelect("p.categories", "categories")
.setFirstResult(5)
.setMaxResults(10);
Promise.all(posts.map(post => postRepository.persist(post)))
.then(savedPosts => {
console.log("Posts has been saved. Lets try to load some posts");
return qb.getResults();
})
.then(loadedPost => {
console.log("post loaded: ", loadedPost);
console.log("now lets get total post count: ");
return qb.getCount();
})
.then(totalCount => {
console.log("total post count: ", totalCount);
console.log("now lets try to load it with same repository method:");
return postRepository.findAndCount();
})
.then(entitiesWithCount => {
console.log("items: ", entitiesWithCount.items);
console.log("count: ", entitiesWithCount.count);
})
.catch(error => console.log("Cannot save. Error: ", error.stack ? error.stack : error));
}, error => console.log("Cannot connect: ", error.stack ? error.stack : error));

View File

@ -0,0 +1,34 @@
import {PrimaryColumn, Column} from "../../../src/decorator/Columns";
import {Table} from "../../../src/decorator/Tables";
import {ManyToMany} from "../../../src/decorator/Relations";
import {PostCategory} from "./PostCategory";
import {PostAuthor} from "./PostAuthor";
import {ManyToOne} from "../../../src/decorator/relations/ManyToOne";
@Table("sample7_post")
export class Post {
@PrimaryColumn("int", { autoIncrement: true })
id: number;
@Column()
title: string;
@Column()
text: string;
@ManyToOne(type => PostAuthor, post => post.posts, {
cascadeInsert: true,
cascadeUpdate: true,
cascadeRemove: true
})
author: PostAuthor;
@ManyToMany(type => PostCategory, category => category.posts, {
cascadeInsert: true,
cascadeUpdate: true,
cascadeRemove: true
})
categories: PostCategory[] = [];
}

View File

@ -0,0 +1,18 @@
import {PrimaryColumn, Column} from "../../../src/decorator/Columns";
import {Table} from "../../../src/decorator/Tables";
import {Post} from "./Post";
import {OneToMany} from "../../../src/decorator/relations/OneToMany";
@Table("sample7_post_author")
export class PostAuthor {
@PrimaryColumn("int", { autoIncrement: true })
id: number;
@Column()
name: string;
@OneToMany(type => Post, post => post.author)
posts: Post[];
}

View File

@ -0,0 +1,22 @@
import {PrimaryColumn, Column} from "../../../src/decorator/Columns";
import {Table} from "../../../src/decorator/Tables";
import {Post} from "./Post";
import {ManyToManyInverse} from "../../../src/decorator/relations/ManyToManyInverse";
@Table("sample7_post_category")
export class PostCategory {
@PrimaryColumn("int", { autoIncrement: true })
id: number;
@Column()
name: string;
@ManyToManyInverse(type => Post, post => post.categories, {
cascadeInsert: true,
cascadeUpdate: true,
cascadeRemove: true
})
posts: Post[] = [];
}

View File

@ -17,11 +17,13 @@ export class QueryBuilder<Entity> {
// Private properties
// -------------------------------------------------------------------------
private broadcaster: OrmBroadcaster;
private _aliasMap: AliasMap;
private type: "select"|"update"|"delete";
private selects: string[] = [];
private fromEntity: { alias: Alias };
private fromTableName: string;
private fromTableAlias: string;
private updateQuerySet: Object;
private joins: Join[] = [];
private groupBys: string[] = [];
@ -31,13 +33,16 @@ export class QueryBuilder<Entity> {
private parameters: { [key: string]: any } = {};
private limit: number;
private offset: number;
private firstResult: number;
private maxResults: number;
// -------------------------------------------------------------------------
// Constructor
// -------------------------------------------------------------------------
constructor(private connection: Connection) {
this._aliasMap = new AliasMap(connection.metadatas);
this.broadcaster = new OrmBroadcaster(connection);
}
// -------------------------------------------------------------------------
@ -129,6 +134,7 @@ export class QueryBuilder<Entity> {
this.fromEntity = { alias: aliasObj };
} else {
this.fromTableName = <string> entityOrTableName;
this.fromTableAlias = alias;
}
return this;
}
@ -242,6 +248,16 @@ export class QueryBuilder<Entity> {
return this;
}
setFirstResult(firstResult: number): this {
this.firstResult = firstResult;
return this;
}
setMaxResults(maxResults: number): this {
this.maxResults = maxResults;
return this;
}
setParameter(key: string, value: any): this {
this.parameters[key] = value;
return this;
@ -283,20 +299,123 @@ export class QueryBuilder<Entity> {
}
getResults(): Promise<Entity[]> {
const broadcaster = new OrmBroadcaster(this.connection);
return this.connection.driver
.query<any[]>(this.getSql())
.then(results => this.rawResultsToEntities(results))
.then(results => {
broadcaster.broadcastLoadEventsForAll(results);
return results;
});
const mainAlias = this.aliasMap.mainAlias.name;
if (this.firstResult || this.maxResults) {
const metadata = this.connection.getMetadata(this.fromEntity.alias.target);
const idsQuery = this.clone()
.select(`DISTINCT(${mainAlias}.${metadata.primaryColumn.name}) as ids`)
.setOffset(this.firstResult)
.setLimit(this.maxResults)
.getSql();
return this.connection.driver
.query<any[]>(idsQuery)
.then((results: any[]) => {
const ids = results.map(result => result["ids"]).join(", ");
const queryWithIds = this.clone()
.andWhere(mainAlias + "." + metadata.primaryColumn.name + " IN (" + ids + ")")
.getSql();
return this.connection.driver.query<any[]>(queryWithIds);
})
.then(results => this.rawResultsToEntities(results))
.then(results => {
this.broadcaster.broadcastLoadEventsForAll(results);
return results;
});
} else {
return this.connection.driver
.query<any[]>(this.getSql())
.then(results => this.rawResultsToEntities(results))
.then(results => {
this.broadcaster.broadcastLoadEventsForAll(results);
return results;
});
}
}
getSingleResult(): Promise<Entity> {
return this.getResults().then(entities => entities[0]);
}
getCount(): Promise<number> {
const mainAlias = this.aliasMap.mainAlias.name;
const metadata = this.connection.getMetadata(this.fromEntity.alias.target);
const countQuery = this.clone()
.select(`COUNT(DISTINCT(${mainAlias}.${metadata.primaryColumn.name})) as cnt`)
.getSql();
return this.connection.driver
.query<any[]>(countQuery)
.then(results => parseInt(results[0]["cnt"]));
}
clone() {
const qb = new QueryBuilder(this.connection);
switch (this.type) {
case "select":
qb.select(this.selects);
break;
case "update":
qb.update(this.updateQuerySet);
break;
case "delete":
qb.delete();
break;
}
if (this.fromEntity && this.fromEntity.alias && this.fromEntity.alias.target) {
qb.from(this.fromEntity.alias.target, this.fromEntity.alias.name);
} else if (this.fromTableName) {
qb.from(this.fromTableName, this.fromTableAlias);
}
this.joins.forEach(join => {
const property = join.alias.target || (join.alias.parentAliasName + "." + join.alias.parentPropertyName);
qb.join(join.type, property, join.alias.name, join.conditionType, join.condition);
});
this.groupBys.forEach(groupBy => qb.addGroupBy(groupBy));
this.wheres.forEach(where => {
switch (where.type) {
case "simple":
qb.where(where.condition);
break;
case "and":
qb.andWhere(where.condition);
break;
case "or":
qb.orWhere(where.condition);
break;
}
});
this.havings.forEach(having => {
switch (having.type) {
case "simple":
qb.where(having.condition);
break;
case "and":
qb.andWhere(having.condition);
break;
case "or":
qb.orWhere(having.condition);
break;
}
});
this.orderBys.forEach(orderBy => qb.addOrderBy(orderBy.sort, orderBy.order));
Object.keys(this.parameters).forEach(key => qb.setParameter(key, this.parameters[key]))
qb.setLimit(this.limit)
.setOffset(this.offset)
.setFirstResult(this.firstResult)
.setMaxResults(this.maxResults);
return qb;
}
// -------------------------------------------------------------------------
// Protected Methods
// -------------------------------------------------------------------------
@ -365,7 +484,8 @@ export class QueryBuilder<Entity> {
this.addParameters(params);
return "UPDATE " + tableName + " " + (alias ? alias : "") + " SET " + updateSet;
}
return "";
throw new Error("No query builder type is specified.");
}
protected createWhereExpression() {

View File

@ -7,7 +7,7 @@ import {EntityPersistOperationBuilder} from "../persistment/EntityPersistOperati
import {PersistOperationExecutor} from "../persistment/PersistOperationExecutor";
import {EntityWithId} from "../persistment/operation/PersistOperation";
// todo: think how we can implement queryCount, queryManyAndCount
// todo: make extended functionality for findOne, find and findAndCount queries, so no need for user to use query builder
/**
* Repository is supposed to work with your entity objects. Find entities, insert, update, delete, etc.
@ -125,6 +125,20 @@ export class Repository<Entity> {
return builder.setParameters(conditions).getResults();
}
/**
* Finds entities that match given conditions.
*/
findAndCount(conditions?: Object): Promise<{ items: Entity[], count: number }> {
const alias = this.metadata.table.name;
const builder = this.createQueryBuilder(alias);
if (conditions) {
Object.keys(conditions).forEach(key => builder.where(alias + "." + key + "=:" + key));
builder.setParameters(conditions);
}
return Promise.all<any>([ builder.getResults(), builder.getCount() ])
.then(([entities, count]: [Entity[], number]) => ({ items: entities, count: count }));
}
/**
* Finds first entity that matches given conditions.
*/