handle personal leaderboards (pivotId)
All checks were successful
Build / build (18) (push) Successful in 45s
Build / build (20) (push) Successful in 1m13s
Build / build (22) (push) Successful in 44s
Build / build (22) (pull_request) Successful in 43s
Build / build (20) (pull_request) Successful in 1m15s
Build / build (18) (pull_request) Successful in 1m29s

This commit is contained in:
Sainan 2025-03-24 14:51:13 +01:00
parent ea201e3be8
commit 2fb2213c59
3 changed files with 44 additions and 9 deletions

View File

@ -6,7 +6,7 @@ export const leaderboardController: RequestHandler = async (req, res) => {
logger.debug(`data provided to ${req.path}: ${String(req.body)}`);
const payload = JSON.parse(String(req.body)) as ILeaderboardRequest;
res.json({
results: await getLeaderboard(payload.field, payload.before, payload.after)
results: await getLeaderboard(payload.field, payload.before, payload.after, payload.pivotId)
});
};
@ -14,4 +14,5 @@ interface ILeaderboardRequest {
field: string;
before: number;
after: number;
pivotId?: string;
}

View File

@ -1,4 +1,4 @@
import { model, Schema } from "mongoose";
import { Document, model, Schema, Types } from "mongoose";
import { ILeaderboardEntryDatabase } from "../types/leaderboardTypes";
const leaderboardEntrySchema = new Schema<ILeaderboardEntryDatabase>(
@ -16,3 +16,9 @@ leaderboardEntrySchema.index({ leaderboard: 1 });
leaderboardEntrySchema.index({ expiry: 1 }, { expireAfterSeconds: 0 }); // With this, MongoDB will automatically delete expired entries.
export const Leaderboard = model<ILeaderboardEntryDatabase>("Leaderboard", leaderboardEntrySchema);
// eslint-disable-next-line @typescript-eslint/ban-types
export type TLeaderboardEntryDocument = Document<unknown, {}, ILeaderboardEntryDatabase> & {
_id: Types.ObjectId;
__v: number;
} & ILeaderboardEntryDatabase;

View File

@ -1,4 +1,4 @@
import { Leaderboard } from "../models/leaderboardModel";
import { Leaderboard, TLeaderboardEntryDocument } from "../models/leaderboardModel";
import { ILeaderboardEntryClient } from "../types/leaderboardTypes";
export const submitLeaderboardScore = async (
@ -29,14 +29,42 @@ export const submitLeaderboardScore = async (
export const getLeaderboard = async (
leaderboard: string,
before: number,
after: number
after: number,
pivotId: string | undefined = undefined
): Promise<ILeaderboardEntryClient[]> => {
const entries = await Leaderboard.find({ leaderboard })
.sort({ score: -1 })
.skip(before)
.limit(after - before);
let entries: TLeaderboardEntryDocument[];
let r: number;
if (pivotId) {
const pivotDoc = await Leaderboard.findOne({ leaderboard, accountId: pivotId });
if (!pivotDoc) {
return [];
}
const beforeDocs = await Leaderboard.find({
leaderboard,
score: { $gt: pivotDoc.score }
})
.sort({ score: 1 })
.limit(before);
const afterDocs = await Leaderboard.find({
leaderboard,
score: { $lt: pivotDoc.score }
})
.sort({ score: -1 })
.limit(after);
entries = [...beforeDocs.reverse(), pivotDoc, ...afterDocs];
r =
(await Leaderboard.countDocuments({
leaderboard,
score: { $gt: pivotDoc.score }
})) - beforeDocs.length;
} else {
entries = await Leaderboard.find({ leaderboard })
.sort({ score: -1 })
.skip(before)
.limit(after - before);
r = before;
}
const res: ILeaderboardEntryClient[] = [];
let r = before;
for (const entry of entries) {
res.push({
_id: entry.accountId.toString(),