内容分类扩展性标签设计

avatar
SugarTurboS Club @SugarTurboS
  • 苏格团队
  • 作者:YaoLang

角色:产品汪小T,程序员小C


小T:小C,有活干了。我们想做个在线题库系统,老师可以搜索题目来备课。

小C看着简易的需求稿,心想,我一分钟几百万上下,竟然找我做这么简单的需求。建个题目表不就完事了。

小C:题目数据从哪里来,包含什么属性?

小T:我们第一期题目数据是从A公司那里买过来的,题目包含正文,选项,答案,题型,难度。

小C:嗯,也就是我要建一张question表,包括这五个属性。那题型和难度有哪些呢?

小T:题型有五种,单选,多选,判断,填空,解答。难度有3种,简单,一般,困难。用户可以根据题型或者难度来筛选。 小C拿着笔画了一下:OK,表设计出来了

t_question表

字段 属性 描述
uid char(32) 唯一标示
body TEXT 正文
options JSON 题目选项
answer TEXT 答案
type int(11) 题目类型,1单选、2多选、3判断、4填空、5解答
difficult int(11) 题目难度,1简单、2一般、3困难

小C:搜索根据body, options模糊匹配,然后筛选让前端传入type = 1或者difficult = 3 进行题型和难度的筛选

小T:哇,果然靠谱,那我们上线吧。


小T:小C,我们的题库系统发到市场上有很多用户反馈说题目量不太够,最近我们找到了B公司合作,希望能把B公司的题库也整合到我们的系统,数据的结构和A公司的很相似,你看下要弄多久。

小C心想,敢情这产品汪不生产题目,只是题目的搬运工啊。

小C:导一下数据就完事了。把接口文档发我对接一下就好了。

小T:好,待会文档发你。

拿到B公司的题目接口,题目整体结构不变,可是题型和难度的分类都比A公司多一点。题型有单选题,多选题,分析题,一般分解题,APP分解题。题型有简单,一般,困难,极难。

小C心想:我去,我要以哪个公司的题目分类作为标准。于是找到了小T

小C:数据如果做整合的话,可不可以将B公司的分析题,一般分解题,APP分解题变成我们的解答题,极难不要,都变成困难。因为现在没有定义一个标准,我不太好整合数据。

小T:那好吧,先按你说的去做。


自那以后,小T又找了两家公司合作,让小C整合数据。并且小T认为其中一家公司的方法(配方法,消元法,排除法)和能力(推理能力,分析能力,计算能力)数据也是很重要的维度,希望能做补充。

小C崩溃了,我一分钟几百万上下,竟然找我来导数据。每次还要去看数据的分类值应该怎么做整合。还经常要加字段。现在因为要接入那两家公司的题库数据,要将表修改成

t_question表

字段 属性 描述
uid char(32) 唯一标示
body TEXT 正文
options JSON 题目选项
answer TEXT 答案
type int(11) 题目类型,1单选、2多选、3判断、4填空、5解答
difficult int(11) 题目难度,1简单、2一般、3困难
ability int(11) 能力属性,1推理能力,2分析能力 。。。。
method int(11) 方法属性,1配方法,2消元法,3排除法。。。
  1. 现在题库有300W数据,未来还会不断地增加,如果频繁改表的话,线上会直接锁表
  2. 如果每个分类我还要去看哪些值应该映射为我们定义的哪些值,后面肯定会吃不消的,因为我们没有一套统一的标准。。。

小C意识到自己跳到了一个大坑中,原来这东西并没有一开始想的这么简单。

经过仔细的思考,小C得出结论:

  1. 行业内根本就没有一套标准,必须针对变化点做扩展
  2. 不同的公司题目的维度数据不一样(如某公司多了能力与方法两个维度)
  3. 不同的公司同一维度的数据值不一样(如B公司的题型和A公司不一样)

专门针对标签建立表

  • 一个标签分类下有多个标签值
  • 一道习题有多个标签属性

t_tag表

字段 属性 描述
uid char(32) 唯一标示
tag_name varchar(64) 标签分类
tag_key varchar(64) 标签key

t_tag_value表

字段 属性 描述
uid char(32) 唯一标示
tag_id char(32) 标签分类id
value_name varchar(64) 标签值名
value_code varchar(64) 标签值编码

t_question_tag表

字段 属性 描述
id char(32) 唯一标示
tag_value_id char(32) 标签值id
question_id char(32) 题目id

当一次查询时,先将查询到的题目id到t_question_tag表中查出tag_value_id集合。 标签进行GROUP BY tag_id聚合后得到以下json

[{
分类名:难度, 
分类key: difficult,
值列表:[{
    值名:简单, 
    值编码: 1
},{
    值名:一般, 
    值编码: 2
},{
    值名:困难, 
    值编码: 3
}]}
]

通过返回标签聚合,可以在前端展示

难度:简单,一般,困难
题型:选择题,判断题,作文,完形填空。。。
方法:配方法,消元法。。。。

筛选时,传入标签key (difficult),标签value (1)得到tag_value_id 然后可以筛选出跟标签绑定的题目。

拓展总结:
  1. 当某张数据表未来可能数据量会很庞大的,不能因为需求变更频繁地增加表的字段,考虑增加中间表的方式来进行拓展
  2. 一般实体数据信息不确定的时候,也可以考虑使用NOSQL检索,如设计成以下的文档,就可以利用NOSQL的数组查询功能检索标签对应的实体。
{
    "uid":"",
    "description":"",
    "tag_ids":["标签1","标签2","标签3"]
}
  1. 该设计也能应用于电商中,如商品的分类筛选
颜色:黄色,蓝色,绿色
尺码:M,L,XL,XXL
风格:休闲,商务
  1. 标签只适合用于有限个数的分类,如文件大小,价格这些不固定的属性是不能做成标签的。
  2. 标签字段是不会有排序需求的,如按照某个分类进行order by。因为标签定位是有限分类,排序没有任何的意义,标签只能用来做筛选。如果一定要排序,建议另外计算标签和其他属性计算出一个分数字段。
问题点:
  1. 为什么标签值要分成标签uid和标签code呢

标签code属于多变的,可以自定义,如让简单定义为1,困难定义为2。如果直接让题目绑定1,很可能和其他的分类冲突。如题型的1为单选题。

  1. 为什么要前端传入key和value不直接传标签uid

会有一些场景需要业务自定义标签,如省市区,用户使用时更想用101100这种全国通用的地区编码来做标签筛选。

做到这里,小C稍微松了一口气,之后你来一个公司的数据,如果有新的分类,就可以加标签类别再加标签值。如果同一分类下来新的值,先看一下分类的中文名是不是对应的上,对应不上新建标签,然后让产品去做标签的整合或者就当成两个不同的标签来算。再也不怕整合数据了。