Redis Sort with Python

09.19.2021

Intro

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.

Setting up Redis

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

Installing Redis Modules

In python, the main used redis module is called redis-py and can be installed using the follows.

pip install redis

Writing the Code

Let's open up a new file, index.py and add a a few keys. The sorting method works with list, sets, and sorted sets.

We start by connecting to our redis instance.

import redis

r = redis.Redis(host='localhost', port=6379, db=0)

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.

r.lpush("my-list", 1)
r.lpush("my-list", 2)
r.lpush("my-list", 3)

res = r.lrange("my-list", 0, -1)
print(res) # [b'3', b'2', b'1']

res = r.sort("my-list")
print(res) # [b'1', b'2', b'3']

We can also sort descinding by using the desc property.

res = r.sort("my-list", desc=True)
print(res) # [b'3', b'2', b'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 paramter with alpha.


r.lpush("my-list", "b")
r.lpush("my-list", "c")
r.lpush("my-list", "a")

res = r.lrange("my-list", 0, -1)
print(res) # [b'a', b'c', b'b']

res = r.sort("my-list", alpha=True)
print(res) # [b'a', b'b', b'c']'

res = r.sort("my-list", alpha=True, desc=True)
print(res) # [b'c', b'b', b'a']

Another feature of the sort is the ability to limit and offset using the num and start parameters respectively. This whay you can paginate through large lists or sets. This can also be combined with other properties.

res = r.sort("my-list", alpha=True, desc=True, start=1, num=2)
print(res) # [b'b', 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 restraunts 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
r.set("restraunt_rating_1", 10)
r.set("restraunt_rating_2", 5)

## Add the ids to a list
r.lpush('res_ids', 1)
r.lpush('res_ids', 2)

## Sort using the ratings
res = r.sort('res_ids', by="restraunt_rating_*", desc=True)
print(res) # [b'1', b'2']

Continuing with this example, let's say we have some extra keys to store the restraunt locations. We can return those values instead of the ids in a list using the get property.

## Creating the ratings
r.set("restraunt_rating_1", 10)
r.set("restraunt_rating_2", 5)

r.set("restraunt_location_1", "colorado")
r.set("restraunt_location_2", "texas")


## Add the ids to a list
r.lpush('res_ids', 1)
r.lpush('res_ids', 2)

## Sort using the ratings
res = r.sort('res_ids', by="restraunt_rating_*", get="restraunt_location_*", desc=True)
print(res) # [b'colorado', b'texas']

Finally, let's say that we stored hashmap data of the restraunts instead of all the extra keys above. We can sort by properties with the following matching methods.

## Creating the ratings
r.hset("restraunt_1", "rating", 10)
r.hset("restraunt_1", "location", "colorado")

r.hset("restraunt_2", "rating", 5)
r.hset("restraunt_2", "location", "texas")


## Add the ids to a list
r.lpush('res_ids', 1)
r.lpush('res_ids', 2)

## Sort using the ratings
res = r.sort('res_ids', by="restraunt_*->rating", get="restraunt_*->location", desc=True)
print(res) # [b'colorado', b'texas']