From fa36b63fbe339fa05f23d80d88635c3750aa8780 Mon Sep 17 00:00:00 2001 From: "Chen, Tai-Lin" Date: Thu, 21 Dec 2023 17:28:48 +0800 Subject: [PATCH] feat: add order to pagination --- .../analytics/src/controllers/commands.js | 9 ++- .../analytics/src/controllers/sessions.js | 8 ++- packages/analytics/src/controllers/users.js | 6 +- packages/analytics/src/models/commands.js | 4 +- packages/analytics/src/models/pagination.js | 68 ++++++++++++------- packages/analytics/src/models/sessions.js | 4 +- packages/analytics/src/models/users.js | 4 +- packages/analytics/tests/pagination.test.js | 32 ++++++--- 8 files changed, 85 insertions(+), 50 deletions(-) diff --git a/packages/analytics/src/controllers/commands.js b/packages/analytics/src/controllers/commands.js index 3a92b0a..36d2f21 100644 --- a/packages/analytics/src/controllers/commands.js +++ b/packages/analytics/src/controllers/commands.js @@ -1,18 +1,21 @@ /* eslint-disable object-shorthand */ -import { Desc } from '../models/pagination.js' +import Pagination from '../models/pagination.js' import { errorCommands } from '../models/commands.js' // eslint-disable-next-line import/prefer-default-export export async function commandListController(req, res) { - const { nextKey } = req.query + const { nextKey, order } = req.query const itemsPerPage = 100 try { - const payload = await errorCommands(new Desc(itemsPerPage, nextKey)) + const payload = await errorCommands( + new Pagination(order, itemsPerPage, nextKey) + ) const newNextKey = payload[payload.length - 1]?._id res.render('commands', { errorCommands: payload, nextKey: newNextKey }) } catch (error) { + console.error(error) res.status(500).send('Error fetching data') } } diff --git a/packages/analytics/src/controllers/sessions.js b/packages/analytics/src/controllers/sessions.js index 435dc74..765f1f7 100644 --- a/packages/analytics/src/controllers/sessions.js +++ b/packages/analytics/src/controllers/sessions.js @@ -1,15 +1,17 @@ -import { Desc } from '../models/pagination.js' +import Pagination from '../models/pagination.js' import { sessionList, sessionDetail } from '../models/sessions.js' export async function sessionListController(req, res) { - const { nextKey } = req.query + const { nextKey, order } = req.query const itemsPerPage = 50 try { // const totalSessions = await sessionCount() // const maxPages = Math.ceil(totalSessions / itemsPerPage) - const sessions = await sessionList(new Desc(itemsPerPage, nextKey)) + const sessions = await sessionList( + new Pagination(order, itemsPerPage, nextKey) + ) const newNextKey = sessions[sessions.length - 1]?._id res.render('sessions', { sessions, nextKey: newNextKey }) diff --git a/packages/analytics/src/controllers/users.js b/packages/analytics/src/controllers/users.js index 69ebf91..96d198c 100644 --- a/packages/analytics/src/controllers/users.js +++ b/packages/analytics/src/controllers/users.js @@ -1,12 +1,12 @@ -import { Desc } from '../models/pagination.js' +import Pagination from '../models/pagination.js' import { userList } from '../models/users.js' export async function userListController(req, res) { - const { nextKey } = req.query + const { nextKey, order } = req.query const itemsPerPage = 50 try { - const users = await userList(new Desc(itemsPerPage, nextKey)) + const users = await userList(new Pagination(order, itemsPerPage, nextKey)) const newNextKey = users[users.length - 1]?._id res.render('users', { users, nextKey: newNextKey }) diff --git a/packages/analytics/src/models/commands.js b/packages/analytics/src/models/commands.js index 32bc9ae..e771c4f 100644 --- a/packages/analytics/src/models/commands.js +++ b/packages/analytics/src/models/commands.js @@ -3,7 +3,7 @@ import { Command } from '@linux-odyssey/models' // eslint-disable-next-line import/prefer-default-export export async function errorCommands(pagination) { const commands = await Command.aggregate([ - pagination.matchStage('_id'), + pagination.match('_id'), { $match: { $and: [ @@ -31,7 +31,7 @@ export async function errorCommands(pagination) { }, }, { $unwind: '$session.user' }, - pagination.order('_id'), + pagination.sort('_id'), pagination.limit(), ]) diff --git a/packages/analytics/src/models/pagination.js b/packages/analytics/src/models/pagination.js index 4509597..d253bec 100644 --- a/packages/analytics/src/models/pagination.js +++ b/packages/analytics/src/models/pagination.js @@ -3,40 +3,60 @@ // eslint-disable-next-line max-classes-per-file import mongoose from 'mongoose' -class Pagination { +export const DESC = 'desc' +export const ASC = 'asc' + +class Asc { + getOperator() { + return '$gt' + } + + getOrder() { + return 1 + } +} + +class Desc { + getOperator() { + return '$lt' + } + + getOrder() { + return -1 + } +} + +export default class Pagination { nextKey = null itemsPerPage = 50 + order = null - constructor(itemsPerPage = 50, nextKey = null) { + constructor(order, itemsPerPage = 50, nextKey = null) { this.itemsPerPage = itemsPerPage + if (!order || order === DESC) { + this.order = new Desc() + } else if (order === ASC) { + this.order = new Asc() + } else { + throw new Error('order must be asc or desc') + } if (nextKey) { this.nextKey = new mongoose.Types.ObjectId(nextKey) } } + match(field) { + const stage = this.nextKey + ? { [field]: { [this.order.getOperator()]: this.nextKey } } + : {} + return { $match: stage } + } + + sort(field) { + return { $sort: { [field]: this.order.getOrder() } } + } + limit() { return { $limit: this.itemsPerPage } } } - -export class Asc extends Pagination { - matchStage(field) { - const stage = this.nextKey ? { [field]: { $gt: this.nextKey } } : {} - return { $match: stage } - } - - order(field) { - return { $sort: { [field]: 1 } } - } -} - -export class Desc extends Pagination { - matchStage(field) { - const stage = this.nextKey ? { [field]: { $lt: this.nextKey } } : {} - return { $match: stage } - } - - order(field) { - return { $sort: { [field]: -1 } } - } -} diff --git a/packages/analytics/src/models/sessions.js b/packages/analytics/src/models/sessions.js index 09c7357..7bc7022 100644 --- a/packages/analytics/src/models/sessions.js +++ b/packages/analytics/src/models/sessions.js @@ -32,7 +32,7 @@ function formatTime(time) { export async function sessionList(pagination) { const sessions = await Session.aggregate([ - pagination.matchStage('_id'), + pagination.match('_id'), { $lookup: { from: 'users', @@ -50,7 +50,7 @@ export async function sessionList(pagination) { as: 'commands', }, }, - pagination.order('_id'), + pagination.sort('_id'), pagination.limit(), ]) diff --git a/packages/analytics/src/models/users.js b/packages/analytics/src/models/users.js index 97ac208..2452123 100644 --- a/packages/analytics/src/models/users.js +++ b/packages/analytics/src/models/users.js @@ -14,7 +14,7 @@ export function userCount() { export async function userList(pagination) { const users = await Session.aggregate([ - pagination.matchStage('user'), + pagination.match('user'), { $group: { _id: '$user', @@ -31,7 +31,7 @@ export async function userList(pagination) { }, }, { $unwind: '$userData' }, - pagination.order('userData._id'), + pagination.sort('userData._id'), pagination.limit(), ]) diff --git a/packages/analytics/tests/pagination.test.js b/packages/analytics/tests/pagination.test.js index c43dccd..9c1db98 100644 --- a/packages/analytics/tests/pagination.test.js +++ b/packages/analytics/tests/pagination.test.js @@ -1,32 +1,42 @@ import mongoose from 'mongoose' -import { Desc } from '../src/models/pagination.js' +import Pagination, { ASC, DESC } from '../src/models/pagination.js' describe('Pagination', () => { - it('should return correct limit', async () => { - const pagination = new Desc(10) + it('should failed when not provide order', () => { + expect(() => new Pagination()).toThrow('order must be asc or desc') + }) + it('should return correct limit', () => { + const pagination = new Pagination(DESC, 10) expect(pagination.limit()).toMatchObject({ $limit: 10 }) }) - it('should return correct order', async () => { - const pagination = new Desc(10) + it('desc should return correct order', async () => { + const pagination = new Pagination(DESC, 10) - expect(pagination.order('_id')).toMatchObject({ $sort: { _id: -1 } }) - expect(pagination.order('time')).toMatchObject({ $sort: { time: -1 } }) + expect(pagination.sort('_id')).toMatchObject({ $sort: { _id: -1 } }) + expect(pagination.sort('time')).toMatchObject({ $sort: { time: -1 } }) + }) + + it('asc should return correct order', async () => { + const pagination = new Pagination(ASC, 10) + + expect(pagination.sort('_id')).toMatchObject({ $sort: { _id: 1 } }) + expect(pagination.sort('time')).toMatchObject({ $sort: { time: 1 } }) }) it('should return correct match stage for empty nextKey', async () => { - const pagination = new Desc(10) + const pagination = new Pagination(DESC, 10) - expect(pagination.matchStage('_id')).toMatchObject({ $match: {} }) + expect(pagination.match('_id')).toMatchObject({ $match: {} }) }) it('should return correct match stage for empty nextKey', async () => { const nextKeyStr = '656c7fea28a4c087bfcefe12' - const pagination = new Desc(10, nextKeyStr) + const pagination = new Pagination(DESC, 10, nextKeyStr) const nextKey = new mongoose.Types.ObjectId(nextKeyStr) - expect(pagination.matchStage('_id')).toMatchObject({ + expect(pagination.match('_id')).toMatchObject({ $match: { _id: { $lt: nextKey,