最近在做特征平台的改造,需要接入新的特征类别。一个特征可能同时属于多个类别,且平台要支持多种类别同时查询,原有的设计结构无法直接扩展,存储和查询需要做兼容改造。通过位运算的特性进行了设计优化,特此记录。
问题描述
现有的特征平台支持两类特征:在线特征、离线特征。
创建新的特征时,可以设置特征类别:
- 情形一:勾选在线特征
- 情形二:勾选离线特征
- 情形三:同时勾选在线特征、离线特征
即一个特征可能属于在线特征,或者属于离线特征,或者同时属于在线和离线特征。
由于数据库中特征类别的字段为整形数字,而非数组,存储现状是:
- 在线特征:标识1
- 离线特征:标识2
- 在线+离线:标识3(同时勾选在线、离线,3=1+2)
筛选查询在线特征时,sql查询条件如下:
1 | where feature_mode in (1,3) |
筛选查询在线特征、离线特征时,sql查询条件如下:
1 | where feature_mode in (1,2,3) |
标识3作为固定条件统一加入查询范围,因为3可以代表全集。
现在需要新加入一种新的特征类别:分单特征。
创建新的特征时,会出现以下情况:
| 在线特征 | 离线特征 | 分单特征 | |
|---|---|---|---|
| 情形1 | ✔ | ||
| 情形2 | ✔ | ||
| 情形3 | ✔ | ||
| 情形4 | ✔ | ✔ | |
| 情形5 | ✔ | ✔ | |
| 情形6 | ✔ | ✔ | |
| 情形7 | ✔ | ✔ | ✔ |
$$
C_3^1+C_3^2+C_3^3=2^3-1
$$
按照原有的设计,分单特征标识为4,数据库存储如下:
- 在线特征:标识1
- 离线特征:标识2
- 在线+离线:标识3(同时勾选在线、离线,3=1+2)
- 分单特征:标识4
- 在线+分单:标识5(5=1+4)
- 离线+分单:标识6(6=2+4)
- 在线+离线+分单:标识7(7=1+2+4)
查询分单特征时,sql查询条件如下:
1 | where feature_mode in (4,3) |
会筛选出在线和离线的特征,原有的固定拼接条件已不再适用。
方案设计
数据存储
多维特征的存储仍然使用求和的方式,用二进制表示如下:
| 在线特征 | 离线特征 | 分单特征 | 十进制 | 二进制 | |
|---|---|---|---|---|---|
| 情形1 | ✔ | 1 | 1 | ||
| 情形2 | ✔ | 2 | 10 | ||
| 情形3 | ✔ | 4 | 100 | ||
| 情形4 | ✔ | ✔ | 3 | 11 | |
| 情形5 | ✔ | ✔ | 5 | 101 | |
| 情形6 | ✔ | ✔ | 6 | 110 | |
| 情形7 | ✔ | ✔ | ✔ | 7 | 111 |
数据查询
观察到,在线特征可以表示为二进制的第一位(最低位),离线特征可以表示为第二位,分单特征可以表示为第三位。
于是,sql查询条件可以设计:
- 查在线特征
1 | where feature_mode & 1 = 1 |
匹配十进制1、3、5、7,覆盖情形1、情形4、情形5、情形7。
- 查在线和分单特征
1 | where ((feature_mode & 1) = 1) OR ((feature_mode & 4) = 4) |
匹配十进制1、3、4、5、6、7,覆盖情形1、情形3、情形4、情形5、情形6、情形7。
总结
通过二进制存储+位运算查询的方式,对于N种特征类别,可以进行2^n-1种组合查询。