1
.实现投票功能,2
.创建文章数据,3
.对文章进行排序。
实现投票功能,要注重文章的时效性与投票的公平性,所以需要给投票功能加上一些约束条件:
所以我们需要使用:
time
,存储文章的发布时间voted:*
,存储已投票用户名单
*
是被投票文章的 ID
score
,存储文章的得票数ONE_WEEK_IN_SECONDS = 7 * 24 * 60 * 60
def article_vote(r, user_id, article_id):
# 使用 time.time() 获取当前时间
# 减去一周的秒数,从而获取一周前的Unix时间
cutoff = time.time() - ONE_WEEK_IN_SECONDS
if r.zscore('time', article_id) < cutoff:
return
if r.sadd('voted:' + article_id, user_id):
r.zincrby('score', article_id, 1)
当用户尝试投票时,使用 ZSCORE
命令读取 time
有序集合,得到这篇文章的发布时间,再判断文章的发布时间是否超过一周。ZSCORE
命令的语法如下:
r.zscore(key, member)
key
:是有序集合的键名member
:是有序集合中的某个成员若未超过,则使用 SADD
命令尝试将用户追加到这篇文章的已投票用户名单中,如果添加成功,则说明该用户未投过票。SADD
命令的语法是:
r.sadd(key, member)
key
:是集合的键名member
:是要添加进集合的元素由于集合中的元素是唯一的,所以sadd
函数会根据member
是否存在在集合中做出不同返回:
True
False
所以返回为 True
时使用 ZINCRBY
命令来为文章的投票数加 1
。zincrby
函数语法如下:
r.zincrby(key, member, increment)
key
:是有序集合的键名member
:是有序集合中要增加分值的成员increment
:是要增加的分值现在系统中还缺少文章数据,所以我们要提供一个创建文章的函数,并把文章数据存储到 Redis
中。创建文章的步骤如下:
ID
Redis
中time
和 score
两个有序集合中def post_article(r, user, title, link):
# 创建新的文章ID,使用一个整数计数器对 article 键执行自增
# 如果该键不存在,article 的值会先被初始化为 0
# 然后再执行自增命令
article_id = str(r.incr('article'))
voted = 'voted:' + article_id
r.sadd(voted, user)
r.expire(voted, ONE_WEEK_IN_SECONDS)
now = time.time()
article = 'article:' + article_id
r.hmset(article, {
'title': title,
'link': link,
'poster': user,
})
r.zadd('score', article_id, 1)
r.zadd('time', article_id, now)
return article_id
将文章作者加入已投票用户名单中和之前一样,这里不再赘述,但在这里我们需要为这个已投票用户名单设置一个过期时间,让它在一周后(到期后)自动删除,减少 Redis
的内存消耗。为键设置过期时间的命令是:
r.expire(key, seconds)
key
:要设置过期时间的键名seconds
:过期时间的长度(单位:秒)这里我们要设置的时间是一周,所以我们可以使用上面定义好的全局变量 ONE_WEEK_IN_SECONDS
。
接下来要存储文章详细信息了,前面介绍过 hset
可以执行单个字段(域)的设置,这里我们使用 hmset
一次性设置多个字段(域),其语法如下:
r.hmset(key, {field: value, [field: value ...]})
我们可以使用 Python
的散列来一次性存储多个字段(域)到 Redis
,只需要将整个散列当作 key
对应的值通过 hmset
函数设置进去就行。
最后,将初始投票数和创建时间设置到 score
和 time
中都可以通过 ZADD
命令来实现:
r.zadd(key, member, score)
key
:有序集合的键名member
:要加入有序集合的成员score
:该成员的分值这里需要注意的是,因为该篇文章的作者已经被加入到该文章已投票用户名单中,为了保持数据一致性,我们需要将文章的初始投票数设为 1
。
实现了文章投票和创建文章功能,接下来我们就需要将评分最高的文章和最新发布的文章从 Redis
中取出了。
首先我们要根据排序方式的不同:
score
有序集合中取出一定量的文章 ID
(score
有序集合存放文章ID
和对应的投票数)time
有序集合中取出一定量的文章 ID
(time
有序集合存放文章ID
和对应的发布时间)构成一个有序文章信息列表,每个元素都:
HGETALL
命令,取出每篇文章的全部信息def get_articles(r, start, end, order='score'):
ids = r.zrevrange(order, start, end)
articles = []
for id in ids:
article_data = r.hgetall(id)
article_data['id'] = id
articles.append(article_data)
return articles
这里因为需要对有序集合进行排序,所以我们在取出文章 ID
时需要使用到 ZREVRANGE
命令,以分值从大到小的排序方式取出文章 ID
。ZREVRANGE
命令的语法是:
r.zrevrange(key, start, stop)
key
:有序集合的键名start
:开始的数组下标stop
:结束的数组下标得到多个文章 ID
后,我们还需要根据每一个文章 ID
获取文章的全部信息,这时就需要使用到 HGETALL
命令,它的语法如下:
r.hgetall(key)
key
:哈希的键名我们取出文章的全部信息后,还为文章信息添加了一个字段 id
。这是因为文章 ID
在 Redis
中是作为键名存储的,不在值当中,所以我们需要附加这个字段到文章信息中。
实现这些方法后,我们大体实现了一个文章投票的后端处理逻辑,能够为文章投票并能根据投票结果改变文章的排序情况。
根据提示,在右侧Begin-End
区域补充代码,完成简化版文章投票网站的后端处理逻辑:
在 article_vote()
函数中:
r
:Redis 客户端user_id
:投票用户
article_id
:被投票文章
Unix
时间戳,存放在变量 cutoff
该用户没有为该文章投过票
在 post_article()
函数中:
r
:Redis 客户端user
:发布用户
title
:文章标题
link
:文章链接
article_id
,新文章 ID
voted
,新文章已投票用户名单存储键名
article
,新文章详细信息存储键名
now
,文章创建时间
ID
递增的顺序依次创建文章Redis
中,包括字段:
文章链接
发布用户
1
在 get_articles()
函数中:
r
:Redis 客户端start
:从排序为 start
的文章开始获取end
:到排序为 end
的文章结束获取
order
:排序方式,分为两种:
time
:按时间排序score
:按投票数排序已提供文章信息空列表,articles
实现按时间/投票数排序
将排序后的文章及其全部信息组成一个列表:
ID
#!/usr/bin/env python
#-*- coding:utf-8 -*-
import time
ONE_WEEK_IN_SECONDS = 7 * 24 * 60 * 60
def article_vote(r, user_id, article_id):
cutoff = time.time() - ONE_WEEK_IN_SECONDS
# 请在下面完成要求的功能
#********* Begin *********#
if r.zscore('time', article_id) < cutoff:
return
if r.sadd('voted:' + article_id, user_id):
r.zincrby('score', article_id, 1)
#********* End *********#
def post_article(r, user, title, link):
article_id = str(r.incr('article'))
voted = 'voted:' + article_id
now = time.time()
article = 'article:' + article_id
# 请在下面完成要求的功能
#********* Begin *********#
r.sadd(voted, user)
r.expire(voted, ONE_WEEK_IN_SECONDS)
r.hmset(article, {
'title': title,
'link': link,
'poster': user,
})
r.zadd('score', article_id, 1)
r.zadd('time', article_id, now)
#********* End *********#
return article_id
def get_articles(r, start, end, order='score'):
articles = []
# 请在下面完成要求的功能
#********* Begin *********#
ids = r.zrevrange(order, start, end)
for id in ids:
article_data = r.hgetall(id)
article_data['id'] = id
articles.append(article_data)
#********* End *********#
return articles