《Redis应用实例》书摘(24):标签系统¶
很多用户生成内容(UGC)的网站和应用都允许用户为他们提供的内容打标签。
举个例子,如果你在视频网站上传了一个Redis相关的视频,那么你可能会给这个视频加上“Redis”、“数据库”、“NoSQL”等标签,以便搜索这些标签的用户可以发现这个视频,而系统也可以基于标签将视频推荐给对这些标签感兴趣的用户。
代码清单 CODE_TAG 展示了基于集合实现的标签系统程序。
代码清单 CODE_TAG 标签系统程序 tag.py
def make_target_key(target):
"""
目标的标签集合,用于记录目标关联的所有标签。
"""
return "Tag:target:{}".format(target)
def make_tag_key(tag):
"""
标签的目标集合,用于记录带有该标签的所有目标。
"""
return "Tag:tag:{}".format(tag)
class Tag:
def __init__(self, client):
self.client = client
def add(self, target, tags):
"""
尝试为目标添加任意多个标签,并返回成功添加的标签数量作为结果。
"""
tx = self.client.pipeline()
# 将target添加至每个给定tag对应的集合中
for tag_key in map(make_tag_key, tags):
tx.sadd(tag_key, target)
# 将所有给定tag添加至target对应的集合中
target_key = make_target_key(target)
tx.sadd(target_key, *tags)
return tx.execute()[-1] # 返回成功添加的标签数量
def remove(self, target, tags):
"""
尝试移除目标带有的任意多个标签,并返回成功移除的标签数量作为结果。
"""
tx = self.client.pipeline()
# 从每个给定tag对应的集合中移除target
for tag_key in map(make_tag_key, tags):
tx.srem(tag_key, target)
# 从target对应的集合中移除所有给定tag
target_key = make_target_key(target)
tx.srem(target_key, *tags)
return tx.execute()[-1] # 返回成功移除的标签数量
def get_tags_by_target(self, target):
"""
获取目标的所有标签。
"""
target_key = make_target_key(target)
return self.client.smembers(target_key)
def get_target_by_tags(self, tags):
"""
根据给定的任意多个标签找出同时带有这些标签的目标。
"""
# 找出所有给定tag对应的集合,然后对它们执行交集运算
tag_keys = map(make_tag_key, tags)
return self.client.sinter(*tag_keys)
作为例子,以下代码演示了如何使用这个程序对Redis、MongoDB和MySQL这三个数据库添加标签,然后基于标签搜索对应的数据库:
>>> from redis import Redis
>>> from tag import Tag
>>> client = Redis(decode_responses=True)
>>> tag = Tag(client)
>>> tag.add("Redis", {"Redis", "NoSQL", "Database"}) # 为数据库添加标签
3
>>> tag.add("MongoDB", {"MongoDB", "NoSQL", "Database"})
3
>>> tag.add("MySQL", {"MySQL", "SQL", "Database"})
3
>>> tag.get_tags_by_target("Redis") # 根据数据库查找标签
{'Redis', 'Database', 'NoSQL'}
>>> tag.get_target_by_tags({"NoSQL"}) # 根据标签查找数据库
{'Redis', 'MongoDB'}
>>> tag.get_target_by_tags({"Database"})
{'Redis', 'MongoDB', 'MySQL'}
>>> tag.get_target_by_tags({"Database", "SQL"}) # 根据多个标签查找数据库
{'MySQL'}