其实这个搜索的功能反倒跟redis没啥关系了,所以我也没归类在redis里。基于es的搜索也不无非是增删改查,先简单说一下项目里用到的这两个功能和api。后续我会详细学习

elasticsearch

es是一个搜索器,具体怎么搜的我不知道(笑)。目前的理解是在新增blog的时候同时把这个对象给es服务器,它通过索引的方式进行缓存。后续查找的时候就可以根据field,也就是字段名来指定匹配位置。

比如这里要搜索文章标题的关键字,就用title;如果是内容的,就用content。经过我的实验发现黑马给的分词器只能检索中文,我想要搜素字母“s”,都搜不到。

项目里的应用也比较简单,就是上述的关键字搜索,然后返回前端的时候要标红,这个步骤是通过在前后加font标签完成的。

首先是配置:

这里用到的对象是RestHighLevelClient,我们只需要配置端口号和ip就可以了,这个客户端会使用es的restful端口进行增删改查。

我们对其的增删改查操作就跟redistemplate一样的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Configuration
public class ElasticSearchConfig {
@Value("${elasticsearch.host}")
private String host;
@Value("${elasticsearch.port}")
private Integer port;
/**
* 配置RestHighLevelClient对象
* 将该对象交给Spring容器去管理
*
* @return RestHighLevelClient对象
*/
@Bean
public RestHighLevelClient restHighLevelClient() {
return new RestHighLevelClient(
RestClient.builder(
//若有多个,可以传一个数组
new HttpHost(host, port, "http")));
}
}

用这个RestHighLevelClient,传输的是一个httprequest,返回的是response。思路是创建一个request,查询标题和内容两个方面的关键词。
返回以后进行一个转换,返回的加上颜色标签的hits是一个数组,需要转换成字符串。最后向前端返回这个字符串。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
@Override
public Result searchBlog(QueryBlogDto dto) throws IOException {
//1.输入校验
if (dto == null){
return Result.fail("输入为空");
}
if (dto.getKeyWord() == null || dto.getKeyWord().isEmpty()){
return Result.fail("输入关键词为空");
}
//异步给mongo
insertSearchHistory(dto.getKeyWord());

//2.查询条件
SearchRequest searchRequest = new SearchRequest("hmdp_blogs");
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

//2.1布尔类型的查询
BoolQueryBuilder builder = QueryBuilders.boolQuery();

//2.2 查询标题和内容两方面的关键词
QueryStringQueryBuilder queryStringQueryBuilder = QueryBuilders.queryStringQuery(dto.getKeyWord()).field("content").field("title").defaultOperator(Operator.OR);
builder.must(queryStringQueryBuilder);

//2.3 文本高亮这个查找到的关键字
HighlightBuilder highlightBuilder = new HighlightBuilder();
highlightBuilder.field("title");
highlightBuilder.field("content");
//2.3.1 标红
highlightBuilder.preTags("<font style='color: red; font-size: inherit;'>");
highlightBuilder.postTags("</font>");
searchSourceBuilder.highlighter(highlightBuilder);

//2.4 传给searchRequest
searchSourceBuilder.query(builder);
searchRequest.source(searchSourceBuilder);

//2.5 包装好了的request给客户端返回
SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

//3 处理消息回应,一个gethits是一个类,要获取这个数组需要在这个包装类里面再获取。
SearchHit[] hits = response.getHits().getHits();
List<Map> list = new ArrayList<>();
for (SearchHit hit : hits){
String json = hit.getSourceAsString();
Map map = JSONUtil.toBean(json, Map.class);
Map<String, HighlightField> highlightFields = hit.getHighlightFields();
if (highlightFields != null){
HighlightField title = highlightFields.get("title");
HighlightField content = highlightFields.get("content");
if (title != null){
//title高亮
Text[] titleFragments = title.getFragments();
String collect = Arrays.stream(titleFragments).map((value) -> value.toString()).collect(Collectors.joining());
map.put("title",collect);
}
if (content != null){
//content高亮
Text[] contentFragments = content.getFragments();
String collect = Arrays.stream(contentFragments).map((value) -> value.toString()).collect(Collectors.joining());
map.put("content",collect);
}
}

list.add(map);
}
return Result.ok(list);
}

mongodb

登录用户的查询记录很多而且变化频繁。需要用非关系型数据库来存储。(其实我个人觉得没必要存在服务器上,这些缓存应该都是在客户端上保留的)

mongodb的配置比较简单,甚至不用写配置类。直接看使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/**
* 保存搜索记录
* @param keyWord
*/
@Async
public void insertSearchHistory(String keyWord){

Long userID = UserHolder.getUser().getId();
//1 先从db找这个数据
Query query = Query.query(Criteria.where("userId").is(userID).and("keyWord").is(keyWord));
QueryBlogDto searchHistory = mongoTemplate.findOne(query, QueryBlogDto.class);
//2 如果又就更新创建时间
if (searchHistory != null){
searchHistory.setCreateTime(new Date());
}
//3 没有就存进db
QueryBlogDto dto = new QueryBlogDto();
dto.setCreateTime(new Date());
dto.setUserId(userID);
dto.setKeyWord(keyWord);

Query query1 = Query.query(Criteria.where("userId").is(userID))
.with(Sort.by(Sort.Direction.DESC,"createTime"));
List<QueryBlogDto> dtos = mongoTemplate.find(query1, QueryBlogDto.class);
//3.1 如果数量少于10条就直接存
if (dtos.size() < 10){
mongoTemplate.save(dto);
}
//3.2 如果多余10就要替换了
else {
QueryBlogDto last = dtos.get(dtos.size() - 1);
mongoTemplate.findAndReplace(Query.query(Criteria.where("keyWord").is(last.getKeyWord())),dto);
}

}

值得注意的是这里使用了@Async,gpt给我的答案是这个跟你新开一个线程没有区别,但是注意要在启动类上enable。这里query更像lambdaQueryWrapper那种。具体的增删改查语法以后再学。