SORT:对键的值进行排序
用户可以通过执行 SORT 命令对列表元素、集合元素或者有序集合成员进行排序。为了让用户能够以不同的方式进行排序,Redis 为 SORT 命令提供了非常多的可选项,如果我们以不给定任何可选项的方式直接调用 SORT 命令,那么命令将对指定键存储的元素执行数字值排序:
SORT key
在默认情况下,SORT 命令将按照从小到大的顺序依次返回排序后的各个值。
比如,以下例子就展示了如何对lucky-numbers集合存储的6个数字值进行排序:
redis> SMEMBERS lucky-numbers -- 以乱序形式存储的集合元素
1) "1024"
2) "123456"
3) "10086"
4) "3.14"
5) "888"
6) "256"
redis> SORT lucky-numbers -- 排序后的集合元素
1) "3.14"
2) "256"
3) "888"
4) "1024"
5) "10086"
6) "123456"
以下例子则展示了如何对 message-queue 列表中的数字值进行排序:
redis> LRANGE message-queue 0 -1 -- 根据插入顺序进行排列的列表元素
1) "1024"
2) "256"
3) "128"
4) "512"
5) "64"
redis> SORT message-queue -- 排序后的列表元素
1) "64"
2) "128"
3) "256"
4) "512"
5) "1024"
指定排序方式
在默认情况下,SORT命令执行的是升序排序操作:较小的值将被放到结果的较前位置,而较大的值则会被放到结果的较后位置。
通过使用可选的ASC选项或者DESC选项,用户可以指定SORT命令的排序方式,其中ASC表示执行升序排序操作,而DESC则表示执行降序排序操作:
SORT key [ASC|DESC]
降序排序操作的做法与升序排序操作的做法正好相反,它会把较大的值放到结果的较前位置,而较小的值则会被放到结果的较后位置。
举个例子,如果我们想要对lucky-numbers集合的元素实施降序排序,那么只需要执行以下代码即可:
redis> SORT lucky-numbers DESC
1) "123456"
2) "10086"
3) "1024"
4) "888"
5) "256"
6) "3.14"
因为SORT命令在默认情况下进行的就是升序排序,所以SORT key命令和SORT key ASC命令产生的效果是完全相同的,因此我们在一般情况下并不会用到ASC选项——除非在特殊情况下,需要告诉别人你正在进行的是升序排序。
对字符串值进行排序
SORT命令在默认情况下进行的是数字值排序,如果我们尝试直接使用 SORT 命令去对字符串元素进行排序,那么命令将产生一个错误:
redis> SMEMBERS fruits
1) "cherry"
2) "banana"
3) "apple"
4) "mango"
5) "dragon fruit"
6) "watermelon"
redis> SORT fruits
(error) ERR One or more scores can't be converted into double
为了让SORT命令能够对字符串值进行排序,我们必须让SORT命令执行字符串排序操作而不是数字值排序操作,这一点可以通过使用ALPHA选项来实现:
SORT key [ALPHA]
作为例子,我们可以使用带ALPHA选项的SORT命令对fruits集合进行排序:
redis> SORT fruits ALPHA
1) "apple"
2) "banana"
3) "cherry"
4) "dragon fruit"
5) "mango"
6) "watermelon"
或者使用以下命令,对 test-record 有序集合的成员进行排序:
redis> ZRANGE test-record 0 -1 WITHSCORES -- 在默认情况下,有序集合成员将根据分值进行排序
1) "ben"
2) "70"
3) "aimee"
4) "86"
5) "david"
6) "99"
7) "cario"
8) "100"
redis> SORT test-record ALPHA -- 但使用 SORT 命令可以对成员本身进行排序
1) "aimee"
2) "ben"
3) "cario"
4) "david"
只获取部分排序结果
在默认情况下,SORT 命令将返回所有被排序的元素,但如果我们只需要其中一部分排序结果,那么可以使用可选的 LIMIT 选项:
SORT key [LIMIT offset count]
其中 offset 参数用于指定返回结果之前需要跳过的元素数量,而 count 参数则用于指定需要获取的元素数量。
举个例子,如果我们想要知道 fruits 集合在排序之后的第 3 个元素是什么,那么只需要执行以下调用就可以了:
redis> SORT fruits ALPHA LIMIT 2 1
1) "cherry"
注意,因为 offset 参数的值是从 0 开始计算的,所以这个命令在获取第 3 个被排序元素时使用了 2 而不是 3 来作为偏移量。
获取外部键的值作为结果
在默认情况下,SORT 命令将返回被排序的元素作为结果,但如果用户有需要,也可以使用GET选项去获取其他值作为排序结果:
SORT key [[GET pattern] [GET pattern] ...]
一个SORT命令可以使用任意多个GET pattern选项,其中pattern参数的值可以是:
-
包含 * 符号的字符串。
-
包含 * 符号和
->
符号的字符串。 -
一个单独的 # 符号。
接下来将分别介绍这3种值的用法和用途。
-
获取字符串键的值
当 pattern 参数的值是一个包含 * 符号的字符串时,SORT 命令将把被排序的元素与*符号进行替换,构建出一个键名,然后使用GET命令去获取该键的值。
举个例子,假设数据库中存储着表11-2所示的一些字符串键,那么我们可以通过执行以下命令对fruits集合的各个元素进行排序,然后根据排序后的元素获取各种水果的价格:
redis> SORT fruits ALPHA GET *-price 1) "8.5" 2) "4.5" 3) "7" 4) "6" 5) "5" 6) "3.5"
Figure 1. 表11-2 存储水果价格的各个字符串键以及它们的值这个SORT命令的执行过程可以分为以下3个步骤:
-
对fruits集合的各个元素进行排序,得出一个由"apple"、"banana"、"cherry"、"dragon fruit"、"mango"、"watermelon"组成的有序元素排列。
-
将排序后的各个元素与*-price模式进行匹配和替换,得出键名 "apple-price"、"banana-price"、"cherry-price"、"dragon fruit-price"、"mango-price"和"watermelon-price"。
-
使用GET命令获取以上各个键的值,并将这些值依次放入结果列表中,最后把结果列表返回给客户端。
图11-2以图形方式展示了整个SORT命令的执行过程。
Figure 2. 图11-2 SORT fruits ALPHA GET*-price命令的执行过程 -
-
获取散列中的键值
当pattern参数的值是一个包含*符号和→符号的字符串时,SORT命令 将使用→左边的字符串为散列名,→右边的字符串为字段名,调用 HGET命令,从散列中获取指定字段的值。此外,用户传入的散列名还需要包含*符号,这个*符号将被替换成被排序的元素。
举个例子,假设数据库中存储着表11-3所示的apple-info、bananainfo等散列,而这些散列的inventory键则存储着相应水果的存货量,那么我们可以通过执行以下命令,对fruits集合的各个元素进行排序,然后根据排序后的元素获取各种水果的存货量:
redis> SORT fruits ALPHA GET *-info->inventory 1) "1000" 2) "300" 3) "50" 4) "500" 5) "250" 6) "324"
Figure 3. 表11-3 存储着水果信息的散列这个SORT命令的执行过程可以分为以下3个步骤:
-
对fruits集合的各个元素进行排序,得出一个由"apple"、"banana"、"cherry"、"dragon fruit"、"mango"、"watermelon"组成的有序元素排列。
-
将排序后的各个元素与*-info模式进行匹配和替换,得出散列名"apple-info"、"banana-info"、"cherry-info"、"dragon fruitinfo"、"mango-info"和"watermelon-info"。
-
使用HGET命令,从以上各个散列中取出inventory字段的值,并将这些值依次放入结果列表中,最后把结果列表返回给客户端。
图11-3以图形方式展示了整个SORT命令的执行过程。
Figure 4. 图11-3 SORT fruits ALPHA GET*-info→inventory命令的执行过程 -
-
获取被排序元素本身
当pattern参数的值是一个#符号时,SORT命令将返回被排序的元素本身。
因为SORT key命令和SORT key GET#命令返回的是完全相同的结果,所以单独使用GET#并没有任何实际作用:
redis> SORT fruits ALPHA
1) "apple"
2) "banana"
3) "cherry"
4) "dragon fruit"
5) "mango"
6) "watermelon"
redis> SORT fruits ALPHA GET # -- 与上一个命令的结果完全相同
1) "apple"
2) "banana"
3) "cherry"
4) "dragon fruit"
5) "mango"
6) "watermelon"
因此,我们一般只会在同时使用多个GET选项时,才使用GET#获取被排序的元素。比如,以下代码就展示了如何在对水果进行排序的同时,获取水果的价格和库存量:
redis> SORT fruits ALPHA GET # GET *-price GET *-info->inventory
1) "apple" -- 水果
2) "8.5" -- 价格
3) "1000" -- 库存量
4) "banana"
5) "4.5"
6) "300"
7) "cherry"
8) "7"
9) "50"
10) "dragon fruit"
11) "6"
12) "500"
13) "mango"
14) "5"
15) "250"
16) "watermelon"
17) "3.5"
18) "324"
使用外部键的值作为排序权重
在默认情况下,SORT命令将使用被排序元素本身作为排序权重,但在有需要时,用户可以通过可选的BY选项指定其他键的值作为排序的权重:
SORT key [BY pattern]
pattern参数的值既可以是包含*符号的字符串,也可以是包含*符号和→符号的字符串,这两种值的作用和效果与使用GET选项的作用和效果一样:前者用于获取字符串键的值,而后者则用于从散列中获取指定字段的值。
举个例子,通过执行以下命令,我们可以使用存储在字符串键中的水果价格作为权重,对水果进行排序:
redis> SORT fruits BY *-price
1) "watermelon"
2) "banana"
3) "mango"
4) "dragon fruit"
5) "cherry"
6) "apple"
因为上面这个排序结果只展示了水果的名字,却没有展示水果的价格,所以这个排序结果并没有清楚地展示水果的名字和价格之间的关系。相反,如果我们在使用BY选项的同时,使用两个GET选项去获取水果的名字以及价格,那么就能够直观地看出水果是按照价格进行排序的了:
redis> SORT fruits BY *-price GET # GET *-price
1) "watermelon" -- 水果的名字
2) "3.5" -- 水果的价格
3) "banana"
4) "4.5"
5) "mango"
6) "5"
7) "dragon fruit"
8) "6"
9) "cherry"
10) "7"
11) "apple"
12) "8.5"
同样,我们还可以通过执行以下命令,使用散列中记录的库存量作为权重,对水果进行排序并获取它们的库存量:
redis> SORT fruits BY *-info->inventory GET # GET *-info->inventory
1) "cherry" -- 水果的名字
2) "50" -- 水果的库存量
3) "mango"
4) "250"
5) "banana"
6) "300"
7) "watermelon"
8) "324"
9) "dragon fruit"
10) "500"
11) "apple"
12) "1000"
保存排序结果
在默认情况下,SORT 命令会直接将排序结果返回给客户端,但如果用户有需要,也可以通过可选的STORE选项,以列表形式将排序结果存储到指定的键中:
SORT key [STORE destination]
如果用户给定的destination键已经存在,那么SORT命令会先移除该键,然后再存储排序结果。带有STORE选项的SORT命令在成功执行之后将返回被存储的元素数量作为结果。
作为例子,以下代码展示了如何将排序fruits集合所得的结果存储到 sorted-fruits列表中:
redis> SORT fruits ALPHA STORE sorted-fruits
(integer) 6 -- 有6个已排序元素被存储了
redis> LRANGE sorted-fruits 0 -1 -- 查看排序结果
1) "apple"
2) "banana"
3) "cherry"
4) "dragon fruit"
5) "mango"
6) "watermelon"