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.