自动补全 P109
自动补全在日常业务中随处可见,应该算一种最常见最通用的功能。实际业务场景肯定要包括包含子串的情况,其实这在一定程度上转换成了搜索功能,即包含某个子串的串,且优先展示前缀匹配的串。如果仅包含前缀,那么可以使用 Trie
树,但在包含其他的情况下,使用数据库/ ES
本身自带查询就足够了。可以按照四种情况(精确匹配、前缀、后缀、包含(也可将后两种融合成包含)),分别查询结果,直至达到数据条数上限或者全部查询完毕。但这种使用方法有缺点:查询次数多、难以分页。不过实际场景中需要补全的情况都只要第一页的数据即可。
自动补全最近联系人 P110
需求: 记录最近联系过的 100 个人名,并支持对输入的串进行自动补全。 P110
数据量很小,所以可以在 Redis
中用列表维护最近联系人,然后在内存中进行过滤可自动补全的串。
步骤: P111
- 维护长度为 100 的最近联系人列表
- 如果指定的联系人已在列表中,则从列表中移除 (
LREM
) - 将指定的联系人添加到列表最前面 (
LPUSH
) - 如果添加完成后,列表长度超过 100 ,则对列表进行修剪,仅保留列表 前面的 100 个联系人 (
LTRIM
)
- 如果指定的联系人已在列表中,则从列表中移除 (
- 获取整个最近联系人列表,在内存中根据四种情况进行过滤即可
通讯录自动补全 P112
需求: 有很多通讯录,每个通讯录中有几千个人(仅包含小写英文字母),尽量减少 Redis
传输给客户端的数据量,实现前缀自动补全。 P112
思路: 使用有序集合存储人名,利用有序集合的特性:当成员的分值相同时,将根据成员字符串的二进制顺序进行排序。如果要查找 abc
前缀的字符串,那么实际上就是查找介于 abbz...
之后和 abd
之前的字符串。所以问题转化为:如何找到第一个排在 abc
之前的元素的排名 和 第一个排在 abd
之前的元素的排名。我们可以构造两个不在有序集合中的字符串 (abb{
, abc{
) 辅助定位,因为 {
是排在 z
后第一个不适用的字符,这样可以保证这两个字符串不存在与有序集合中,且满足转化后的问题的限制。 P113
综上: 通过将给定前缀的最后一个字符替换为第一个排在该字符前的字符,再再在末尾拼接上左花括号,可以得到前缀的前驱 (predecessor) ,通过给前缀的末尾拼接上左花括号,可以得到前缀的后继 (successor) 。
- 字符集:当处理的字符不仅仅限于
a~z
范围,那么要处理好以下三个问题:P113
- 将所有字符转换为字节:使用
UTF-8
、UTF-16
或者UTF-32
字符编码(注意:UTF-16
和UTF-32
只有大端版本可用于上述方法) - 找出需要支持的字符范围,确保所选范围的前面和后面至少留有一个字符
- 使用位于范围前后的字符分别代替反引号
`
和左花括号{
- 将所有字符转换为字节:使用
步骤: P114
- 运用思路中的方法找到前缀的前驱和后继(为了防止同时查询相同的前缀出现错误,可以在前驱和后继之后添加上
UUID
) - 将前驱和后继插入到有序集合里
- 查看前驱和后继的排名
- 取出他们之间的元素
- 从有序集合中删除前驱和后继
通过向有序集合添加元素来创建查找范围,并在取得范围内的元素之后移除之前添加的元素,这种技术还可以应用在任何已排序索引 (sorted index) 上,并且能通过改善(第七章介绍)应用于几种不同类型