Plain and simple leaderboard. Ranks are 1-based.
client
: Redis connection objectkey
: KeyType Redis key for the sorted set (usually a string)options
: LeaderboardOptions configurationsortPolicy
: SortPolicy determines which scores are better than others
Allowed values:'high-to-low'
: sort scores in descending order'low-to-high'
: sort scores in ascending order
updatePolicy
: UpdatePolicy determines what happens between old and new scores
Allowed values:'replace'
: the new score will replace the previous one'aggregate'
: previous and new scores will be added'best'
: the best score is kept (determined by the sort policy)
limitTopN
?: number: keep only the top N entries, determined by the sort policy.
This lets you limit the number of entries stored, thus saving memory.
If not specified, or the value is0
, then there is no limit
const lb = new Leaderboard(client, 'lb:test', {
sortPolicy: 'high-to-low',
updatePolicy: 'replace'
// limitTopN: 1000 (disabled, no limit)
});
Note that when you update an entry that doesn't exist, it will be created, so update/insert is the same operation.
-
updateOne(id: ID, value: Score | number, updatePolicy?: UpdatePolicy)
: Promise<Score | void> update a single entryid
: ID id of the entry to updatevalue
: Score | number score or number to addupdatePolicy
?: UpdatePolicy override the default update policy only for this update
The update behaviour is determined by the sort and update policies.
If the update policy is
aggregate
orbest
then the method will return the final score (the addition or the score which was better), otherwise void.await lb.updateOne("player-1", 999); // override update policy await lb.updateOne("player-1", 999, 'replace');
O(log(N))
where N is the number of entries in the leaderboard.Note: why Score | number? When the update policy is set to
replace
orbest
the value should be a Score, but when the update policy is set toaggregate
it behaves more like an amount than a full score. Either way, both are number. -
update(entries: EntryUpdateQuery | EntryUpdateQuery[], updatePolicy?: UpdatePolicy)
: Promise<Score[] | void[]> update one or more entriesentries
: EntryUpdateQuery | EntryUpdateQuery[] entry or entries to updateupdatePolicy
?: UpdatePolicy override the default update policy only for this update
This method is very similar to
updateOne
, but it lets you update multiple entries in one go.Analogous to the return of
updateOne
but as an array, where each value matches the order of the entries in the input.// single await lb.update({ id: "player-1", value: 999 }); // multiple await lb.update([ { id: "player-1", value: 123 }, { id: "player-2", value: 420 }, { id: "player-3", value: 777 }, { id: "player-4", value: 696 } ]); // override update policy await lb.update({ id: "player-1", value: 999 }, 'replace');
O(log(N))
for each entry updated, where N is the number of entries in the leaderboard.
-
remove(ids: ID | ID[])
: Promise<void> remove one or more entries from the leaderboard// single await lb.remove("player-1"); // multiple await lb.remove(["player-1", "player-2", "player-3"]);
O(M*log(N))
where N is the number of entries in the leaderboard and M the number of entries to be removed. -
clear()
: Promise<void> remove all the entries from the leaderboard
Note: it will delete the underlying Redis keyawait lb.clear(); // leaderboard is no more
O(N)
where N is the number of entries in the leaderboard.
-
score(id: ID)
: Promise<Score | null> retrieve the score of an entry, null if it doesn't existid
: ID id of the entry
await lb.score("player-1"); /// === 999 await lb.score("non-existant"); /// === null
O(1)
-
rank(id: ID)
: Promise<Rank | null> retrieve the rank of an entry, null if it doesn't existid
: ID id of the entry
await lb.rank("player-1"); /// === 3 await lb.rank("non-existant"); /// === null
O(log(N))
where N is the number of entries in the leaderboard. -
find(id: ID)
: Promise<Entry | null> retrieve an entry, null if it doesn't existid
: ID id of the entry
await lb.find("player-1"); /// === { "id": "player-1", score: 999, rank: 3 } await lb.find("non-existant"); /// === null
O(log(N))
where N is the number of entries in the leaderboard. -
at(rank: Rank)
: Promise<Entry | null> retrieve an entry at a specific rank, null if out of boundsrank
: Rank rank to query
await lb.rank(3); /// === { "id": "player-1", score: 999, rank: 3 } await lb.rank(10000000); /// === null
O(log(N))
where N is the number of entries in the leaderboard.
-
top(max: number)
: Promise<Entry[]> retrieve the top entriesmax
: number number of entries to return
await lb.top(10); /// === [{ id: "n1", score: 999, rank: 1 }, ... 9 more]
O(log(N)+M)
where N is the number of entries in the leaderboard and M ismax
-
bottom(max: number)
: Promise<Entry[]> retrieve the bottom entries (from worst to better)max
: number number of entries to return
await lb.bottom(10); /// === [{ id: "n10", score: 111, rank: 10 }, ... 9 more]
O(log(N)+M)
where N is the number of entries in the leaderboard and M ismax
-
list(low: Rank, high: Rank)
: Promise<Entry[]> retrieve entries between ranksawait lb.list(100, 200); /// === [{ id: "n100", score: 100, rank: 100 }, ... 100 more]
O(log(N)+M)
where N is the number of entries in the leaderboard and M the number of entries returned (high
-low
+ 1) -
around(id: ID, distance: number, fillBorders?: boolean = false)
: Promise<Entry[]> retrieve the entries around an entryid
: ID id of the entry at the centerdistance
: number number of entries at each side of the queried entryfillBorders
?: boolean whether to include entries at the other side if the entry is too close to one of the borders. In other words, it always makes sure to return at least 2*distance
+1 entries (if there are enough in the leaderboard)
Let's say we have the following entries and we query the 3rd entry:
1st 2dn 3rd 4th 5th 6th 7th 8th 9th 10th ↑ Now we use
around("3rd", 4, fillBorders)
withfillBorders
=false
→ [ 1st, 2nd, 3rd, 4th, 5th, 6th, 7th ] (default)- got 2 + 1 + 4 = 7 elements
fillBorders
=true
→ [ 1st, 2nd, 3rd, 4th, 5th, 6th, 7th, 8th, 9th ]- got 2 + 1 + 6 = 9 elements
await lb.around("3rd", 4); // fillBorders=false by default /// === [ /// { id: "1st", score: 99, rank: 1 }, /// { id: "2nd", score: 88, rank: 2 }, /// { id: "3rd", score: 77, rank: 3 }, /// ... 4 more /// ]
await lb.around("3rd", 4, true); /// === [ /// { id: "1st", score: 99, rank: 1 }, /// { id: "2nd", score: 88, rank: 2 }, /// { id: "3rd", score: 77, rank: 3 }, /// ... 6 more /// ]
O(log(N)+M)
where N is the number of entries in the leaderboard and M is (2*distance
+1) -
listByScore(min: Score, max: Score)
: Promise<Entry[]> retrieve entries within a score rangeawait lb.listByScore(20, 30); /// === [ /// { id: "ecd", score: 20, rank: 37 }, /// { id: "yug", score: 22, rank: 38 }, /// { id: "bls", score: 27, rank: 39 } /// ]
O(log(N)+M)
where N is the number of entries in the leaderboard and M the number of entries returned
exportStream(batchSize: number)
: Readable create a readable stream to iterate all entries in the leaderboardbatchSize
: number number of entries to retrieve per iteration
const stream = lb.exportStream(100); stream.on("data", (entries) => { // process entries // note: (use pause and resume if you need to do async work, check out EXAMPLES.md) }); stream.on("close", () => { // done });
O(log(N)+M)
each iteration, where N is the number of entries in the leaderboard and M the batch size
count()
: Promise<number> returns the number of entries stored in the leaderboard. Complexity:O(1)
redisClient
: Redis redis connectionredisKey
: KeyType sorted set keysortPolicy
: SortPolicy sort policyupdatePolicy
: UpdatePolicy update policy