diff --git a/packages/analytics/src/constants.js b/packages/analytics/src/constants.js index 15f98c8..e69de29 100644 --- a/packages/analytics/src/constants.js +++ b/packages/analytics/src/constants.js @@ -1,23 +0,0 @@ -/* eslint-disable class-methods-use-this */ -// eslint-disable-next-line max-classes-per-file -class Order {} - -export class Asc extends Order { - matchStage(nextKey) { - return { $gt: nextKey } - } - - order() { - return 1 - } -} - -export class Desc extends Order { - matchStage(nextKey) { - return { $lt: nextKey } - } - - order() { - return -1 - } -} diff --git a/packages/analytics/src/controllers/commands.js b/packages/analytics/src/controllers/commands.js index 4ba78a2..3a92b0a 100644 --- a/packages/analytics/src/controllers/commands.js +++ b/packages/analytics/src/controllers/commands.js @@ -1,6 +1,5 @@ /* eslint-disable object-shorthand */ -import { Desc } from '../constants.js' -import { Command } from '@linux-odyssey/models' +import { Desc } from '../models/pagination.js' import { errorCommands } from '../models/commands.js' // eslint-disable-next-line import/prefer-default-export @@ -9,23 +8,7 @@ export async function commandListController(req, res) { const itemsPerPage = 100 try { - // const totalCommands = await Command.countDocuments({ - // $and: [ - // { error: { $exists: true } }, - // { error: { $ne: null } }, - // { error: { $ne: '' } }, - // ], - // }) - - // const maxPages = Math.ceil(totalCommands / itemsPerPage) - - // const pageNumber = req.query - - const payload = await errorCommands({ - nextKey, - itemsPerPage, - order: new Desc(), - }) + const payload = await errorCommands(new Desc(itemsPerPage, nextKey)) const newNextKey = payload[payload.length - 1]?._id res.render('commands', { errorCommands: payload, nextKey: newNextKey }) diff --git a/packages/analytics/src/controllers/sessions.js b/packages/analytics/src/controllers/sessions.js index 8cbe537..435dc74 100644 --- a/packages/analytics/src/controllers/sessions.js +++ b/packages/analytics/src/controllers/sessions.js @@ -1,5 +1,5 @@ -import { Desc } from '../constants.js' -import { sessionCount, sessionList, sessionDetail } from '../models/sessions.js' +import { Desc } from '../models/pagination.js' +import { sessionList, sessionDetail } from '../models/sessions.js' export async function sessionListController(req, res) { const { nextKey } = req.query @@ -9,11 +9,7 @@ export async function sessionListController(req, res) { // const totalSessions = await sessionCount() // const maxPages = Math.ceil(totalSessions / itemsPerPage) - const sessions = await sessionList({ - nextKey, - itemsPerPage, - order: new Desc(), - }) + const sessions = await sessionList(new Desc(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 0e81a83..69ebf91 100644 --- a/packages/analytics/src/controllers/users.js +++ b/packages/analytics/src/controllers/users.js @@ -1,16 +1,12 @@ -import { Desc } from '../constants.js' -import { userList, userCount } from '../models/users.js' +import { Desc } from '../models/pagination.js' +import { userList } from '../models/users.js' export async function userListController(req, res) { const { nextKey } = req.query const itemsPerPage = 50 try { - const users = await userList({ - nextKey, - itemsPerPage, - order: new Desc(), - }) + const users = await userList(new Desc(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 89d1b2d..32bc9ae 100644 --- a/packages/analytics/src/models/commands.js +++ b/packages/analytics/src/models/commands.js @@ -1,16 +1,9 @@ -import mongoose from 'mongoose' import { Command } from '@linux-odyssey/models' // eslint-disable-next-line import/prefer-default-export -export async function errorCommands({ nextKey, itemsPerPage, order }) { - let matchStage = {} - if (nextKey) { - const key = new mongoose.Types.ObjectId(nextKey) - matchStage = { _id: order.matchStage(key) } - } - console.log(matchStage, itemsPerPage, nextKey, order) +export async function errorCommands(pagination) { const commands = await Command.aggregate([ - { $match: matchStage }, + pagination.matchStage('_id'), { $match: { $and: [ @@ -38,8 +31,8 @@ export async function errorCommands({ nextKey, itemsPerPage, order }) { }, }, { $unwind: '$session.user' }, - { $sort: { _id: order.order() } }, - { $limit: itemsPerPage }, + pagination.order('_id'), + pagination.limit(), ]) return commands.map(({ _id, command, error, createdAt, session }) => { diff --git a/packages/analytics/src/models/pagination.js b/packages/analytics/src/models/pagination.js new file mode 100644 index 0000000..4509597 --- /dev/null +++ b/packages/analytics/src/models/pagination.js @@ -0,0 +1,42 @@ +/* eslint-disable lines-between-class-members */ +/* eslint-disable class-methods-use-this */ +// eslint-disable-next-line max-classes-per-file +import mongoose from 'mongoose' + +class Pagination { + nextKey = null + itemsPerPage = 50 + + constructor(itemsPerPage = 50, nextKey = null) { + this.itemsPerPage = itemsPerPage + if (nextKey) { + this.nextKey = new mongoose.Types.ObjectId(nextKey) + } + } + + 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 d2a47c6..09c7357 100644 --- a/packages/analytics/src/models/sessions.js +++ b/packages/analytics/src/models/sessions.js @@ -1,4 +1,3 @@ -import mongoose from 'mongoose' import { Command, Session } from '@linux-odyssey/models' export function sessionCount() { @@ -31,16 +30,9 @@ function formatTime(time) { return `${hours}h ${minutes % 60}m ${seconds % 60}s` } -export async function sessionList({ nextKey, itemsPerPage, order }) { - let matchStage = {} - if (nextKey) { - const key = new mongoose.Types.ObjectId(nextKey) - matchStage = { _id: order.matchStage(key) } - } +export async function sessionList(pagination) { const sessions = await Session.aggregate([ - { - $match: matchStage, - }, + pagination.matchStage('_id'), { $lookup: { from: 'users', @@ -58,8 +50,8 @@ export async function sessionList({ nextKey, itemsPerPage, order }) { as: 'commands', }, }, - { $sort: { _id: order.order() } }, - { $limit: itemsPerPage }, + pagination.order('_id'), + pagination.limit(), ]) return sessions.map( diff --git a/packages/analytics/src/models/users.js b/packages/analytics/src/models/users.js index 34075b3..97ac208 100644 --- a/packages/analytics/src/models/users.js +++ b/packages/analytics/src/models/users.js @@ -12,15 +12,9 @@ export function userCount() { return User.find().count() } -export async function userList({ nextKey, itemsPerPage, order }) { - let matchStage = {} - if (nextKey) { - const key = new mongoose.Types.ObjectId(nextKey) - matchStage = { user: order.matchStage(key) } - } - console.log(matchStage) +export async function userList(pagination) { const users = await Session.aggregate([ - { $match: matchStage }, + pagination.matchStage('user'), { $group: { _id: '$user', @@ -37,8 +31,8 @@ export async function userList({ nextKey, itemsPerPage, order }) { }, }, { $unwind: '$userData' }, - { $sort: { 'userData._id': order.order() } }, - { $limit: itemsPerPage }, + pagination.order('userData._id'), + pagination.limit(), ]) return users.map(({ userData, count, lastActivityAt }) => { diff --git a/packages/analytics/tests/pagination.test.js b/packages/analytics/tests/pagination.test.js new file mode 100644 index 0000000..c43dccd --- /dev/null +++ b/packages/analytics/tests/pagination.test.js @@ -0,0 +1,37 @@ +import mongoose from 'mongoose' +import { Desc } from '../src/models/pagination.js' + +describe('Pagination', () => { + it('should return correct limit', async () => { + const pagination = new Desc(10) + + expect(pagination.limit()).toMatchObject({ $limit: 10 }) + }) + + it('should return correct order', async () => { + const pagination = new Desc(10) + + expect(pagination.order('_id')).toMatchObject({ $sort: { _id: -1 } }) + expect(pagination.order('time')).toMatchObject({ $sort: { time: -1 } }) + }) + + it('should return correct match stage for empty nextKey', async () => { + const pagination = new Desc(10) + + expect(pagination.matchStage('_id')).toMatchObject({ $match: {} }) + }) + + it('should return correct match stage for empty nextKey', async () => { + const nextKeyStr = '656c7fea28a4c087bfcefe12' + const pagination = new Desc(10, nextKeyStr) + const nextKey = new mongoose.Types.ObjectId(nextKeyStr) + + expect(pagination.matchStage('_id')).toMatchObject({ + $match: { + _id: { + $lt: nextKey, + }, + }, + }) + }) +})