redis Autocomplete
- 面对 自动提示所需要阐述的问题:
- 需求:输入 abc 提示出 abc相关的数据?
- 采用数据库能否完成此项操作?
- 为什么要使用redis?
- redis 有几种实现方式? 分别是什么?
- 针对上面的问题做一一回答:
1)当我们访问一个网站,需要搜索的时候,打开搜索框后,假如 我们 输入:abc 该网站可能自动帮你补全一些信息,做个自动提示功能。
2)采用 数据库能否完成呢? 当然是可以的。
eg:需要 搜索 名称,代码,简称之类的。 通过 sql语句完成.
select * from tablename t where t.name like "%abc%" or t.code like "%abc%" ...
这种方式一定是可以的,只不过在时间上需要的时间长而已。 若我们做这种键盘精灵(快速提示相关内容),对时间要求肯定是比较高的,如果 项目小的话,可以考虑采用这种方式。
3)为什么采用redis?
redis也是一种存储结构。redis读取速度快,所以结合实际开发,做按键精灵在为合适不过了。
4)下面我们说说用redis 如何实现按键精灵。
假设内容是 a--z 之间的数据 ,输入 abc 能提示出 abbz ---abcz 之间的数据。使用redis zset的存储结构能够完美的得到相应的结果。其中redis中提过了一个方法与其对应:zrangebylex 。 那我们要如何做呢?先把数据存储到redis中,使用zset 结构存储,其中score的值都为0. (基于ASCII)
给定一些数据 abca abcd abcf abcu abau abfh abac .按照上述的搜索查找, 要想 输入abc ,会提示 abca,abcd,abcf,abcu.
其中的核心代码:KCCompanyStockService.java
package com.vikedata.redissearch.service;
import com.alibaba.fastjson.JSON;
import com.vikedata.redissearch.dao.KCCompanyStockDAO;
import com.vikedata.redissearch.entity.KCCompanyStock;
import com.vikedata.redissearch.reids.JedisCache;
import com.vikedata.redissearch.utils.ChineseToUtfUtil;
import com.vikedata.redissearch.utils.MyPageInfo;
import com.vikedata.redissearch.utils.ResponseContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @Auther: Zpjeck
* @Date: 2019/10/16 18:06
* @Description:
*/
@Service
public class KCCompanyStockService {
@Autowired
private JedisCache jedisCache;
@Autowired
private KCCompanyStockDAO kcCompanyStockDAO;
private static Logger logger = LoggerFactory.getLogger(KCCompanyStockService.class);
@Value("${spring.spirit.codeKey}")
private String codeKey;
@Value("${spring.spirit.pinyinKey}")
private String pinyinKey;
@Value("${spring.spirit.nameKey}")
private String nameKey;
@Value("${spring.spirit.all}")
private String all;
@Scheduled(cron = " 0 0 0 1/1 * ? ")
public void initData() {
logger.info("键盘精灵开始更新数据。。。");
List<KCCompanyStock> allCompanyStock = kcCompanyStockDAO.findAllCompanyStock();
for (KCCompanyStock kcCompanyStock : allCompanyStock) {
jedisCache.addZSet(codeKey, 0, kcCompanyStock.getSecuritycode());
jedisCache.addZSet(pinyinKey, 0, kcCompanyStock.getSecuritypinyin());
jedisCache.addZSet(nameKey, 0, ChineseToUtfUtil.convertStringToUTF8(kcCompanyStock.getSecurityshortname()));
String key1 = all + "_" + kcCompanyStock.getSecuritycode();
String key2 = all + "_" + kcCompanyStock.getSecuritypinyin();
String key3 = all + "_" + ChineseToUtfUtil.convertStringToUTF8(kcCompanyStock.getSecurityshortname());
jedisCache.setValue(key1, JSON.toJSONString(kcCompanyStock));
jedisCache.setValue(key2, JSON.toJSONString(kcCompanyStock));
jedisCache.setValue(key3, JSON.toJSONString(kcCompanyStock));
}
logger.info("键盘精灵更新数据 结束。。。");
}
public ResponseContext findByFont(Long pageNum, Long pageSize, String select) {
pageNum = pageNum == null ? 1L : pageNum;
pageSize = pageSize == null ? 100L : pageSize;
select = select == null ? "" : select;
select = select.toUpperCase();
Set<String> zsetBylex = null;
List<KCCompanyStock> list = new ArrayList<>();
MyPageInfo<KCCompanyStock> pageInfo = new MyPageInfo<KCCompanyStock>();
if (isInteger(select)) {
// 全为数字
String temp = "[" + select;
zsetBylex = jedisCache.getZsetBylex(codeKey, temp, temp + ":");
Set<String> zsetBylex1 = jedisCache.getZsetBylex(codeKey, temp, temp + ":", 0, pageSize.intValue());
for (String s : zsetBylex1) {
KCCompanyStock byParam = findByParam(s);
if (byParam !=null){
if (byParam.getTrademarketcode().startsWith("069001001")){
byParam.setStockCode("sh"+byParam.getSecuritycode());
}
if (byParam.getTrademarketcode().startsWith("069001002")){
byParam.setStockCode("sz"+byParam.getSecuritycode());
}
list.add(byParam);
}
}
}
if (select.matches("[a-zA-Z]+")) {
// 全都是英文字母
String temp = "[" + select;
zsetBylex = jedisCache.getZsetBylex(pinyinKey, temp, temp + "{");
Set<String> zsetBylex1 = jedisCache.getZsetBylex(pinyinKey, temp, temp + "{", 0, pageSize.intValue());
for (String s : zsetBylex1) {
KCCompanyStock byParam = findByParam(s);
if (byParam !=null){
if (byParam.getSecuritycode().startsWith("069001001")){
byParam.setStockCode("sh"+byParam.getSecuritycode());
}
if (byParam.getTrademarketcode().startsWith("069001002")){
byParam.setStockCode("sz"+byParam.getSecuritycode());
}
list.add(byParam);
}
}
}
if (isAllChinese(select)){
String chinese = ChineseToUtfUtil.convertStringToUTF8(select);
String temp = "[" + chinese ;
zsetBylex = jedisCache.getZsetBylex(nameKey, temp, temp + "{");
Set<String> zsetBylex1 = jedisCache.getZsetBylex(nameKey, temp, temp + "{", 0, pageSize.intValue());
for (String s : zsetBylex1) {
String string = ChineseToUtfUtil.convertUTF8ToString(s);
KCCompanyStock byParam = findByParam(s);
if (byParam !=null){
if (byParam.getSecuritycode().contains("A")){
byParam.setSecuritycode("");
}
if (byParam.getTrademarketcode().startsWith("069001001")){
byParam.setStockCode("sh"+byParam.getSecuritycode());
}
if (byParam.getTrademarketcode().startsWith("069001002")){
byParam.setStockCode("sz"+byParam.getSecuritycode());
}
list.add(byParam);
}
}
}
Long total = Long.valueOf(zsetBylex.size());
long pages = (total - 1) / pageSize + 1;
pageInfo.setPages(pages);
pageInfo.setPageSize(Long.valueOf(list.size()));
pageInfo.setTotal(total);
pageInfo.setPageNum(pageNum);
pageInfo.setList(list);
return ResponseContext.getSuccess(pageInfo);
}
public KCCompanyStock findByParam(String param){
if (param == null || "".equals(param)){
return null;
}
String key = all + "_" + param;
String value = jedisCache.getValue(key);
KCCompanyStock kcCompanyStock = JSON.parseObject(value, KCCompanyStock.class);
return kcCompanyStock;
}
public boolean isInteger(String str) {
Pattern pattern = Pattern.compile("^[-\\+]?[\\d]*$");
return pattern.matcher(str).matches();
}
// 判断是否为中文
public static boolean isAllChinese(String str){
for (int i = 0; i < str.length(); i++) { //遍历所有字符
char ch = str.charAt(i);
if(ch < 0x4E00 ||ch > 0x9FA5){ //中文在unicode编码中所在的区间为0x4E00-0x9FA5
return false; //不在这个区间,说明不是中文字符,返回false
}
}
return true; //全部在中文区间,说明全部是中文字符,返回true
}
// 判断是否包含数字
public static boolean HasDigit(String content) {
boolean flag = false;
if ("A".equals(content)){
return true;
}
Pattern p = Pattern.compile(".*\\d+.*");
Matcher m = p.matcher(content);
if (m.matches()) {
flag = true;
}
return flag;
}
}
目录结构:
代码地址:
https://gitee.com/zpjeck/redis-Autocomplete