当前位置: 首页 > article >正文

Redis篇--应用篇4--自动提示,自动补全

1、概述

Redis本身并不直接提供自动补全(Auto-Complete)功能,但可以通过合理的数据结构和算法设计实现自动补全的功能。常见的实现方式是使用有序集合(Sorted Set)或前缀树(Trie)结合键值对(String)来存储和查询前缀匹配的词项。

2、方案概述

(1)、数据结构选择

  • 词项存储:使用Redis的Sorted Set来存储所有词项及其权重,以便根据权重排序。
  • 前缀索引:使用Redis的Set类型来存储每个前缀对应的词项列表。

(2)、关键操作

  • 添加词项:将词项插入到Sorted Set中,并为每个前缀创建索引。
  • 查询前缀:根据用户输入的前缀,查找所有匹配的词项,并按权重排序返回结果。

(3)、逻辑梳理

1、添加词项

每次添加一个新词项时,我们需要将其插入到Sorted Set中,并为该词项的所有前缀创建索引。假设我们有一个词项 “apple”,它的权重为 5,我们将执行以下操作:
第一步:插入词项到Sorted Set:

ZADD autocomplete:items 5 "apple"

第二步:拆分词项的前缀,存储前缀到词项的Set集合中

- 前缀"a"SADD autocomplete:index:a apple

- 前缀"ap"SADD autocomplete:index:ap apple

- 前缀"app"SADD autocomplete:index:app apple

- 前缀"appl"SADD autocomplete:index:appl apple

- 前缀"apple"SADD autocomplete:index:apple apple
2、查询前缀

当用户输入一个前缀时,我们可以根据该前缀查找所有匹配的词项,并从Sorted Set中获取它们的权重,按权重排序返回结果。假设用户输入了前缀"app",我们将执行以下操作:
第一步:查找匹配的词项

  SMEMBERS autocomplete:index:app

如:以"app"开头的词项,此处返回的Set集合为[“apple”, “application”]。

第二步:获取词项的权重并排序

 ZREVRANGE autocomplete:items 0 -1 WITHSCORES BYSCORE LIMIT 0 10

如:返回 Sorted Set 中的前 10 个词项及其权重,按权重从高到低排序。

3、更新词项权重

为了实现动态更新词项的权重(例如根据用户的点击次数或搜索频率),我们可以使用ZINCRBY命令来增加某个词项的权重。假设我们要将词项"apple"的权重增加 1:

ZINCRBY autocomplete:items 1 "apple"
4、删除词项

如果需要删除某个词项,我们需要从Sorted Set中移除它,并删除所有与该词项相关的前缀索引。假设我们要删除词项"apple":
第一步:Sorted Set 中移除词项:

 ZREM autocomplete:items "apple"

第二步:删除所有前缀索引

- 前缀 "a"SREM autocomplete:index:a apple
- 前缀 "ap"SREM autocomplete:index:ap apple
- 前缀 "app"SREM autocomplete:index:app apple
- 前缀 "appl"SREM autocomplete:index:appl apple
- 前缀 "apple"SREM autocomplete:index:apple apple

3、代码示例

(1)、操作实现类

RedisTemplate配置和序列化注入就不赘述了。
第一:存储目标词项到Zset中。拆分词条成多个索引前缀,创建索引和词条的映射关系,通过Set存储。
第二:根据前缀查询符合预期的词项Set列表,回归到Zset中根据权重重新排序。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Set;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class AutocompleteService {

    private static final String ITEMS_KEY = "autocomplete:items";           // 词条存储(ZSet存储,可以按照分值排序)
    private static final String INDEX_PREFIX = "autocomplete:index:";         // 索引存储(Set存储,存储所有词条索引和词条的关系)

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    // 添加词项
    public void addTerm(String term, double score) {
        redisTemplate.opsForZSet().add(ITEMS_KEY, term, score);
        for (int i = 1; i <= term.length(); i++) {
            String prefix = term.substring(0, i);
            redisTemplate.opsForSet().add(INDEX_PREFIX + prefix, term);
        }
    }

    // 查询前缀
    public List<String> searchByPrefix(String prefix, int limit) {
        Set<Object> terms = redisTemplate.opsForSet().members(INDEX_PREFIX + prefix);
        if (terms == null || terms.isEmpty()) {
            return new ArrayList<>();
        }

        // 获取词项的权重并排序
        List<String> sortedTerms = redisTemplate.opsForZSet()
                .reverseRangeByScore(ITEMS_KEY, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, 0, limit)
                .stream()
                .filter(terms::contains)
                .map(Object::toString)
                .collect(Collectors.toList());

        return sortedTerms;
    }

    // 更新词项权重
    public void updateTermScore(String term, double increment) {
        redisTemplate.opsForZSet().incrementScore(ITEMS_KEY, term, increment);
    }

    // 删除词项
    public void deleteTerm(String term) {
        redisTemplate.opsForZSet().remove(ITEMS_KEY, term);
        for (int i = 1; i <= term.length(); i++) {
            String prefix = term.substring(0, i);
            redisTemplate.opsForSet().remove(INDEX_PREFIX + prefix, term);
        }
    }
}

