排行榜设计需求:
- 体现自然榜
- 总榜和分类榜
- 可以人为干预,干预了总榜需要影响分榜。
比如极端情况下总榜前 8 名都是人为干预的。在分类榜里面,某一个专辑可能在自然榜排第一,
也要排在总榜第 8 名后面。
(因为客户端看到的总榜第 8 名, 在分类榜里面看到其他专辑在它之上是不合理的)
我做了如下设计:
top_type 分自然榜和干预榜。
create table daily_top_board
(
id bigserial PRIMARY KEY,
top_date timestamp with time zone,
category_id uuid,
album_id uuid,
top_type smallint,
total_rank smallint,
genre_rank smallint
);
create unique index daily_top_board_index
on daily_top_board (top_date, album_id);
由于客户端呈现的分类榜需要干预榜的全部信息
需要查出干预榜之后,动态排完,再过滤掉非此分类的专辑。
省略得到排过序的自然榜和干预榜查询 sql 。
其中动态重排的主要过程如下:
def merge_helper(alist, blist, key=None):
# 其中 alist 是自然榜 ,blist 是干预榜,都已排序
#(同事 xd 参与讨论过)
c, aindex, bindex, alen, blen, cindex = \
[], 0, 0, len(alist), len(blist), 0
while aindex < alen and bindex < blen:
aitem_data = alist[aindex]
aitem = key(aitem_data) if key else aitem_data
bitem_data = blist[bindex]
bitem = key(bitem_data) if key else bitem_data
if bitem <= aitem:
c.append(bitem_data)
bindex += 1
else:
if (cindex + 1) < bitem:
c.append(aitem_data)
aindex += 1
else:
c.append(bitem_data)
bindex += 1
cindex += 1
if aindex == alen:
c.extend(blist[bindex:])
if bindex == blen:
c.extend(alist[aindex:])
return c
附带看看自然榜的产生,与之前的 model 对应。
# 表的设计假定了 所有专辑都会有分类
# 不用个巨大的榜,总榜拿前 200 个。每个分类必须有前 100 个。
# 如果是一个巨大的榜,动态插入更新运营榜,会带来巨大的数据写入。
with album_score as (
select
id as album_id,
album_category.category_id,
row_number() over ( order by 一些计算公式 desc)
as total_rank,
row_number() over ( partition by album_category.category_id order by 一些计算公式 desc )
as genre_rank
from album
left join album_duration on album.id = album_duration.album_id
left join album_category on album_category.album_id = album.id
-- 省略一下过滤条件
), rank_data as (
select
album_score.category_id,
album_score.album_id,
album_score.genre_rank as genre_rank,
album_score.total_rank as total_rank
from album_score where
(album_score.genre_rank < 101 or
album_score.total_rank < 201
) and
album_score.category_id is not null
) insert into top_board (
top_date, category_id, album_id, total_rank, genre_rank, top_type)
select
$3,
rank_data.category_id,
rank_data.album_id,
rank_data.total_rank,
rank_data.genre_rank,
1
from rank_data
on conflict (top_date, album_id)
do nothing
----- 用这种方式去掉重复专辑。但这里如果干预榜出现了这个专辑
----- 这种设计就丢失了这个专辑原来的排名了,设计就需要取舍。
----- (要不然查询中去除重复,还涉及干预榜,很复杂)
总结:
- 由于每个分类榜都需要前 100 个专辑,但某些冷门的分类,在总榜可能排名很低。如果构建一个巨大的总榜,浪费资源。
- 干预榜。干预榜明确的需求就是指定排名。
- 其中隐含的内部需求(如:运营人员怎么调控,怎么设置排名,操作之后对客户端的影响)设计都需要考虑周全。