1.有序集合的集合操作
集合类型提供了强大的集合操作命令,但是如果需要排序就要用到有序集合类型。Redis为了命令的精简和高性能的操作,就没有提供使用现有命令来实现的功能,再提供额外的命令。比如想直接获取集合运算结果的情况,我们可以使用MULTI,ZINTERSTORE,ZRANGE,DEL和EXEC这5个命令:
MULTI
ZINTERSTORE tempKey
ZRANGE tempKey
DEL tempKey
EXEC
2.SORT命令
SORT命令可以对列表类型、集合类型和有序集合类型键进行排序,并且能完成与关系型数据库中的连接查询类似的操作。
现在博客中标有“java”标签的文章的ID分别是“2”,“6”,“12”和“26”,由于集合类型的元素是无序的,所以无法使用SMEMBERS命令得到排序后的结果。为了能让博客标签页面下的文章也能按照发布时间顺序排列,可以借助SORT命令实现,如下:
redis>SORT tag:java:posts
1) "2"
2) "6"
3) "12"
4) "26"
除了集合类型外,SORT命令还可以对列表类型元素和有序集合类型进行排序:
redis>LPUSH mylist 4 2 5 7 9
(integer) 5
redis>SORT mylist
1) "2"
2) "4"
3) "5"
4) "7"
5) "9"
对有序集合排序时,会忽略元素的分数,只针对元素自身的值进行排序:
127.0.0.1:6379> ZADD myzset 50 2 40 32 45 8
(integer) 3
127.0.0.1:6379> SORT myzset
1) "2"
2) "8"
3) "32"
除了使用SORT命令,我们还可以使用ALPHA命令对字典的非数字类型元素进行排序:
127.0.0.1:6379> LPUSH mylistalpha a c e d b C A
(integer) 7
127.0.0.1:6379> SORT mylistalpha
(error) ERR One or more scores can't be converted into double
127.0.0.1:6379> SORT mylistalpha ALPHA
1) "A"
2) "C"
3) "a"
4) "b"
5) "c"
6) "d"
7) "e"
SORT命令还有类似于关系型数据库的倒序排列,使用DESC:
127.0.0.1:6379> SORT mylistalpha ALPHA DESC
1) "e"
2) "d"
3) "c"
4) "b"
5) "a"
6) "C"
7) "A"
Redis的排序还支持指定查询的条数,使用LIMIT start offset:
127.0.0.1:6379> SORT mylistalpha ALPHA DESC LIMIT 1 3
1) "d"
2) "c"
3) "b"
127.0.0.1:6379>
3.BY参数
BY参数的语法为BY参考键。其中参考键可以是字符串类型键或者是散列类型键的某个字段(表示为键名->字段名)。如果提供了BY参数,SORT命令将不再依据元素自身的值进行排序,而是对每个元素使用的值替换参考键中第一个”*”并获取其值,然后依据该值对元素进行排序。
SORT tag:ruby:posts BY post:*->time DESC
1) "12"
2) "26"
3) "6"
4) "2"
上例中SORT命令会读取post:2、post:6、post:12、post:26几个散列键中的time字段的值并以此决定tag:rubyLposts键中各文章ID的顺序。
除了散列类型外,参考键还可以是字符串类型,比如:
127.0.0.1:6379> LPUSH sortbylist 2 3 1
(integer) 3
127.0.0.1:6379> SET itemscore:1 50
OK
127.0.0.1:6379> SET itemscore:2 100
OK
127.0.0.1:6379> SET itemscore:3 -10
OK
127.0.0.1:6379> SORT sortbylist BY itemscore:* DESC
1) "2"
2) "1"
3) "3"
127.0.0.1:6379>
当参考键名不包含“*”时(即常量键名,与元素值无关),SORT命令将不会执行排序操作,因为Redis认为这种情况是没有意义的(因为所有要比较的值都是一样的)。例如:
127.0.0.1:6379> SORT sortbylist BY anytext
1) "1"
2) "3"
3) "2"
例子中anytext是常量键名(甚至anytext键可以不存在),此时SORT的结果与LRANGE的结果是一样的,没有执行任何排序操作。在不需要排序但需要借助SORT命令获得与元素相关的数据时,变量键名是很有用的。
如果几个元素的参考值相同,则SORT命令会再去比较元素本身的值来决定元素的顺序。像这样:
127.0.0.1:6379> LPUSH sortbylist 4
(integer) 4
127.0.0.1:6379> SET itemscore:4 100
OK
127.0.0.1:6379> SORT sortbylist BY itemscore:* DESC
1) "4"
2) "2"
3) "3"
4) "1"
127.0.0.1:6379>
上例中“4”的参考键的值和“2”的参考键的值是一样的,都是100,所以SORT命令会再比较“4”和“2”元素本身的大小来决定二者的顺序。
当某个元素的参考键不存在时,redis会默认它的参考键值为0:
127.0.0.1:6379> LPUSH sortbylist 6
(integer) 5
127.0.0.1:6379> SORT sortbylist BY itemscore:* DESC
1) "4"
2) "2"
3) "3"
4) "6"
5) "1"
上例中的元素“6”没有设置参考键值,默认它的参考键值为0,所以排在了元素“1”(参考键值为:-10)的前面。
4.GET参数
现在有这么一个需求:上面我们对标签下的文章ID进行排序后获得了ID的列表,现在我们想获取对应文章的title。你是不是想使用HGET命令再获取对应的文章ID的标题,会不会觉得很麻烦。其实这时候我们使用SORT命令的GET参数获取排序后文章的title,就可以解决这个麻烦。
GET参数并不影响排序,他的作用是使SORT命令的返回结果不再是元素本身的值,而是你GET参数中指定的键值。GET命令支持字符串类型和散列类型的键,并使用*作为占位符。下面我们就使用GET参数直接将排序后的文章的title获取出来:
127.0.0.1:6379> SORT tag:ruby:posts BY post:*->time DESC GET post:*->title
SORT命令中还可以有多个GET参数(而BY只能有一个),所以还可以这样用:
127.0.0.1:6379> SORT tag:ruby:posts BY post:*->time DESC GET post:*->title GET post:->time
如果我们想获取文章的ID该怎么办呢,我们可以使用GET #获取键本身的值:
127.0.0.1:6379> SORT tag:ruby:posts BY post:*->time DESC GET post:*->title GET post:*->title GET #