(2)、测试类

模拟操作:新增词条、根据输入前缀获取符合的所有词条。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/api/autocomplete")
public class AutoCompleteController {

    @Autowired
    private  AutocompleteService autoCompleteService;

    /**
     * 添加建议词
     * @param term 建议词
     * @param score 权重(可选,默认为 1)
     * @return 成功消息
     */
    @PostMapping("/add")
    public String addSuggestion(@RequestParam String term, @RequestParam(required = false, defaultValue = "1.0") double score) {
        autoCompleteService.addTerm(term, score);
        return "Suggestion added: " + term;
    }

    /**
     * 获取建议词
     * @param prefix 用户输入的前缀
     * @param limit 返回的建议词数量
     * @return 匹配的建议词列表
     */
    @GetMapping("/suggest")
    public List<String> getSuggestions(@RequestParam String prefix, @RequestParam(defaultValue = "5") int limit) {
       return autoCompleteService.searchByPrefix(prefix, limit);
    }
}

(3)、测试

第一步:添加词条
本例一共添加apple(权重1),ap11(权重1),ap22(权重2),application(权重2)
在这里插入图片描述
可以看到redis中正常存储了Zset的4个词项。
且每一个词项的所有前缀索引也设置了Set集合映射。
在这里插入图片描述

第二步:查询前缀,获取自动补全的词条
查询ap,可以看到能查询到所有匹配的词条,并且是按照权重排序的。
在这里插入图片描述
在查询app,符合的词条只有2个,也是按照权重排序的。
在这里插入图片描述

4、总结

通过合理使用Redis的有序集合和键值对,结合前缀索引和权重排序,可以实现一个自动补全词语的方案。这个方案通常建议用于小型应用,也可以通过分布式架构和Redis模块扩展到大规模应用场景。

5、其他方案实现

  • RediSearch:如果你需要更复杂的自动补全功能(如模糊匹配、分词、全文搜索等),可以考虑使用[RediSearch]模块。RediSearch提供了强大的搜索和自动补全功能,适合构建复杂的应用场景。
  • Elasticsearch:如果你已经在使用Elasticsearch,也可以考虑将自动补全功能集成到Elasticsearch中,利用其强大的全文搜索和自动补全功能。
  • Algolia:如果你不想自己管理基础设施,可以考虑使用Algolia这样的第三方服务,它专门为搜索和自动补全提供了高度优化的解决方案。

http://www.kler.cn/a/454249.html

相关文章:

  • 使 el-input 内部的内容紧贴左边
  • 国标GB28181-2022平台EasyGBS:安防监控中P2P的穿透方法
  • WPF 绘制过顶点的圆滑曲线(样条,贝塞尔)
  • C++、Python有哪些相同和不同
  • Java Stream流详解——串行版
  • 一键图片转3D模型,AI建模,一键把图片转三维模型,二维图片转3维模型,AI建模
  • Oracle怎么写存储过程的定时任务执行语句
  • 骁龙 8 至尊版:AI 手机的变革先锋
  • 青少年编程与数学 02-005 移动Web编程基础 02课题、视口与像素
  • QT--模型/视图
  • 如何使用 Django 框架创建简单的 Web 应用?
  • Android native+html5的混合开发
  • 我的 2024 年终总结
  • 设计模式の命令访问者迭代器模式
  • MIT实验笔记冲刺2 实验部分
  • 工作生活做事慢效率低原因及解决方案
  • C语言变量指针、指针变量、数组指针、指针数组
  • 流程图(二)利用python绘制网络图
  • shardingsphere分库分表项目实践5-自己用java写一个sql解析器+完整项目源码
  • 探秘“香水的 ChatGPT”:AI 开启嗅觉奇幻之旅!
  • 【自留】Unity VR入门
  • [原创](Modern C++)现代C++的第三方库的导入方式: 例如Visual Studio 2022导入GSL 4.1.0
  • python之打印、变量、格式化输出
  • SkyWalking服务监控、追踪、诊断可视化工具
  • Flink的Watermark水位线详解
  • 嵌入式学习-QT-Day09