Sorting is a common programming task, and you may be tempted to pull data from Redis and sort it client side. However, using Redis's built in sort function will be more performant and general and offers a robust functionality. In this article, we will learn how to sort with Redis using Python.
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
We will need two methods for subscribing to a channel. The first is the subscribe
method where we can pass one or more channels to listen to. Next, we will use the on
method with the message
parameter to list for message events coming to our Redis connection.
Let's open up a new file, index.js
and add a a few keys. The sorting method works with list, sets, and sorted sets.
We start by connecting to our redis instance.
const Redis = require("ioredis")
const redis = new Redis()
Redis can sort a list, set or sorted set at a particular key. Let's start by creating a simple list with numbers. The default sort is by ascending by numbers.
async function main() {
await redis.lpush("my-list", 1)
await redis.lpush("my-list", 2)
await redis.lpush("my-list", 3)
res = await redis.lrange("my-list", 0, -1)
console.log(res) // [ '3', '2', '1' ]
res = await redis.sort("my-list")
console.log(res) // [ '1', '2', '3' ]
}
(async () => {
await main()
})()
We can also sort descending by using the desc
property.
res = await redis.sort("my-list", "desc")
console.log(res) // [ '3', '2', '1' ]
The next type of sorting is alpha sorting. Let's create a list with letters. We can sort by alpha using the alpha
parameter. You can also use the desc
parameter with alpha.
await redis.lpush("my-list", "b")
await redis.lpush("my-list", "c")
await redis.lpush("my-list", "a")
res = await redis.lrange("my-list", 0, -1)
console.log(res) // [ 'a', 'c', 'b' ]
res = await redis.sort("my-list", "alpha")
console.log(res) // [ 'a', 'b', 'c' ]
res = await redis.sort("my-list", "alpha", "desc")
console.log(res) // [ 'c', 'b', 'a' ]
Another feature of the sort is the ability to limit and offset using the limit
parameter respectively. This how you can paginate through large lists or sets. This can also be combined with other properties.
res = await redis.sort("my-list", "LIMIT", 1, 2, "alpha", "desc")
console.log(res) // ['b', 'a']
Getting to a more advanced sorting, we can use scores set in other keys to sort our list. For example, let's say we have the ids of some restaurants in a DB and the avg ratings sort in other keys. We can use the keys to sort by matching. Here is an example.
// Creating the ratings
await redis.set("restaurant_rating_1", 10)
await redis.set("restaurant_rating_2", 5)
// Add the ids to a list
await redis.lpush('res_ids', 1)
await redis.lpush('res_ids', 2)
// Sort using the ratings
res = await redis.sort('res_ids', "by", "restaurant_rating_*", "desc")
console.log(res) // [ '1', '2' ]
Continuing with this example, let's say we have some extra keys to store the restaurant locations. We can return those values instead of the ids in a list using the get
property.
// Creating the ratings
await redis.set("restaurant_rating_1", 10)
await redis.set("restaurant_rating_2", 5)
await redis.set("restaurant_location_1", "colorado")
await redis.set("restaurant_location_2", "texas")
// Add the ids to a list
await redis.lpush('res_ids', 1)
await redis.lpush('res_ids', 2)
// Sort using the ratings
res = await redis.sort('res_ids', "by", "restaurant_rating_*", "get", "restaurant_location_*", "desc")
console.log(res) // ['colorado', 'texas']
Finally, let's say that we stored hashmap data of the restaurants instead of all the extra keys above. We can sort by properties with the following matching methods.
// Creating the ratings
await redis.hset("restaurant_1", "rating", 10)
await redis.hset("restaurant_1", "location", "colorado")
await redis.hset("restaurant_2", "rating", 5)
await redis.hset("restaurant_2", "location", "texas")
//// Add the ids to a list
await redis.lpush('res_ids', 1)
await redis.lpush('res_ids', 2)
//// Sort using the ratings
res = await redis.sort('res_ids', "by", "restaurant_*->rating", "get", "restaurant_*->location", "desc")
console.log(res) // ['colorado', 'texas']