需求

打标是个很常见的运营需求。一开始,往往会被简单的实现为数组,之后就是各种硬编码的补丁逻辑。概括一些比较关键的运营需求:

  • 标签分多个维度,如,性别和年龄属于不同维度。
  • 标签分层级:
    篮球版块包含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被反向勾选:

graph TB
tag_type2_value1[tag_value1]
ITEM --> tag_type1
ITEM --> tag_type2
subgraph 标签维度1
tag_type1 --> tag_value1
tag_type1 --> tag_value2
tag_type1 --> tag_value3
end
subgraph 标签维度2
tag_type2 --不包括此节点--> tag_type2_value1
style tag_type2_value1 fill:#FFFFFF,stroke-width:1px
end

标签搜索

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)。
    实际的需求中,查询条件会更为复杂。可以将搜索代码组合使用。可以根据不同的场景,将上面的搜索语句做组合,达到复用目的。
    用户看到的内容,不全部由用户控制,还可以由运营调节规则,根据用户的属性生成用户标签。如,年龄、性别、信用分数等等。

    总结

  • 用树结构实现多维度,分层级标签。
  • 用数组实现树结构。
  • 按标签维度、场景组合查询策略,一般不同标签维度之间的关系为与,同维度标签之间的关系为或,尽量将查询策略配置化。
  • 按维度配置标签映射规则(根据用户属性生成用户标签)。

enjolras1205
77 声望9 粉丝

引用和评论

0 条评论