handle personal leaderboards (pivotId)
Some checks failed
Build / build (18) (push) Has been cancelled
Build / build (22) (push) Has been cancelled
Build / build (20) (push) Has been cancelled
Build / build (18) (pull_request) Successful in 44s
Build / build (20) (pull_request) Successful in 1m11s
Build / build (22) (pull_request) Successful in 1m23s

This commit is contained in:
Sainan 2025-03-24 14:51:13 +01:00
parent ea201e3be8
commit 4e3823473f
3 changed files with 41 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)}`); logger.debug(`data provided to ${req.path}: ${String(req.body)}`);
const payload = JSON.parse(String(req.body)) as ILeaderboardRequest; const payload = JSON.parse(String(req.body)) as ILeaderboardRequest;
res.json({ 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; field: string;
before: number; before: number;
after: 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"; import { ILeaderboardEntryDatabase } from "../types/leaderboardTypes";
const leaderboardEntrySchema = new Schema<ILeaderboardEntryDatabase>( 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. leaderboardEntrySchema.index({ expiry: 1 }, { expireAfterSeconds: 0 }); // With this, MongoDB will automatically delete expired entries.
export const Leaderboard = model<ILeaderboardEntryDatabase>("Leaderboard", leaderboardEntrySchema); 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"; import { ILeaderboardEntryClient } from "../types/leaderboardTypes";
export const submitLeaderboardScore = async ( export const submitLeaderboardScore = async (
@ -29,14 +29,39 @@ export const submitLeaderboardScore = async (
export const getLeaderboard = async ( export const getLeaderboard = async (
leaderboard: string, leaderboard: string,
before: number, before: number,
after: number after: number,
pivotId: string | undefined = undefined
): Promise<ILeaderboardEntryClient[]> => { ): Promise<ILeaderboardEntryClient[]> => {
const entries = await Leaderboard.find({ leaderboard }) let entries: TLeaderboardEntryDocument[];
.sort({ score: -1 }) let r: number;
.skip(before) if (pivotId) {
.limit(after - before); const pivotDoc = await Leaderboard.findOne({ leaderboard, accountId: pivotId });
if (!pivotDoc) {
return [];
}
const beforeDocs = await Leaderboard.find({
score: { $gt: pivotDoc.score }
})
.sort({ score: 1 })
.limit(before);
const afterDocs = await Leaderboard.find({
score: { $lt: pivotDoc.score }
})
.sort({ score: -1 })
.limit(after);
entries = [...beforeDocs.reverse(), pivotDoc, ...afterDocs];
r = await Leaderboard.countDocuments({
leaderboard: leaderboard,
score: { $gt: pivotDoc.score }
});
} else {
entries = await Leaderboard.find({ leaderboard })
.sort({ score: -1 })
.skip(before)
.limit(after - before);
r = before;
}
const res: ILeaderboardEntryClient[] = []; const res: ILeaderboardEntryClient[] = [];
let r = before;
for (const entry of entries) { for (const entry of entries) {
res.push({ res.push({
_id: entry.accountId.toString(), _id: entry.accountId.toString(),