From 33c61c7a831c6792b2de7bc0445fcbf1f24b4cf8 Mon Sep 17 00:00:00 2001 From: Menci Date: Sun, 21 Apr 2019 10:50:41 +0800 Subject: [PATCH] Optimize submissions pagination query --- models/common.ts | 60 +++++++++++++++++++++++++++++++++++-------- modules/submission.js | 15 +++++++---- utility.js | 13 ++++++++++ views/submissions.ejs | 2 +- 4 files changed, 74 insertions(+), 16 deletions(-) diff --git a/models/common.ts b/models/common.ts index 889fb9d..a19c911 100644 --- a/models/common.ts +++ b/models/common.ts @@ -33,14 +33,14 @@ export default class Model extends TypeORM.BaseEntity { static async queryPage(paginater: Paginater, where, order, largeData) { if (!paginater.pageCnt) return []; - let queryBuilder = where instanceof TypeORM.SelectQueryBuilder - ? where - : this.createQueryBuilder().where(where); + const queryBuilder = where instanceof TypeORM.SelectQueryBuilder + ? where + : this.createQueryBuilder().where(where); if (order) queryBuilder.orderBy(order); - queryBuilder = queryBuilder.skip((paginater.currPage - 1) * paginater.perPage) - .take(paginater.perPage); + queryBuilder.skip((paginater.currPage - 1) * paginater.perPage) + .take(paginater.perPage); if (largeData) { const rawResult = await queryBuilder.select('id').getRawMany(); @@ -50,18 +50,58 @@ export default class Model extends TypeORM.BaseEntity { 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]); - let queryBuilder = where instanceof TypeORM.SelectQueryBuilder - ? where - : this.createQueryBuilder().where(where); + const queryBuilder = where instanceof TypeORM.SelectQueryBuilder + ? where + : this.createQueryBuilder().where(where); if (order) queryBuilder.orderBy(order); - queryBuilder = queryBuilder.skip(range[0] - 1) - .take(range[1] - range[0] + 1); + queryBuilder.skip(range[0] - 1) + .take(range[1] - range[0] + 1); return queryBuilder.getMany(); } diff --git a/modules/submission.js b/modules/submission.js index b4b923e..b4e1263 100644 --- a/modules/submission.js +++ b/modules/submission.js @@ -103,16 +103,21 @@ app.get('/submissions', async (req, res) => { isFiltered = true; } - let paginate = syzoj.utils.paginate(await JudgeState.countQuery(query), req.query.page, syzoj.config.page.judge_state); - let judge_state = await JudgeState.queryPage(paginate, query, { id: "DESC" }, true); + query.orderBy({ + id: "DESC" + }); + + const queryResult = await JudgeState.queryPageWithLargeData(query, syzoj.utils.paginateLargeData( + req.query.currPageTop, req.query.currPageBottom, syzoj.config.page.judge_state + ), parseInt(req.query.page)); + const judge_state = queryResult.data; await judge_state.forEachAsync(async obj => { await obj.loadRelationships(); if (obj.problem.type !== 'submit-answer') obj.code_length = obj.code.length; - }) + }); res.render('submissions', { - // judge_state: judge_state, items: judge_state.map(x => ({ info: getSubmissionInfo(x, displayConfig), token: (x.pending && x.task_id != null) ? jwt.sign({ @@ -123,7 +128,7 @@ app.get('/submissions', async (req, res) => { result: getRoughResult(x, displayConfig, true), running: false, })), - paginate: paginate, + paginate: queryResult.meta, pushType: 'rough', form: req.query, displayConfig: displayConfig, diff --git a/utility.js b/utility.js index d300cb9..68b04ad 100644 --- a/utility.js +++ b/utility.js @@ -245,6 +245,19 @@ module.exports = { } }; }, + paginateLargeData(currPageTop, currPageBottom, perPage) { + function parseIntOrNull(x) { + if (typeof x === 'string') x = parseInt(x); + if (typeof x !== 'number' || isNaN(x)) return null; + return x; + } + + return { + currPageTop: parseIntOrNull(currPageTop), + currPageBottom: parseIntOrNull(currPageBottom), + perPage + }; + }, removeTitleTag(s) { return s.replace(/「[\S\s]+?」/, ''); }, diff --git a/views/submissions.ejs b/views/submissions.ejs index cc16b77..44ffa24 100644 --- a/views/submissions.ejs +++ b/views/submissions.ejs @@ -116,7 +116,7 @@ <% } else { %>
- <% include page %> + <% include page_largedata %> <% } %>