Sorted sets are a powerful data set used in redis. If you are familiar with binary search, you know the importance of having a presorted set to access items in log(n). This data type is often used in analytics and leaderboards. In this article, we will learn how to work with sorted sets in Redis and Nodejs.
For setting up Redis, I would recommend using a service for you in prod. Azure for example, has a great redis service that scales easily. However, you will want to learn redis and eventually how to scale it yourself. This will help with debugging cloud services or eventually, saving money and not using them.
We will start our intro to redis via using docker compose. Create a docker-compose.yml
file and add the following.
version: "3.2"
services:
redis:
image: "redis:alpine"
command: redis-server
ports:
- "6379:6379"
volumes:
- $PWD/redis-data:/var/lib/redis
- $PWD/redis.conf:/usr/local/etc/redis/redis.conf
environment:
- REDIS_REPLICATION_MODE=master
Ensure you have docker installed and run
docker-compose up
There are two modules I see often used in nodejs. I will tend towards
ioredis
as it has built in support for promises and many other features in redis.
npm install ioredis
Let's open up a new file, index.js
and go through many of
the common commands you will used with lists in redis.
We can create a Sorted Set and add a value to the set using the following. The example below is similar to creating a unique list so:
const movieRatings = [
{
key: "star-wars",
score: 4,
},
{
key: "marvel",
score: 5,
},
]
We can add items to a sorted set by using the zadd
method. We pass in the name of the set, the score, then the key we want to add the score too.
// Sorted Set Add
await redis.zadd("movie-ratings", 4, "star-wars")
await redis.zadd("movie-ratings", 5, "marvel")
We can get the member of a sorted set using the zrange
function. We pass the set name, the start index, then the end index.
// Sorted Set Range
const result = await redis.zrange("movie-ratings", 1, 3)
console.log(result) // [ 'marvel' ]
For each of the examples below, I will use the following template to run all the commands. Here is my full index file. We will just replace the commands each time.
const Redis = require("ioredis")
const redis = new Redis({})
async function main() {
// Sorted Set Add
await redis.zadd("movie-ratings", 4, "star-wars")
await redis.zadd("movie-ratings", 5, "marvel")
// Sorted Set Range
const result = await redis.zrange("movie-ratings", 1, 3)
console.log(result)
}
(async () => {
await main()
})()
If we want to get the size of our set, we can use zcard
.
async function sortedSetSize() {
// Sorted Set Count
const result = await redis.zcard("movie-ratings")
console.log(result) // 2
}
We can count the number of items between a min and max value using the zcount
method.
async function sortedSetCount() {
// Sorted Set Count
const result = await redis.zcount("movie-ratings", 4, 5)
console.log(result) // 2
const result2 = await redis.zcount("movie-ratings", 1, 3)
console.log(result2) // 0
}
Sorted set range will return a list of items within an index. We can also use 0, -1 to select all item in the set.
async function sortedSetRange() {
// Sorted Set Range
const result = await redis.zrange("movie-ratings", 1, 3)
console.log(result) // [ 'marvel' ]
const result2 = await redis.zrange("movie-ratings", 0, -1)
console.log(result2) // [ 'star-wars', 'marvel' ]
}
To get the different or elements in one set and not another, we can use the zdiff
function.
async function sortedSetDiff() {
// Sorted Set Diff
await redis.zadd("movie-ratings-user2", 3, "marvel")
const numberOfSets = 2
const result = await redis.zdiff(numberOfSets, "movie-ratings", "movie-ratings-user2")
console.log(result) // [ 'star-wars' ]
}
Similar to the above, we can use the zinter
method to see the items shared amongst two sets.
async function sortedSetInter() {
// Sorted Set Intersection
await redis.zadd("movie-ratings-user2", 3, "marvel")
const numberOfSets = 2
const result = await redis.zinter(numberOfSets, "movie-ratings", "movie-ratings-user2")
console.log(result) // [ 'marvel' ]
}
If we want to increment a a score, we can use zincrby
. Below we increment the rating of new-movie
by 2.
async function sortedSetIncrementBy() {
// Sorted Set Increment By
await redis.zadd("movie-ratings", 4, "new-movie")
await redis.zincrby("movie-ratings", 2, "new-movie")
}
If we have a list of keys, and we want to get their scores, we can use the zmscore
.
async function sortedSetMultipleScore() {
// Sorted Set Multiple Score
const result = await redis.zmscore("movie-ratings", "star-wars", "marvel")
console.log(result) // [ '4', '5' ]
}
To get the ranking of a specific item, think position on a leader board, we can use the zrank
method.
async function sortedSetRank() {
// Sorted Set Rank
const result = await redis.zrank("movie-ratings", "star-wars")
console.log(result) // 1
const result2 = await redis.zrank("movie-ratings", "marvel")
console.log(result2) // 2
}