业务场景
用户输入内容,快速返回建议,示例效果如下
技术选型
- spring boot 3
- elasticsearch server 7.17.4
- spring data elasticsearch 5.0.1
- elasticsearch-java-api 8.5.3
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>8.5.3</version>
</dependency>
yml
spring:
elasticsearch:
uris: http://127.0.0.1:9200
data:
elasticsearch:
repositories:
enabled: true
实体类
为了启动时候自己创建相关的index,以及存储搜索内容
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.CompletionField;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
import org.springframework.data.elasticsearch.core.suggest.Completion;
/**
* 业务搜索建议
* @author chunyang.leng
* @date 2023-08-21 14:24
*/
@Document(indexName = "biz_suggest")
public class BizSuggestDocument {
@Id
private Long id;
/**
* 标题,可以用于纠错,不参与搜索建议
*/
@Field(type = FieldType.Text, analyzer = "ik_max_word")
private String name;
/**
* 自动补全标题,搜索建议使用的对象
*/
@CompletionField(analyzer = "ik_max_word", searchAnalyzer = "ik_smart")
private Completion completionName;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Completion getCompletionName() {
return completionName;
}
public void setCompletionName(Completion completionName) {
this.completionName = completionName;
}
}
搜索结果对象
/**
* 搜索建议返回对象
* @author chunyang.leng
* @date 2023-08-21 19:02
*/
public class SuggestVO {
/**
* 数据id
*/
private Long id;
/**
* 内容
*/
private String text;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
搜索业务层
- 数据导入时候,因为有数据格式要求,必须使用实体类进行写入
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.CompletionSuggester;
import co.elastic.clients.elasticsearch.core.search.Suggester;
import co.elastic.clients.elasticsearch.core.search.Suggestion;
import co.elastic.clients.elasticsearch.core.search.TermSuggester;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* @author chunyang.leng
* @date 2023-08-18 18:29
*/
@Component
public class SuggestionServiceImpl implements SuggestionService {
/**
* 搜索建议 key
*/
private static final String SUGGEST_TAG = "suggest_query";
/**
* 纠错key
*/
private static final String TERM_TAG = "suggest_team";
@Autowired
private ElasticsearchClient elasticsearchClient;
/**
* 根据 关键词,返回搜索建议
*
* @param match 搜索关键词
* @return 搜索建议,10条
*/
@Override
public List<SuggestVO> suggest(String match) throws IOException {
SearchRequest completionSuggestSearchRequest = new SearchRequest
.Builder()
.suggest(
new Suggester
.Builder()
.suggesters(SUGGEST_TAG, builder -> builder.prefix(match)
.completion(new CompletionSuggester
.Builder()
.field("completionName")
.size(10)
.build()
)
)
.build())
.build();
SearchResponse<BizSuggestDocument> completionSuggestSearch = elasticsearchClient.search(completionSuggestSearchRequest, BizSuggestDocument.class);
Map<String, List<Suggestion<BizSuggestDocument>>> suggest = completionSuggestSearch.suggest();
List<Suggestion<BizSuggestDocument>> suggestions = suggest.get(SUGGEST_TAG);
return suggestions
.parallelStream()
.flatMap(x -> x.completion()
.options()
.stream()
.map(o -> {
// 原始数据对象,如果有需要,可以对其进行操作
BizSuggestDocument source = o.source();
String text = o.text();
String idValue = o.id();
Long id = Long.valueOf(idValue);
SuggestVO vo = new SuggestVO();
vo.setId(id);
vo.setText(text);
return vo;
}))
.collect(Collectors.toList());
}
}
导入数据
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.elasticsearch.client.elc.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.suggest.Completion;
/**
* @author chunyang.leng
* @date 2023-08-21 18:09
*/
@SpringBootTest
public class EsTest {
@Autowired
private ElasticsearchTemplate elasticsearchTemplate;
@Test
public void test() {
BizSuggestDocument document = new BizSuggestDocument();
document.setId(1L);
document.setName("飞翔的世界");
String[] s = "你的世界1.0,我的世界2.0".split(",");
Completion completion = new Completion(s);
completion.setWeight(10);
document.setCompletionName(completion);
elasticsearchTemplate.save(document);
BizSuggestDocument document2 = new BizSuggestDocument();
document2.setId(2L);
document2.setName("路人甲乙丙");
String[] s2 = "你的滑板鞋1.0,我的滑板鞋2.0".split(",");
Completion completion1 = new Completion(s2);
completion1.setWeight(5);
document2.setCompletionName(completion1);
elasticsearchTemplate.save(document2);
}
}
POSTMAN 测试结果如下