Redis SORT_RO Command Explained

The Redis SORT_RO command is a read-only variant of the SORT command. It allows us to sort lists, sets, and sorted sets.

The SORT command enables us to have the sorted elements returned to the client, or stored in a separate key. But the SORT_RO command only allows us to have them returned to the client.

The SORT_RO variant was introduced in Redis 7.0.0 to enable SORT functionality in read-only replicas without breaking compatibility on command flags.

Syntax

SORT_RO key [BY pattern] [LIMIT offset count] [GET pattern [GET pattern ...]] [ ASC | DESC] [ALPHA]

The various options are explained in the following examples.

Example

Suppose we create a list:

LPUSH scores 10 8 3 7 5 12 7 0 1 9 2

Result:

(integer) 11

And let’s take a look at the list (without sorting it):

LRANGE scores 0 -1

Result:

 1) "2"
 2) "9"
 3) "1"
 4) "0"
 5) "7"
 6) "12"
 7) "5"
 8) "7"
 9) "3"
10) "8"
11) "10"

Now let’s use the SORT_RO command to change the order:

SORT_RO scores

Result:

 1) "0"
 2) "1"
 3) "2"
 4) "3"
 5) "5"
 6) "7"
 7) "7"
 8) "8"
 9) "9"
10) "10"
11) "12"

The ASC and DESC Modifiers

By default, the SORT_RO command sorts the results in ascending order. So when using numbers, a list is sorted from small to large. We can explicitly enforce this with the ASC modifier. We can use the DESC modifier to sort from large to small.

Here’s the previous example again, but this time with the ASC modifier:

SORT_RO scores ASC

Result:

 1) "0"
 2) "1"
 3) "2"
 4) "3"
 5) "5"
 6) "7"
 7) "7"
 8) "8"
 9) "9"
10) "10"
11) "12"

We get the same result as in the previous example.

Here it is with the DESC modifier:

SORT_RO scores DESC

Result:

 1) "12"
 2) "10"
 3) "9"
 4) "8"
 5) "7"
 6) "7"
 7) "5"
 8) "3"
 9) "2"
10) "1"
11) "0"

The ALPHA Modifier

We can use the ALPHA modifier to sort string values lexicographically.

Suppose we create this list:

LPUSH pets "Tweet" "Fluffy" "Scratch" "Bark" "Fluffy" "Woof"

Result:

(integer) 6

We can look at its contents (unsorted):

LRANGE pets 0 -1

Result:

1) "Woof"
2) "Fluffy"
3) "Bark"
4) "Scratch"
5) "Fluffy"
6) "Tweet"

Now let’s use SORT_RO to sort the contents in ascending order:

SORT_RO pets ALPHA

Result:

1) "Bark"
2) "Fluffy"
3) "Fluffy"
4) "Scratch"
5) "Tweet"
6) "Woof"

Or we can use the ASC modifier to explicitly sort it in ascending order:

SORT_RO pets ASC ALPHA

Result:

1) "Bark"
2) "Fluffy"
3) "Fluffy"
4) "Scratch"
5) "Tweet"
6) "Woof"

And here it is in descending order:

SORT_RO pets DESC ALPHA

Result:

1) "Woof"
2) "Tweet"
3) "Scratch"
4) "Fluffy"
5) "Fluffy"
6) "Bark"

We can also apply the ALPHA modifier on numbers to sort them alphabetically:

SORT_RO scores ALPHA

Result:

 1) "0"
 2) "1"
 3) "10"
 4) "12"
 5) "2"
 6) "3"
 7) "5"
 8) "7"
 9) "7"
10) "8"
11) "9"

The LIMIT Modifier

We can use the LIMIT modifier to limit how many elements are returned. This modifier takes an offset amount as well as the number of elements to return from that offset.

We can specify no offset by using 0 as the offset:

SORT_RO pets LIMIT 0 3 ASC ALPHA

Result:

1) "Bark"
2) "Fluffy"
3) "Fluffy"

And here it is with an offset:

SORT_RO pets LIMIT 2 3 ASC ALPHA

Result:

1) "Fluffy"
2) "Scratch"
3) "Tweet"

The BY Option

We can use the BY option to sort by external keys as weights to compare instead of comparing the actual elements in the list.

Suppose we create a list like this:

LPUSH uid 1 2 3 4 5

Result:

(integer) 5

And we set the following points_* keys:

MSET points_1 500 points_2 200 points_3 300 points_4 400 points_5 100

Result:

OK

We can sort mylist by the points_* keys:

SORT_RO uid BY points_* ASC

Result:

1) "5"
2) "2"
3) "3"
4) "4"
5) "1"

The order of the elements is reflective of the values in the points_* keys.

Here it is when using DESC for descending order:

SORT_RO uid BY points_* DESC

Result:

1) "1"
2) "4"
3) "3"
4) "2"
5) "5"

Getting External Keys

The above technique can be handy when each list element represents a unique ID of a separate object. These objects can have their weights stored in external keys (in this case, the points_* keys), which can be used for sorting them.

We can also use the GET option to retrieve the external keys.

Suppose we create the following objects:

MSET user_1 "Homer" user_2 "Bart" user_3 "Marge" user_4 "Maggie" user_5 "Milhouse"

Result:

OK

We can now build on the previous example by doing the following:

SORT_RO uid BY points_* GET user_*

Result:

1) "Milhouse"
2) "Bart"
3) "Marge"
4) "Maggie"
5) "Homer"

Here it is in descending order:

SORT_RO uid BY points_* GET user_* DESC

Result:

1) "Homer"
2) "Maggie"
3) "Marge"
4) "Bart"
5) "Milhouse"

We can also use another GET with the # pattern to get the other element:

SORT_RO uid BY points_* GET user_* GET #

Result:

 1) "Milhouse"
 2) "5"
 3) "Bart"
 4) "2"
 5) "Marge"
 6) "3"
 7) "Maggie"
 8) "4"
 9) "Homer"
10) "1"

For hash fields, we can use the following syntax for BY and GET:

SORT_RO points BY weight_*->fieldname GET object_*->fieldname

Sorting by a Non-Existent Key

We can use the BY option to sort by a key that doesn’t exist. When we do this, it skips the sorting altogether:

SORT_RO points BY nonexistentkey

Result:

1) "3"
2) "2"
3) "4"
4) "1"
5) "5"

However, we can still use the ASC and DESC modifiers:

SORT_RO points BY nonexistentkey DESC

Result:

1) "5"
2) "1"
3) "4"
4) "2"
5) "3"

The STORE Option

As mentioned, the SORT_RO command doesn’t provide us with the ability to store its results. This is by design, as there has always been the SORT command that does allow us to store the results.

Therefore, if you need to store the results in another key, use the SORT command with the STORE option.