这里主要涉及存储和查询方案。不涉及前端搜索的分词解析。完整方案,需要包括前端分词,排除停止词,挖掘相关的词后再进行热搜词的入库和搜索。
Redis Zincrby 命令对有序集合中指定成员的分数加上增量 increment
当 key 不存在,或分数不是 key 的成员时, ZINCRBY key increment member 等同于 ZADD key increment member 。
如:对key为2019-11-25,成员为keywords,增加5分
redis 127.0.0.1:6379> ZINCRBY '2019-11-25' 5 keywords
Redis Zrange 返回有序集中,结果由小到大;Zrevrange 命令返回结果由大到小。
如:查出key为2019-11-25,top3的成员
redis 127.0.0.1:6379> ZRANGE '2019-11-25' 0 3
利用有序集合zset的特性,即可达到按权重排序的效果。
Java实现:
@Component
@Slf4j
public class HotSearchProcessor {
/**
* 保存搜索
* @param keyword
*/
public void saveSkuSearch(String keyword){
JedisUtil.getJedisInstance().execZIncrBy(DateUtil.format(new Date(), "yyyyMMdd"), 1, keyword);
}
/**
* 获取热搜
*
* @param backwardDay 统计天数
* @param hotNum 热门检索数量
* @return
*/
public List<String> getSkuHotSearch(Integer backwardDay, Integer hotNum) {
Date nowDate = new Date();
int end = hotNum * 3;
List<MallSkuHotSearchResp> totalList = new ArrayList<>(backwardDay * end);
for (int i = 0; i <= backwardDay; i++) {
Set<Tuple> tupleSet = JedisUtil.getJedisInstance().execZrevrangeWithScores(DateUtil.format(DateUtils.addDays(nowDate, 0 - i), "yyyyMMdd"), 0, end);
if (CollectionUtils.isNotEmpty(tupleSet)) {
for (Tuple tuple : tupleSet) {
MallSkuHotSearchResp resp = new MallSkuHotSearchResp();
resp.setScore((int) tuple.getScore());
resp.setSearchKeyword(tuple.getElement());
totalList.add(resp);
}
}
}
Map<String, Integer> map = new LinkedHashMap<>(totalList.size());
totalList.stream().collect(Collectors.groupingBy(MallSkuHotSearchResp::getSearchKeyword)).forEach((k, v) -> {
map.put(k, v.stream().mapToInt(MallSkuHotSearchResp::getScore).sum());
});
map.entrySet().stream().sorted(Map.Entry.<String, Integer>comparingByValue().reversed()).forEachOrdered(e -> map.put(e.getKey(), e.getValue()));
List<String> respList = new ArrayList<>();
map.entrySet().stream().limit(hotNum).forEach(entry -> {
respList.add(entry.getKey());
});
return respList;
}
}