You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
178 lines
5.2 KiB
178 lines
5.2 KiB
import * as TypeORM from "typeorm"; |
|
import * as LRUCache from "lru-cache"; |
|
import * as DeepCopy from "deepcopy"; |
|
|
|
interface Paginater { |
|
pageCnt: number; |
|
perPage: number; |
|
currPage: number; |
|
} |
|
|
|
const caches: Map<string, LRUCache<number, Model>> = new Map(); |
|
|
|
function ensureCache(modelName) { |
|
if (!caches.has(modelName)) { |
|
caches.set(modelName, new LRUCache({ |
|
max: 500 |
|
})); |
|
} |
|
|
|
return caches.get(modelName); |
|
} |
|
|
|
function cacheGet(modelName, id) { |
|
return ensureCache(modelName).get(parseInt(id)); |
|
} |
|
|
|
function cacheSet(modelName, id, data) { |
|
if (data == null) { |
|
ensureCache(modelName).del(id); |
|
} else { |
|
ensureCache(modelName).set(parseInt(id), data); |
|
} |
|
} |
|
|
|
export default class Model extends TypeORM.BaseEntity { |
|
static cache = false; |
|
|
|
static async findById<T extends TypeORM.BaseEntity>(this: TypeORM.ObjectType<T>, id?: number): Promise<T | undefined> { |
|
const doQuery = async () => await (this as any).findOne(parseInt(id as any) || 0); |
|
|
|
if ((this as typeof Model).cache) { |
|
let result; |
|
if (result = cacheGet(this.name, id)) { |
|
return result.clone(); |
|
} |
|
|
|
result = await doQuery(); |
|
if (result) { |
|
cacheSet(this.name, id, result); |
|
} |
|
return result.clone(); |
|
} else { |
|
return await doQuery(); |
|
} |
|
} |
|
|
|
clone() { |
|
const object = {}; |
|
TypeORM.getConnection().getMetadata(this.constructor).ownColumns.map(column => column.propertyName).forEach(key => { |
|
object[key] = DeepCopy(this[key]); |
|
}); |
|
return (this.constructor as any).create(object); |
|
} |
|
|
|
async destroy() { |
|
const id = (this as any).id; |
|
await TypeORM.getManager().remove(this); |
|
if ((this.constructor as typeof Model).cache) { |
|
cacheSet(this.constructor.name, id, null); |
|
} |
|
} |
|
|
|
async save(): Promise<this> { |
|
await super.save(); |
|
if ((this.constructor as typeof Model).cache) { |
|
cacheSet(this.constructor.name, (this as any).id, this); |
|
} |
|
return this; |
|
} |
|
|
|
static async countQuery<T extends TypeORM.BaseEntity>(this: TypeORM.ObjectType<T>, query: TypeORM.SelectQueryBuilder<T> | string) { |
|
let parameters: any[] = null; |
|
if (typeof query !== 'string') { |
|
[query, parameters] = query.getQueryAndParameters(); |
|
} |
|
|
|
return parseInt(( |
|
await TypeORM.getManager().query(`SELECT COUNT(*) FROM (${query}) AS \`__tmp_table\``, parameters) |
|
)[0]['COUNT(*)']); |
|
} |
|
|
|
static async countForPagination(where) { |
|
const queryBuilder = where instanceof TypeORM.SelectQueryBuilder |
|
? where |
|
: this.createQueryBuilder().where(where); |
|
return await queryBuilder.getCount(); |
|
} |
|
|
|
static async queryAll(queryBuilder) { |
|
return await queryBuilder.getMany(); |
|
} |
|
|
|
static async queryPage(paginater: Paginater, where, order, largeData) { |
|
if (!paginater.pageCnt) return []; |
|
|
|
const queryBuilder = where instanceof TypeORM.SelectQueryBuilder |
|
? where |
|
: this.createQueryBuilder().where(where); |
|
|
|
if (order) queryBuilder.orderBy(order); |
|
|
|
queryBuilder.skip((paginater.currPage - 1) * paginater.perPage) |
|
.take(paginater.perPage); |
|
|
|
if (largeData) { |
|
const rawResult = await queryBuilder.select('id').getRawMany(); |
|
return await Promise.all(rawResult.map(async result => this.findById(result.id))); |
|
} |
|
|
|
return queryBuilder.getMany(); |
|
} |
|
|
|
static async queryPageWithLargeData(queryBuilder, { currPageTop, currPageBottom, perPage }, type) { |
|
const queryBuilderBak = queryBuilder.clone(); |
|
|
|
const result = { |
|
meta: { |
|
hasPrevPage: false, |
|
hasNextPage: false, |
|
top: 0, |
|
bottom: 0 |
|
}, |
|
data: [] |
|
}; |
|
|
|
queryBuilder.take(perPage); |
|
if (type === -1) { |
|
if (currPageTop != null) queryBuilder.andWhere('id > :currPageTop', { currPageTop }); |
|
} else if (type === 1) { |
|
if (currPageBottom != null) queryBuilder.andWhere('id < :currPageBottom', { currPageBottom }); |
|
} |
|
|
|
result.data = await queryBuilder.getMany(); |
|
|
|
if (result.data.length === 0) return result; |
|
|
|
const queryBuilderHasPrev = queryBuilderBak.clone(), |
|
queryBuilderHasNext = queryBuilderBak; |
|
|
|
result.meta.top = result.data[0].id; |
|
result.meta.bottom = result.data[result.data.length - 1].id; |
|
|
|
result.meta.hasPrevPage = !!(await queryBuilderHasPrev.andWhere('id > :id', { |
|
id: result.meta.top |
|
}).take(1).getOne()); |
|
result.meta.hasNextPage = !!(await queryBuilderHasNext.andWhere('id < :id', { |
|
id: result.meta.bottom |
|
}).take(1).getOne()); |
|
|
|
return result; |
|
} |
|
|
|
static async queryRange(range: any[], where, order) { |
|
range[0] = parseInt(range[0]); |
|
range[1] = parseInt(range[1]); |
|
|
|
const queryBuilder = where instanceof TypeORM.SelectQueryBuilder |
|
? where |
|
: this.createQueryBuilder().where(where); |
|
|
|
if (order) queryBuilder.orderBy(order); |
|
|
|
queryBuilder.skip(range[0] - 1) |
|
.take(range[1] - range[0] + 1); |
|
|
|
return queryBuilder.getMany(); |
|
} |
|
}
|
|
|