此篇文章分享 ElasticsearchRestTemplate 实现搜索建议
使用技术栈:SpringBoot 、Elastic Search 、 Kibana、Knife4j 接口文档
什么是搜索建议?
红色框的这个列表就是搜索建议
搜索建议也叫自动补全,我们输入查询时,基于部分输入动态提供可能的完整查询或相关建议的功能。
业务场景
搜索建议,建议什么呢? 我们以 mysql 的 title 字段作为搜索建议, 给 ES 库 suggest 字段的值。
解释前缀搜索建议:通过用户输入搜索词当作搜索前缀,匹配 ES 数据库所有的 suggest (搜索建议字段),成功匹配会返回一个 List<String> 集合。
这就是一个前缀搜索建议
"suggest" : {
"type" : "completion",
"analyzer" : "simple",
"preserve_separators" : true,
"preserve_position_increments" : true,
"max_input_length" : 50
}
实现步骤
ES 要求:要实现搜索建议 Suggester
,必须构造一个类型 completion 的字段索引,用来存搜索建议,用以下语法追加字段
PUT /post_v1/_mapping
{
"properties": {
"suggest": {
"type": "completion"
}
}
}
1. 从 0 开始,新建 ES 索引
动 kibana,打开 dev tool 窗口
新建索引指令,不用过多关注 title 字段的 fields, 跟搜索建议无关
PUT post_v1
{
"aliases": {
"post": {}
},
"mappings": {
"properties": {
"favournum": {
"type": "long"
},
"id": {
"type": "long"
},
"isDelete": {
"type": "keyword"
},
"tags": {
"type": "keyword"
},
"thumbnum": {
"type": "long"
},
"title": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
},
"analyzer": "ik_max_word",
"search_analyzer": "ik_smart"
},
"suggest": {
"type": "completion"
}
}
}
}
2. 同步数据到 ES
ES 索引目前是空的,把 MySQL 的数据同步到 ES ,使用 logstash ,编写配置文件 mytask.conf
根据配置文件,你需要提供一个 mysql 的 jar 包,才能正常运行 logstash, 本地 maven仓库里找一个就行
input {
jdbc {
jdbc_driver_library => "D:\software\ElasticStack\logstash-7.17.23\config\mysql-connector-java-8.0.29.jar(你本地的 mysql 依赖包)"
jdbc_driver_class => "com.mysql.cj.jdbc.Driver"
jdbc_connection_string => "jdbc:mysql://localhost:3306/my_db"
jdbc_user => "你的mysql数据库账号"
jdbc_password => "你的mysql数据库密码"
statement => "SELECT * from post"
parameters => { "favorite_artist" => "Beethoven" }
schedule => "*/5 * * * * *" # 每五秒执行一次
jdbc_default_timezone => "Asia/Shanghai"
plugin_timezone => "local"
}
}
filter {
if [title] and [title] != "" {
mutate {
add_field => {
"[suggest][input]" => "%{title}"
}
}
}
}
output {
stdout { codec => rubydebug }
elasticsearch {
hosts => "http://localhost:9200"
index => "post_v1"
document_id => "%{id}"
doc_as_upsert => true
}
}
运行 logstash , 在自己的 logstash 文件路径输出cmd,打开黑窗口,运行
bin> logstash.bat -f ..\config\mytask.conf
同步成功!ES 查询
3. ElasticsearchRestTemplate 实现搜索建议
引入依赖
<!-- elasticsearch-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
封装请求参数类
封装响应类
写一个 SearchSuggestController, 用 ElasticsearchRestTemplate 实现搜索建议,根据输入的搜索词作为前缀匹配 ES 的 suggest 字段。匹配整个 post_v1 的 suggest, 所以返回 List<String>
ps:这里用到了自己封装的响应包装类 BaseResponse ,不用也一样
import org.springframework.data.elasticsearch.core.suggest.response.Suggest;
@RestController()
@RequestMapping("/search")
@Slf4j
public class SearchSuggestController {
@Resource
private ElasticsearchRestTemplate elasticsearchRestTemplate;
@PostMapping("/suggest")
public BaseResponse<SearchSuggestVO> searchAll(@RequestBody SearchRequest searchRequest, HttpServletRequest request) {
String searchText = searchRequest.getSearchText();
// 构造建议
SuggestBuilder suggestBuilder = new SuggestBuilder()
// "post-suggest" 是自定义搜索建议名称
.addSuggestion("post-suggest", SuggestBuilders.completionSuggestion("suggest")
.prefix(searchText));
// 构造查询
NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
.withSuggestBuilder(suggestBuilder)
.build();
SearchHits<PostEsDTO> searchHits = elasticsearchRestTemplate.search(searchQuery, PostEsDTO.class);
// 提取搜索建议
Suggest suggest = searchHits.getSuggest();
List<String> suggestions = new ArrayList<>();
if (suggest != null) {
Suggest.Suggestion<? extends Suggest.Suggestion.Entry<? extends Suggest.Suggestion.Entry.Option>> postSuggest = suggest.getSuggestion("post-suggest");
if (postSuggest != null) {
for (Suggest.Suggestion.Entry<? extends Suggest.Suggestion.Entry.Option> entry : postSuggest.getEntries()) {
for (Suggest.Suggestion.Entry.Option option : entry.getOptions()) {
suggestions.add(option.getText()); // 添加每个建议结果到列表
}
}
}
}
SearchSuggestVO searchSuggestVO = new SearchSuggestVO();
searchSuggestVO.setSuggestions(suggestions);
return ResultUtils.success(searchSuggestVO);
}
Knif4j 测试接口
响应了搜索建议,完成!