排行榜设计需求:

  1. 体现自然榜
  2. 总榜和分类榜
  3. 可以人为干预,干预了总榜需要影响分榜。

比如极端情况下总榜前 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

    ----- 用这种方式去掉重复专辑。但这里如果干预榜出现了这个专辑 
    ----- 这种设计就丢失了这个专辑原来的排名了,设计就需要取舍。
    ----- (要不然查询中去除重复,还涉及干预榜,很复杂)

    

总结:

  1. 由于每个分类榜都需要前 100 个专辑,但某些冷门的分类,在总榜可能排名很低。如果构建一个巨大的总榜,浪费资源。
  2. 干预榜。干预榜明确的需求就是指定排名。
  3. 其中隐含的内部需求(如:运营人员怎么调控,怎么设置排名,操作之后对客户端的影响)设计都需要考虑周全。

 

jiamo post at 2021-02-23