《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'}

Tip

本文摘录自《Redis应用实例》一书。
欢迎访问书本主页以了解更多Redis使用案例:huangz.works/rediscookbook/
../_images/rediscookbook-banner.png