您好,欢迎访问一九零五行业门户网

怎么使用java和redis实现一个简单的热搜功能

具备以下功能:
1:搜索栏展示当前登陆的个人用户的搜索历史记录,删除个人历史记录
2:用户在搜索栏输入某字符,则将该字符记录下来 以zset格式存储的redis中,记录该字符被搜索的个数以及当前的时间戳 (用了dfa算法,感兴趣的自己百度学习吧)
每当用户查询已经存在于 redis 中的字符时,就会累加计数,以便获取平台上最热门的十条查询数据。可以自己编写 api 或在 redis 中预先添加一些关键词
4:最后还要做不雅文字过滤功能。这个很重要不说了你懂的。
代码实现热搜与个人搜索记录功能,主要controller层下几个方法就行了 :
1:向redis 添加热搜词汇(添加的时候使用下面不雅文字过滤的方法来过滤下这个词汇,合法再去存储
2:每次点击给相关词热度 +1
3: 根据key搜索相关最热的前十名
4:插入个人搜索记录
5:查询个人搜索记录
首先配置好redis数据源等等基础 
最后贴上核心的 服务层的代码 :
package com.****.****.****.user; import com.jianlet.service.user.redisservice;import org.apache.commons.lang.stringutils;import org.springframework.data.redis.core.*;import org.springframework.stereotype.service;import javax.annotation.resource;import java.util.*;import java.util.concurrent.timeunit; /** * @author: mrwanghc * @date: 2020/5/13 * @description: */@transactional@service("redisservice")public class redisserviceimpl implements redisservice { //导入数据源 @resource(name = "redissearchtemplate") private stringredistemplate redissearchtemplate; //新增一条该userid用户在搜索栏的历史记录 //searchkey 代表输入的关键词 @override public int addsearchhistorybyuserid(string userid, string searchkey) { string shistory = rediskeyutils.getsearchhistorykey(userid); boolean b = redissearchtemplate.haskey(shistory); if (b) { object hk = redissearchtemplate.opsforhash().get(shistory, searchkey); if (hk != null) { return 1; }else{ redissearchtemplate.opsforhash().put(shistory, searchkey, "1"); } }else{ redissearchtemplate.opsforhash().put(shistory, searchkey, "1"); } return 1; } //删除个人历史数据 @override public long delsearchhistorybyuserid(string userid, string searchkey) { string shistory = rediskeyutils.getsearchhistorykey(userid); return redissearchtemplate.opsforhash().delete(shistory, searchkey); } //获取个人历史数据列表 @override public list<string> getsearchhistorybyuserid(string userid) { list<string> stringlist = null; string shistory = rediskeyutils.getsearchhistorykey(userid); boolean b = redissearchtemplate.haskey(shistory); if(b){ cursor<map.entry<object, object>> cursor = redissearchtemplate.opsforhash().scan(shistory, scanoptions.none); while (cursor.hasnext()) { map.entry<object, object> map = cursor.next(); string key = map.getkey().tostring(); stringlist.add(key); } return stringlist; } return null; } //新增一条热词搜索记录,将用户输入的热词存储下来 @override public int incrementscorebyuserid(string searchkey) { long now = system.currenttimemillis(); zsetoperations zsetoperations = redissearchtemplate.opsforzset(); valueoperations<string, string> valueoperations = redissearchtemplate.opsforvalue(); list<string> title = new arraylist<>(); title.add(searchkey); for (int i = 0, lengh = title.size(); i < lengh; i++) { string tle = title.get(i); try { if (zsetoperations.score("title", tle) <= 0) { zsetoperations.add("title", tle, 0); valueoperations.set(tle, string.valueof(now)); } } catch (exception e) { zsetoperations.add("title", tle, 0); valueoperations.set(tle, string.valueof(now)); } } return 1; } //根据searchkey搜索其相关最热的前十名 (如果searchkey为null空,则返回redis存储的前十最热词条) @override public list<string> gethotlist(string searchkey) { string key = searchkey; long now = system.currenttimemillis(); list<string> result = new arraylist<>(); zsetoperations zsetoperations = redissearchtemplate.opsforzset(); valueoperations<string, string> valueoperations = redissearchtemplate.opsforvalue(); set<string> value = zsetoperations.reverserangebyscore("title", 0, double.max_value); //key不为空的时候 推荐相关的最热前十名 if(stringutils.isnotempty(searchkey)){ for (string val : value) { if (stringutils.containsignorecase(val, key)) { if (result.size() > 9) {//只返回最热的前十名 break; } long time = long.valueof(valueoperations.get(val)); if ((now - time) < 2592000000l) {//返回最近一个月的数据 result.add(val); } else {//时间超过一个月没搜索就把这个词热度归0 zsetoperations.add("title", val, 0); } } } }else{ for (string val : value) { if (result.size() > 9) {//只返回最热的前十名 break; } long time = long.valueof(valueoperations.get(val)); if ((now - time) < 2592000000l) {//返回最近一个月的数据 result.add(val); } else {//时间超过一个月没搜索就把这个词热度归0 zsetoperations.add("title", val, 0); } } } return result; } //每次点击给相关词searchkey热度 +1 @override public int incrementscore(string searchkey) { string key = searchkey; long now = system.currenttimemillis(); zsetoperations zsetoperations = redissearchtemplate.opsforzset(); valueoperations<string, string> valueoperations = redissearchtemplate.opsforvalue(); zsetoperations.incrementscore("title", key, 1); valueoperations.getandset(key, string.valueof(now)); return 1; } }
核心的部分写完了,剩下的需要你自己将如上方法融入到你自己的代码中就行了。
代码实现过滤不雅文字功能,在springboot 里面写一个配置类加上@configuration注解,在项目启动的时候加载一下,代码如下:
package com.***.***.interceptor; import org.springframework.context.annotation.configuration;import org.springframework.core.io.classpathresource;import java.io.*;import java.util.hashmap;import java.util.hashset;import java.util.map;import java.util.set; //屏蔽敏感词初始化@configuration@suppresswarnings({ "rawtypes", "unchecked" })public class sensitivewordinit { // 字符编码 private string encoding = "utf-8"; // 初始化敏感字库 public map initkeyword() throws ioexception { // 读取敏感词库 ,存入set中 set<string> wordset = readsensitivewordfile(); // 将敏感词库加入到hashmap中//确定有穷自动机dfa return addsensitivewordtohashmap(wordset); } // 读取敏感词库 ,存入hashmap中 private set<string> readsensitivewordfile() throws ioexception { set<string> wordset = null; classpathresource classpathresource = new classpathresource("static/censorword.txt"); inputstream inputstream = classpathresource.getinputstream(); //敏感词库 try { // 读取文件输入流 inputstreamreader read = new inputstreamreader(inputstream, encoding); // 文件是否是文件 和 是否存在 wordset = new hashset<string>(); // stringbuffer sb = new stringbuffer(); // bufferedreader是包装类,先把字符读到缓存里,到缓存满了,再读入内存,提高了读的效率。 bufferedreader br = new bufferedreader(read); string txt = null; // 读取文件,将文件内容放入到set中 while ((txt = br.readline()) != null) { wordset.add(txt); } br.close(); // 关闭文件流 read.close(); } catch (exception e) { e.printstacktrace(); } return wordset; } // 将hashset中的敏感词,存入hashmap中 private map addsensitivewordtohashmap(set<string> wordset) { // 初始化敏感词容器,减少扩容操作 map wordmap = new hashmap(wordset.size()); for (string word : wordset) { map nowmap = wordmap; for (int i = 0; i < word.length(); i++) { // 转换成char型 char keychar = word.charat(i); // 获取 object tempmap = nowmap.get(keychar); // 如果存在该key,直接赋值 if (tempmap != null) { nowmap = (map) tempmap; } // 不存在则,则构建一个map,同时将isend设置为0,因为他不是最后一个 else { // 设置标志位 map<string, string> newmap = new hashmap<string, string>(); newmap.put("isend", "0"); // 添加到集合 nowmap.put(keychar, newmap); nowmap = newmap; } // 最后一个 if (i == word.length() - 1) { nowmap.put("isend", "1"); } } } return wordmap; }}
然后这是工具类代码 :
package com.***.***.interceptor; import java.io.ioexception;import java.util.hashset;import java.util.iterator;import java.util.map;import java.util.set; //敏感词过滤器:利用dfa算法 进行敏感词过滤public class sensitivefilter { //敏感词过滤器:利用dfa算法 进行敏感词过滤 private map sensitivewordmap = null; // 最小匹配规则 public static int minmatchtype = 1; // 最大匹配规则 public static int maxmatchtype = 2; // 单例 private static sensitivefilter instance = null; // 构造函数,初始化敏感词库 private sensitivefilter() throws ioexception { sensitivewordmap = new sensitivewordinit().initkeyword(); } // 获取单例 public static sensitivefilter getinstance() throws ioexception { if (null == instance) { instance = new sensitivefilter(); } return instance; } // 获取文字中的敏感词 public set<string> getsensitiveword(string txt, int matchtype) { set<string> sensitivewordlist = new hashset<string>(); for (int i = 0; i < txt.length(); i++) { // 判断是否包含敏感字符 int length = checksensitiveword(txt, i, matchtype); // 存在,加入list中 if (length > 0) { sensitivewordlist.add(txt.substring(i, i + length)); // 减1的原因,是因为for会自增 i = i + length - 1; } } return sensitivewordlist; } // 替换敏感字字符 public string replacesensitiveword(string txt, int matchtype, string replacechar) { string resulttxt = txt; // 获取所有的敏感词 set<string> set = getsensitiveword(txt, matchtype); iterator<string> iterator = set.iterator(); string word = null; string replacestring = null; while (iterator.hasnext()) { word = iterator.next(); replacestring = getreplacechars(replacechar, word.length()); resulttxt = resulttxt.replaceall(word, replacestring); } return resulttxt; } /** * 获取替换字符串 * * @param replacechar * @param length * @return */ private string getreplacechars(string replacechar, int length) { string resultreplace = replacechar; for (int i = 1; i < length; i++) { resultreplace += replacechar; } return resultreplace; } /** * 检查文字中是否包含敏感字符,检查规则如下:<br> * 如果存在,则返回敏感词字符的长度,不存在返回0 * @param txt * @param beginindex * @param matchtype * @return */ public int checksensitiveword(string txt, int beginindex, int matchtype) { // 敏感词结束标识位:用于敏感词只有1位的情况 boolean flag = false; // 匹配标识数默认为0 int matchflag = 0; map nowmap = sensitivewordmap; for (int i = beginindex; i < txt.length(); i++) { char word = txt.charat(i); // 获取指定key nowmap = (map) nowmap.get(word); // 存在,则判断是否为最后一个 if (nowmap != null) { // 找到相应key,匹配标识+1 matchflag++; // 如果为最后一个匹配规则,结束循环,返回匹配标识数 if ("1".equals(nowmap.get("isend"))) { // 结束标志位为true flag = true; // 最小规则,直接返回,最大规则还需继续查找 if (sensitivefilter.minmatchtype == matchtype) { break; } } } // 不存在,直接返回 else { break; } } if (sensitivefilter.maxmatchtype == matchtype){ if(matchflag < 2 || !flag){ //长度必须大于等于1,为词 matchflag = 0; } } if (sensitivefilter.minmatchtype == matchtype){ if(matchflag < 2 && !flag){ //长度必须大于等于1,为词 matchflag = 0; } } return matchflag; }}
在你代码的controller层直接调用方法判断即可:
//非法敏感词汇判断 sensitivefilter filter = sensitivefilter.getinstance(); int n = filter.checksensitiveword(searchkey,0,1); if(n > 0){ //存在非法字符 logger.info("这个人输入了非法字符--> {},不知道他到底要查什么~ userid--> {}",searchkey,userid); return null; }
也可将敏感文字替换*等字符 :
sensitivefilter filter = sensitivefilter.getinstance(); string text = "敏感文字"; string x = filter.replacesensitiveword(text, 1, "*");
最后刚才的 sensitivewordinit.java 里面用到了 censorword.text 文件,放到你项目里面的 resources 目录下的 static 目录中,这个文件就是不雅文字大全,也需要您与时俱进的更新,项目启动的时候会加载该文件。
以上就是怎么使用java和redis实现一个简单的热搜功能的详细内容。
其它类似信息

推荐信息