《Redis应用实例》书摘(25):自动补全

自动补全经常出现在搜索引擎或者应用的搜索框中,比如当我们在搜索引擎的搜索框中输入字母“re”的时候,搜索引擎就会通过自动补全提示我们是否要选择“reddit”、“redis”、“react”或者“reuters”等建议的其中一个,而在输入“redis”的时候,搜索引擎则会提示“redis命令”、“redisson”、“redisinsight”或者“redis desktop manager”等建议以供选择。

代码清单 CODE_AUTO_COMPLETE 展示了自动补全程序的具体实现代码。


代码清单 CODE_AUTO_COMPLETE 自动补全程序 auto_complete.py

DEFAULT_WEIGHT = 1.0
DEFAULT_NUM = 10

def make_ac_key(subject, segment):
    """
    基于给定的主题和输入片段,构建保存建议项的建议表。
    """
    return "AutoComplete:{0}:{1}".format(subject, segment)

def create_segments(content):
    """
    根据输入的字符串为其创建片段。
    例子:对于输入"abc",将创建输出["a", "ab", "abc"]
    """
    return [content[:i] for i in range(1, len(content)+1)]

class AutoComplete:

    def __init__(self, client, subject):
        self.client = client
        self.subject = subject

    def feed(self, content, weight=DEFAULT_WEIGHT):
        """
        根据输入内容构建自动补全建议表。
        可选的weight参数用于指定需要增加的输入权重。
        """
        tx = self.client.pipeline()
        # 将输入分解为片段,然后对其相应的建议表执行操作
        for seg in create_segments(content):
            # 构建建议表键名
            key = make_ac_key(self.subject, seg)
            # 更新输入在该表中的权重
            tx.zincrby(key, weight, content)
        tx.execute()

    def hint(self, segment, num=DEFAULT_NUM):
        """
        根据给定的片段返回指定数量的补全建议,各个建议项之间按权重从高到低排列。
        """
        # 构建建议表键名
        key = make_ac_key(self.subject, segment)
        # 获取补全建议
        return self.client.zrange(key, 0, num-1, desc=True)

    def set(self, content, weight):
        """
        为输入内容设置指定的权重。
        """
        tx = self.client.pipeline()
        for seg in create_segments(content):
            key = make_ac_key(self.subject, seg)
            # 直接设置权重
            tx.zadd(key, {content: weight})
        tx.execute()

作为示例,以下代码演示了如何使用这个自动补全程序:

>>> from redis import Redis
>>> from auto_complete import AutoComplete
>>> client = Redis(decode_responses=True)
>>> ac = AutoComplete(client, "search")
>>> for i in range(5):  # 模拟用户输入
...   ac.feed("banana")
...
>>> for i in range(2):
...   ac.feed("banquet")
...
>>> for i in range(3):
...   ac.feed("band")
...
>>> ac.hint("ban")  # 模拟用户获取建议
['banana', 'band', 'banquet']

这段代码通过多次调用feed()来模拟用户输入多个单词:其中"banana"输入了五次,"band"输入了三次,而"banquet"只输入了两次,所以在针对输入"ban"获取建议的时候,自动补完程序将按照输入次数从多到少分别获取上述三个单词作为建议项。

Tip

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