redis-rank
Back-end to generate and manage leaderboards using Redis. Written in TypeScript and Promise-based.
Features
All the library is promise based.
- Plain Leaderboards: insert and update entries. List them in multiple ways.
- Periodic Leaderboards: automatically create leaderboards for different time spans (supported: minute, hourly, daily, weekly, monthly, yearly, all-time)
- Leaderboard Matrix: combine multiple leaderboards with dimensions and features. Update them together with only one call. More info.
- Guaranteed at most one trip to Redis on each function call, taking advantage of Redis's
EVAL
and MULTI
.
Planned features:
- PLANNED: Archive (export) leaderboards to another database for long-term storage
Quick Start
Install
$ npm install redis-rank
Redis 2.6.12 or newer is required. Packages ioredis and moment are dependencies.
Import and connect
First import/require ioredis
and redis-rank
.
ES5
const Redis = require('ioredis');
const RedisRank = require('redis-rank');
const Leaderboard = RedisRank.Leaderboard;
ES6
import { Redis } from 'ioredis';
import { Leaderboard } from 'redis-rank';
Then create a Leaderboard.
You will have to provide a ioredis connection object.
See here for more information on how to set it up.
let ioredis_client = new Redis({
host: "127.0.0.1",
port: 6379
});
let lb = new Leaderboard(ioredis_client, {
path: "lb",
lowToHigh: false
});
Basic usage
All the methods listed here are promises.
lb.add("alice", 25);
lb.add("bob", 13);
lb.add("dave", 42);
lb.add("eve", 54);
lb.add("bob", 27);
lb.incr("alice", 10);
lb.incr("dave", -5);
lb.remove("eve");
lb.total();
lb.peek("bob");
lb.score("dave");
lb.rank("alice");
lb.at(1);
lb.list(5, 10);
lb.top(10);
lb.around("id", 10);
lb.around("id", 10, true);
lb.clear();
Note: most of the methods will return null
if the entry is not found.
Periodic Leaderboard
let plb = new PeriodicLeaderboard(redis, {
path: "plb",
timeFrame: 'all-time',
now(): () => new Date(),
leaderboardOptions: {
lowToHigh: false,
...
}
});
Then every time you need it, call getCurrent
to get the corresponding Leaderboard for the current time.
let lb = plb.getCurrent();
lb.add("pepe", 99);
lb.top(10);
Leaderboard Matrix
A matrix of leaderboards is defined by its dimensions and features. A dimension represents an abstract group (global, region, map) asocciated with a time frame (all-time, weekly). A feature is a kind of leaderboad and score, for example, a basic numeric score, the number of kills, most seconds survived, etc.
Let's say we want to create a leaderboard for a game with 5 dimensions:
- global: a permanent,
all-time
leaderboard monthly
, weekly
, daily
: dynamic, periodic leaderboards- US: a permanent,
all-time
, country specific leaderboard
And some features, lets say:
- kills: number of enemies killed (higher is better)
- coins: number or coins collected (higher is better)
- time: time (in seconds) taken to complete a level (lower is better)
The leaderboard matrix for the game would look like this:
| kills | coins | time |
---|
global | ... | ... | ... |
monthly | ... | ... | ... |
weekly | ... | ... | ... |
daily | ... | ... | ... |
US | ... | ... | ... |
And in code, this looks like:
let mlb = new LeaderboardMatrix(redis, {
path: 'mygame',
dimensions: [{
name: 'global',
timeFrame: 'all-time'
}, {
name: 'monthly',
timeFrame: 'monthly'
}, {
name: 'weekly',
timeFrame: 'weekly'
}, {
name: 'daily',
timeFrame: 'daily'
}, {
name: 'US',
timeFrame: 'all-time'
}],
features: [{
name: 'kills'
}, {
name: 'coins'
}, {
name: 'time',
options: { lowToHigh: true }
}]
});
To add a new entry for the leaderboards you don't have to retrieve every leaderboard and call add
on each one. You can use the add
function of the LeaderboardMatrix
object:
mlb.add(
"pepe",
{
kills: 36,
coins: 92,
time: 342
}, [
'global',
'monthly',
'weekly',
'daily',
]
);
To list entries within the matrix, yo can use top
and around
based on a dimension like this:
lm.top('weekly', 'kills', 3);
[
{ id: 'pepe', rank: 1, kills: 36, coins: 92, time: 342 },
{ id: '....', rank: 2, kills: 27, coins: 123, time: 295 },
{ id: '....', rank: 3, kills: 16, coins: 77, time: 420 }
]
lm.around('monthly', 'time', pepe, 15);
To access a single leaderboard you can use the get
function:
let lb = mlb.get('global', 'kills');
if(lb) {
lb.top(10);
}
API
You can peek at the documented code for more information.
TypeScript definitions are available.
Running tests
A Redis server with default configuration is expected in localhost. Note: The database will be flushed.
$ npm test
I tried with ioredis-mock but it has some problems with lua scripts.
License
MIT. See LICENSE.