联想搜索实现方案(java基于字典树算法的实现方式)
1、背景
最近我的自动化测试平台(PostGirl)上有一个小需求:
用户在知识库的搜索框输入关键字,下方自动显示出以该关键字开头的词汇。实现效果类似百度的联想搜索(见下图)。
2、方案一
开始我的实现思路是使用redis的zset来实现。通过zadd添加元素。搜索的时候使用zrank获取到关键字的位置,然后通过zrange 得到所有以关键字开头的词汇,最后进行展示。
核心代码如下:
// 1、将关键字存储到redis中,用于后续输入联想功能jedis.zadd("search",0,word.getName());// 2、查询出关键字处于有序集合的位置Long wordIndex = resource.zrank("search", keyWord);
// 3、获取指定起始位置到末尾的所有值 Set<String> results = resource.zrange("search", wordIndex, -1L);for (String result: results) {if (result.startsWith(keyWord)) {reustlts.add(result); // 这个里面存储了所有以指定关键字开头的词汇。}}
但是,使用redis实现有个缺点,在zset中获取以某个字开头的位置很好确定,但是结尾不好确定,容易把不相关的内容搜索出来,感觉不怎么好处理。
方案二
在内存中维护一颗字典树,每次插入关键字后将字典树序列化为json字符保存到数据库。同时更新字典树对象。当重启的时候,将数据库中的json字符串查询出来然后实例化为对象(这一步在启动时加载效果更好)。
代码实现如下:
package com.cz.commons.search;import com.cz.commons.holder.Holder;
import com.google.gson.Gson;import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** 字典树,用于搜索联想* @program: PostGirl-panent* @description: Trie* @author: Cheng Zhi* @create: 2021-07-10 12:36**/
public class Trie implements Serializable {public static Trie trie;public static TrieNode root ;public Trie() {if (root == null) {root = new TrieNode();}}public static Trie getTrie() {if (trie == null) {trie = new Trie();}return trie;}static class TrieNode {// 标识是否为终节点Boolean isEnd;// 多少个路线通过该节点Integer num;// 保存所有儿子节点Map<String,TrieNode> sonMap;// 标记是否存在儿子节点Boolean hasSon;TrieNode() {isEnd = false;hasSon = false;num = 1;sonMap = new HashMap<String, TrieNode>();}}/*** 插入字典树* @param sentence*/public void insert(String sentence) {TrieNode node = root;for (int i=0; i<sentence.length(); i++) {String word = String.valueOf(sentence.charAt(i));// 如果树中不存在该字if (!node.sonMap.containsKey(word)) {node.sonMap.put(word,new TrieNode());node.hasSon = true;} else {node.num ++;}node = node.sonMap.get(word);}node.isEnd = true;}/*** 在树中查找* @param sentence* @return*/public boolean search(String sentence) {TrieNode node = root;for (int i=0; i<sentence.length(); i++) {String word = String.valueOf(sentence.charAt(i));if (node.sonMap.containsKey(word)) {node = node.sonMap.get(word);} else {return false;}}return node.isEnd;}/*** 获取所有以入参关键字开头的词汇* @param sentence* @return*/public List<String> searchList(String sentence) {List<String> resultList = new ArrayList<>();TrieNode node = root;StringBuilder prefix = new StringBuilder();String[] strings = new String[20];int index = 0;Holder<Integer> holder = new Holder<>(index);for (int i=0; i<sentence.length(); i++) {String word = String.valueOf(sentence.charAt(i));// 如果树中不包含关键字,直接返回if (!node.sonMap.containsKey(word)) {return resultList;} else {node = node.sonMap.get(word);}prefix.append(word);// 判断如果是一句话中的最后一个字符if (i == sentence.length() -1) {final String finalSb = prefix.toString();recursion(node, resultList, holder, prefix.toString());}}return resultList;}/*** 递归获取字典树的树枝。* @param node* @return*/private String recursion(TrieNode node, List<String> strings, Holder<Integer> holder, String sb) {String prefix = sb;if (node.hasSon) {for (String key : node.sonMap.keySet()) {if (node.sonMap.get(key).isEnd) {//strings[holder.getValue()] = sb+key;strings.add(sb+key);}holder.setValue(holder.getValue() + 1);}for (String key : node.sonMap.keySet()) {holder.setValue(holder.getValue() + 1);recursion(node.sonMap.get(key),strings,holder, sb+key);}}return sb.toString();}/*** 为Trie内部类TrieNode初始化值* @param jsonObject* @return*/public Trie setTrieNode(String jsonObject) {TrieNode trieNode = mapToObject(jsonToMap(jsonObject));Trie trie = getTrie();trie.root = trieNode;return trie;}/*** 将json转化为Map* @param jsonStr* @return*/public Map<?, ?> jsonToMap(String jsonStr) {Map<?, ?> ObjectMap = null;Gson gson = new Gson();java.lang.reflect.Type type = new com.google.gson.reflect.TypeToken<Map<?,?>>() {}.getType();ObjectMap = gson.fromJson(jsonStr, type);return ObjectMap;}/*** 将map转化为java对象* @param map* @return*/public TrieNode mapToObject(Map<?, ?> map) {TrieNode trieNode = new TrieNode();Double doubleNum = (Double) map.get("num");trieNode.num = doubleNum.intValue();;trieNode.isEnd = (Boolean) map.get("isEnd");trieNode.hasSon = (Boolean) map.get("hasSon");Map<?,?> sonMap = (Map<?, ?>) map.get("sonMap");Map<String, TrieNode> newMap = new HashMap<>();for (Object key : sonMap.keySet()) {newMap.put((String) key, mapToObject((Map<?, ?>) sonMap.get(key)));}trieNode.sonMap = newMap;return trieNode;}public static void main(String[] args) {Trie chTrie = new Trie();Gson gson = new Gson();String str = "这是一坨,这是,这是一箱,测试,这是个好孩子,这测试,这是行,这是好,这是啥,这是朱,这是猪啊啊啊啊啊啊啊啊";String[] splits = str.split(",");for (String split : splits) {chTrie.insert(split);}String strs = "这是";System.out.println(chTrie.searchList(strs));}
}
效果
思考
这样做,数据量小貌似没有问题,但是,如果数据量相当大的话,那么这颗树将会很大,使用数据库操作必定会很慢。还得再想想怎么搞。。。
补充
好多朋友询问Holder是什么?holder是一个用来做数据传递的载体,可以理解为是一个容器。
下面是源码:
package pers.cz.commons.holder;/*** @program: netDisc 使用引用传递的方式实现不可变类的传递:例如int类型的传递* @description: Holder* @author: Cheng Zhi* @create: 2020-12-05 13:40**/
public class Holder<T> implements IHolder {private T value;private IHolder pipeHolder;public T getValue() {return value;}public void setValue(T value) {this.value = value;}public Holder(T value) {this.value = value;}public Holder() {}@Overridepublic String toString() {return "Holder{" +"value=" + value +", pipeHolder=" + pipeHolder +'}';}@Overridepublic Object get() {return null;}@Overridepublic void set(Object obj) {}/*** 测试方法* @param args*/public static void main(String[] args) {// doing();String name = "cz";Holder holder = new Holder(name);change(holder);System.out.println(holder.getValue());Integer a = 5;Holder holder1 = new Holder(a);changeInt(holder1);System.out.println(holder1.getValue());}private static void changeInt(Holder holder) {Integer a = (Integer) holder.getValue();holder.setValue( a+1);}private static void change(Holder holder) {holder.setValue("cz666");}
}
package pers.cz.commons.holder;import java.io.Serializable;public interface IHolder extends Serializable {public abstract Object get();public abstract void set(Object obj);}
联想搜索实现方案(java基于字典树算法的实现方式)相关推荐
- php 实现联想式 搜索,PHP实现搜索联想功能(基于字典树算法)
搜索联想功能是各大搜索引擎具备的基础功能,如下图所示,这个功能简化了用户的输入行为,并且能够给用户推荐热门的搜索词,下面我们来讲一下如何用php实现搜索联想的功能. 实现原理 搜索联想功能拆解一下由两 ...
- es keyword和text的区别以及联想词实现方案
keyword和text的区别 相同 支持模糊查询和精确查询 不同 分词 keyword 不分词 text 分词 聚合查询 keyword 支持 text 不支持 联想词实现方案 内容 基于es的ke ...
- 基于java基于javaweb的管理系统设计与实现怎样选题思路分享
首先简单介绍一下自己,,MY IS一名勤奋的程序员JAVA 开发工程师,目前在一家 IT 互联网公司,主要负责公司搬砖的后端开发工作,除了我,团队里都是多年开发经验的老师,累积丰富开发经验,擅长JAV ...
- Elasticsearch5.3.1 IK分词,同义词/联想搜索设置
本文主要是记录Elasticsearch5.3.1 IK分词,同义词/联想搜索设置,本来是要写fscrawler的多种格式(html,pdf,word...)数据导入的,但是IK分词和同义词配置还是折 ...
- java基于Springboot+vue的文体文具销售商城网站 elementui
在现实生活中,不论是学生还是工作人员,都需要对你的文具进行书写完成相应的学业以及工作.传统的文具购买方式都是到文具店购买.是到文具店购买对应的文具也有一定的弊端,一方面是文具店,因为成本的原因商品并没 ...
- 路由选路java,java基于蚁群算法路由选择可视化动态模拟-开题报告
<java基于蚁群算法路由选择可视化动态模拟-开题报告>由会员分享,可在线阅读,更多相关<java基于蚁群算法路由选择可视化动态模拟-开题报告(3页珍藏版)>请在人人文库网上搜 ...
- atitit.跨架构 bs cs解决方案. 自定义web服务器的实现方案 java .net jetty HttpListener...
atitit.跨架构 bs cs解决方案. 自定义web服务器的实现方案 java .net jetty HttpListener 1. 自定义web服务器的实现方案,基于原始socket vs ...
- Java基于opencv实现图像数字识别(一),java开发面试笔试题
我总结出了很多互联网公司的面试题及答案,并整理成了文档,以及各种学习的进阶学习资料,免费分享给大家. 扫描二维码或搜索下图红色VX号,加VX好友,拉你进[程序员面试学习交流群]免费领取.也欢迎各位一起 ...
- geohash美团附近酒店搜索-技术方案
美团附近酒店搜索-技术方案 自Redis 3.2开始,Redis基于geohash和zset提供了地理位置相关功能. 什么是Geohash Geohash是一种地址编码,它能把二维的经纬度编码成一维的 ...
最新文章
- linux运维/自动化开发__目录
- Js 对小数的处理(科学计数法 , 显示精度)
- libuv / 编译 libuv 1.30 过程说明(静态库)
- Boost:ping的测试程序
- tf.placeholder函数的用法
- springBoot跨域解决
- respond java 使用_java – 使用android问题的HttpResponse:执行总...
- Windows 7 SP1意味着XP降级的结束
- 中文搜索引擎技术揭密-网络蜘蛛
- php对联广告代码,网站漂浮对联广告代码
- python 数学公式显示_python 数学公式·
- bootstrap日期插件daterangepicker的使用
- 为了自己的梦想而努力
- 自学云计算进度(2)-Linux基础命令
- 浏览器显示“您与此网站之间建立的连接不安全”的解决方案
- 第14节 三个败家子(14)——在辉煌中走向深渊
- Android存储子系统流程--vold
- 【Android归纳】基于XListView的下拉刷新、上拉加载更多的控件分析
- PHP/Golang实现—数据结构之顺序栈
- @程序员,你需要点金融常识
热门文章
- zzuli 1787: 生化危机 (BFS)
- 基于ymir2.0版本搭建本地化调试的环境
- 居家隔离没事干?进来秒变算法工程师
- 关于java开发mc模组出现的问题
- ACWing算法提高课 友好城市
- TransRepair:自动测试及修复神经网络翻译模型的不一致性问题
- 关于理财年轻人正在跑步入场
- 【多输入模型 Multiple-Dimension 数学原理分析以及源码详解 深度学习 Pytorch笔记 B站刘二大人 (6/10)】
- [转]数据分析与处理之二:Leveldb 实现原理
- 【续】DeepMind项目取得新进展:一种改进机器人学习系统的新思路