原文链接:
https://blog.csdn.net/qq_41889508/article/details/123592124
https://blog.csdn.net/qq_41060647/article/details/127528075
首先感谢原文作者,在原有代码做了一点点小的修改,作为记录。直接贴代码
先说下作者改动:支持批量删除历史记录。
原创:
1.新增搜索记录:
新增一条该userid用户在搜索栏的历史记录
public void addRedisRecentSearch(Long hhxsUserId,String searchKeywords) {
ZSetOperations<String,String> zSet = redisTemplate.opsForZSet();
//由于zset 的集合特性当插入已经存在的 v 值 (搜索记录) 时只会更新score 值,
zSet.add(RedisKeyConstant.RECENT_SEARCH+hhxsUserId, searchKeywords, Instant.now().getEpochSecond());
//获取到全部用户的最近搜索记录,用reverseRangeWithScores方法,可以获取到根据score排序之后的集合
Set<ZSetOperations.TypedTuple<String>> typedTuples = zSet.reverseRangeWithScores(RedisKeyConstant.RECENT_SEARCH, 0, -1);
}
2.用户的最近搜索
public List<String> searchRecentList() {
Long userId = getUserId();
Set<String> userRecentSearchVos = redisUtils.listRecentSearch(userId);
List<String> collect = userRecentSearchVos.stream().collect(Collectors.toList());
return collect;
}
/**
* 最近搜索列表
* @return
*/
public Set<String> listRecentSearch(Long userId) {
Set<ZSetOperations.TypedTuple<String>> typedTuples = redisTemplate.opsForZSet().reverseRangeWithScores(ConstantRedisKey.RECENT_SEARCH+userId, 0, -1);
return Optional.ofNullable(typedTuples)
.map(tuples -> tuples.stream()
.map(ZSetOperations.TypedTuple::getValue)
.filter(Objects::nonNull)
.limit(20)
.collect(Collectors.collectingAndThen(
Collectors.toCollection(LinkedHashSet::new), LinkedHashSet::new)))
.orElseGet(LinkedHashSet::new);
}
2.1 最近搜索添加权重
public void addSearchCount(String searchKeywords) {
String key = RedisKeyConstant.SEARCH_TITLE + searchKeywords;
Boolean b = redisTemplate.hasKey(key);
if(b){
String count = (String)redisTemplate.opsForValue().get(key);
redisTemplate.opsForValue().set(key,Long.valueOf(count)+1L);
}else{
redisTemplate.opsForValue().set(key, 1L);
}
}
3.删除该用户下所有历史搜索记录
public void removeZsetAll(Long userId) {
redisTemplate.delete(RedisKeyConstant.RECENT_SEARCH + userId);
}
以下为原作者,感谢!!!
1、pom文件
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.1.8.RELEASE</version>
</dependency>
2、HotSerachController
package net.beidousky.web.app.controller;
import net.beidousky.web.app.domain.Result;
import net.beidousky.web.app.service.impl.HotSearchService;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
* 热搜及最近搜索
* 2022-10-26
* jwb
*/
@RequestMapping("/app/serach")
@RestController
public class HotSerachController {
@Resource
private RedisTemplate redisTemplate;
@Resource
private HotSearchService hotSearchService;
/**
* 删除redis
* @param key
* @return
*/
@GetMapping("/remove")
public Result removeRedis(String key){
redisTemplate.delete(key);
return Result.success();
}
/**
* 搜索
* @param query
* @return
*/
@GetMapping("/search")
public Result listProduct(String query) {
return Result.success(hotSearchService.search(query));
}
/**
* 热搜列表
* @return
*/
@ResponseBody
@GetMapping("/hotSearch")
public Result listHotSearch() {
return Result.success(hotSearchService.listHotSearch());
}
/**
* 最近搜索列表
* @return
*/
@ResponseBody
@GetMapping("/latelySearch")
public Result recentHotSearch() {
return Result.success(hotSearchService.listRecentSearch());
}
}
2、SearchRedisHelper
package net.beidousky.web.app.controller;
import net.beidousky.common.core.domain.model.LoginUser;
import net.beidousky.common.utils.SecurityUtils;
import net.beidousky.web.app.domain.Product;
import net.beidousky.web.app.domain.UserRecentSearch;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.time.Instant;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@Component
public class SearchRedisHelper {
@Resource
private RedisTemplate redisTemplate;
/**
* 热搜的key
*/
public static final String HOT_SEARCH = "hotSearch";
/**
* 最近搜索的key
*/
public static final String RECENT_SEARCH = "latelySearch";
/**
* 最近搜索的大小
*/
public static final Integer CURRENT_SEARCH_SIZE = 3;
/**
* 热搜key的过期时间
*/
public static final Integer HOT_SEARCH_EXPIRE_TIME = 3;
/**
* 设置redis的过期时间
* expire其实是懒加载,不设置key的时候是不会执行的
*/
@PostConstruct
public void setHotSearchExpireTime() {
redisTemplate.expire(HOT_SEARCH, HOT_SEARCH_EXPIRE_TIME, TimeUnit.SECONDS);
}
/**
* redis添加最近搜索
* @param query
*/
public void addRedisRecentSearch(String query) {
UserRecentSearch userRecentSearch = new UserRecentSearch();
LoginUser loginUser = SecurityUtils.getLoginUser();
//用户id 当前用户
userRecentSearch.setUnionId(loginUser.getUserId());
//搜索信息
userRecentSearch.setSearchInfo(query);
//score为一个分值,需要把最近浏览的商品id 的分值设置为最大值,
//此处我们可以设置为当前时间Instant.now().getEpochSecond()
//这样最近浏览的商品id的分值一定最大,排在ZSet集合最前面。
ZSetOperations<String, UserRecentSearch> zSet = redisTemplate.opsForZSet();
//由于zset 的集合特性当插入已经存在的 v 值 (商品id) 时只会更新score 值,
zSet.add(RECENT_SEARCH, userRecentSearch, Instant.now().getEpochSecond());
//获取到全部用户的最近搜索记录,用reverseRangeWithScores方法,可以获取到根据score排序之后的集合
Set<ZSetOperations.TypedTuple<UserRecentSearch>> typedTuples = zSet.reverseRangeWithScores(RECENT_SEARCH, 0, -1);
//只得到当前用户的最近搜索记录,注意这里必须保证set集合的顺序
Set<UserRecentSearch> userRecentSearches = listRecentSearch();
if (userRecentSearches.size() > CURRENT_SEARCH_SIZE) {
//获取到最开始浏览的第一条
UserRecentSearch userRecentSearchLast = userRecentSearches.stream().reduce((first, second) -> second).orElse(null);
//删除最开始浏览的第一条
zSet.remove(RECENT_SEARCH, userRecentSearchLast);
}
}
/**
* 热搜列表
* @return
*/
public Set<Product> listHotSearch() {
//0 5 表示0-5下标对应的元素
return redisTemplate.opsForZSet().reverseRangeWithScores(HOT_SEARCH, 0, 10);
}
/**
* redis添加热搜
* @param query
*/
public void addRedisHotSearch(String query) {
//1:表示每调用一次,当前product的分数+1
// productList.forEach(product -> redisTemplate.opsForZSet().incrementScore(HOT_SEARCH, product, 1D));
redisTemplate.opsForZSet().incrementScore(HOT_SEARCH, query, 1D);
}
/**
* 最近搜索列表
* @return
*/
public Set<UserRecentSearch> listRecentSearch() {
LoginUser loginUser = SecurityUtils.getLoginUser();
Set<ZSetOperations.TypedTuple<UserRecentSearch>> typedTuples = redisTemplate.opsForZSet().reverseRangeWithScores(RECENT_SEARCH, 0, -1);
return Optional.ofNullable(typedTuples)
.map(tuples -> tuples.stream()
.map(ZSetOperations.TypedTuple::getValue)
.filter(Objects::nonNull)
.filter(userRecentSearch -> Objects.equals(userRecentSearch.getUnionId(),loginUser.getUserId()))
.collect(Collectors.collectingAndThen(
Collectors.toCollection(LinkedHashSet::new), LinkedHashSet::new)))
.orElseGet(LinkedHashSet::new);
}
}
3、HotSearchService
package net.beidousky.web.app.service.impl;
import net.beidousky.web.app.controller.SearchRedisHelper;
import net.beidousky.web.app.domain.Product;
import net.beidousky.web.app.domain.UserRecentSearch;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.Set;
@Service
public class HotSearchService {
@Resource
private SearchRedisHelper searchRedisHelper;
/**
* 搜索
* @param query
* @return
*/
public String search(String query) {
//业务代码可用es.....此处略过....模拟数据库数据
searchRedisHelper.addRedisRecentSearch(query);
searchRedisHelper.addRedisHotSearch(query);
return query;
}
/**
* 热搜列表
* @return
*/
public Set<Product> listHotSearch() {
return searchRedisHelper.listHotSearch();
}
/**
* 最近搜索列表
* @return
*/
public Set<UserRecentSearch> listRecentSearch() {
return searchRedisHelper.listRecentSearch();
}
}
4、UserRecentSearch
package net.beidousky.web.app.domain;
import lombok.Data;
import java.io.Serializable;
@Data
public class UserRecentSearch implements Serializable {
/**
* 搜索信息
*/
private String searchInfo;
/**
* 用户id
*/
private Long unionId;
}