需求
打标是个很常见的运营需求。一开始,往往会被简单的实现为数组,之后就是各种硬编码的补丁逻辑。概括一些比较关键的运营需求:
- 标签分多个维度,如,性别和年龄属于不同维度。
- 标签分层级:
篮球版块包含NBA,CBA... - 不同场景,不同的查询规则。
- 反选能力:
可以选择儿童,仅儿童可见,也可选择非儿童,仅儿童不可见。 - 按规则打标(按规则将用户属性映射到一个标签集合内):
年龄 | 标签 |
---|---|
0-1 | 婴儿 |
2-12 | 儿童 |
... | ... |
数据结构
因为标签分维度,分层级。自然会想到用树来存储多级标签。那么如何建索引会是个问题。这里可以参考mongodb官方文档的指引,用数组实现树结构:
https://www.mongodb.com/docs/manual/tutorial/model-tree-struc...
这个范式可以应用到其它db,如elasticsearch。
mongodb 打标结构
db.xx.insert({
"tag_attr": {
"tag_type1": {
"select": true
},
"tag_type2": {
"select": false
}
},
"tag_tree": [
"tag_type1,tag_value1,",
"tag_type1,tag_value2,",
"tag_type1,tag_value3,",
"tag_type2,tag_value1,",
]
})
如下图所示,tag_type1 维度的 tag_value1,2,3被勾选,tag_type2维度的tag_value1被反向勾选:
标签搜索
mongodb 查询 example
构造数据,分别为
- 无标签
- tag_type1下的tag_value1
- tag_type1下的tag_value2
- tag_type1下的tag_value1和tag_value2
非tag_type1下的tag_value2
db.xx.insertMany([ {"tag_attr": {}, "tag_tree": []}, {"tag_attr": {"tag_type1": {"select": true}}, "tag_tree": ["tag_type1,tag_value1,"]}, {"tag_attr": {"tag_type1": {"select": true}}, "tag_tree": ["tag_type1,tag_value2,"]}, {"tag_attr": {"tag_type1": {"select": true}}, "tag_tree": ["tag_type1,tag_value1,", "tag_type1,tag_value2,"]}, {"tag_attr": {"tag_type1": {"select": false}}, "tag_tree": ["tag_type1,tag_value2,"]}, ])
查询所有tag_type1
test> db.xx.find({"tag_attr": {"tag_type1": {"select": true}}, "tag_tree": {"$regex": "^tag_type1,"}}) [ { _id: ObjectId("651b7ce750d1bcc01bd06334"), tag_attr: { tag_type1: { select: true } }, tag_tree: [ 'tag_type1,tag_value1,' ] }, { _id: ObjectId("651b7ce750d1bcc01bd06335"), tag_attr: { tag_type1: { select: true } }, tag_tree: [ 'tag_type1,tag_value2,' ] }, { _id: ObjectId("651b7ce750d1bcc01bd06336"), tag_attr: { tag_type1: { select: true } }, tag_tree: [ 'tag_type1,tag_value1,', 'tag_type1,tag_value2,' ] } ]
查询tag_type1下的tag_value2
test> db.xx.find({"tag_attr": {"tag_type1": {"select": true}}, "tag_tree": {"$regex": "^tag_type1,tag_value2,"}}) [ { _id: ObjectId("651b7ce750d1bcc01bd06335"), tag_attr: { tag_type1: { select: true } }, tag_tree: [ 'tag_type1,tag_value2,' ] }, { _id: ObjectId("651b7ce750d1bcc01bd06336"), tag_attr: { tag_type1: { select: true } }, tag_tree: [ 'tag_type1,tag_value1,', 'tag_type1,tag_value2,' ] } ]
查询非tag_type1,tag_value2的item
test> db.xx.find({"tag_attr": {"tag_type1": {"select": false}}, "tag_tree": {"$regex": "^tag_type1,tag_value2,"}}) [ { _id: ObjectId("651b7e5850d1bcc01bd06338"), tag_attr: { tag_type1: { select: false } }, tag_tree: [ 'tag_type1,tag_value2,' ] } ]
假设tag_type1,tag_value2为成人标签。那么,儿童用非成人查询条件搜索(查询非tag_type1,tag_value2),而成人可以搜索全年龄段内容(查询所有tag_type1)。
实际的需求中,查询条件会更为复杂。可以将搜索代码组合使用。可以根据不同的场景,将上面的搜索语句做组合,达到复用目的。
用户看到的内容,不全部由用户控制,还可以由运营调节规则,根据用户的属性生成用户标签。如,年龄、性别、信用分数等等。总结
- 用树结构实现多维度,分层级标签。
- 用数组实现树结构。
- 按标签维度、场景组合查询策略,一般不同标签维度之间的关系为与,同维度标签之间的关系为或,尽量将查询策略配置化。
- 按维度配置标签映射规则(根据用户属性生成用户标签)。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。