前言
今天给大家讲述一下如何简单快速的基于SpringBoot整合ElasticSearch搜索补全功能,该功能是全文检索中很常见的功能,当我们输入关键字,ElasticSearch自动帮我们进行单词联想如下:
自动补全演示
这里ElasticSearch采用的6.8.6版本,ElasticSearch和Kibana的安装教程请见第一章《ElasticsSearch安装》, 在ES官方文档提供了Completion Suggester实现自动补全功能,需要自动补全的字段类型一定是completion,参与补全查询的字段必须是completion类型。字段的内容可以是用来补全的多个词条形成的数组。下面先使用kibana做一个演示:
第一步,创建索引
PUT goods #创建索引
第二步:创建映射 , 下面给title指定为completion类型,且使用ik_smart分词
PUT goods/_doc/_mapping
{
"properties": {
"title": {
"type": "completion",
"analyzer" : "ik_smart"
}
}
}
第三步:给索引添加文档 ,下面添加了2个文档,title 都是以笔记本开头的内容
PUT goods/_doc/1
{
"title":"笔记本支架"
}
PUT goods/_doc/2
{
"title":"笔记本电脑"
}
第四步:执行搜索,通过 suggest 处理补全
GET /goods/_search
{
"suggest": {
"title_suggest": {
"text": "笔记本",
"completion": {
"field": "title",
"skip_duplicates": true,
"size": 10
}
}
}
}
- title_suggest :为 suggest取的一个名字而已
- text :自动补全的文本前缀
- field:对哪个字段自动补全
- skip_duplicates : 跳过重复
- size :取前10条结果
查询的结果如下
SpringBoot整合ES完成自动补全
第一步:导入依赖,我采用的SpringBoot版本是2.2.5.RELEASE, pom.xml如下
<parent>
<groupId> org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.5.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
第二步:yaml配置 ,启动类常规写法,配置文件中指向ES地址,如下
server:
port: 1000
spring:
elasticsearch:
rest:
uris:
- http://localhost:9200
第三步:编写一个ES的Document对象
@Data
@AllArgsConstructor
@NoArgsConstructor
@Document(indexName = "goods",type = "_doc" ,shards = 1 ,replicas = 0)
public class GoodsDoc {
@Id
@Field(type = FieldType.Keyword)
private String id;
@CompletionField(analyzer="ik_smart",searchAnalyzer="ik_smart", maxInputLength = 100)
private Completion title;
@Field(type = FieldType.Float)
private float price;
}
这里把title标记为Completion类型,同时使用了@CompletionField注解指定为自动补全字段。使用IK分词器
第四步:编写repository 对象
@Repository
public interface GoodsRepository extends ElasticsearchRepository<GoodsDoc, String> {
}
第五步:编写成测试类
@RunWith(SpringRunner.class)
@SpringBootTest(classes = ApplicationStarter.class)
public class ESTest {
@Autowired
private ElasticsearchRestTemplate restTemplate;
@Autowired
private GoodsRepository repository;
//创建索引和映射
@Test
public void testCreateIndex(){
restTemplate.createIndex(GoodsDoc.class);
restTemplate.putMapping(GoodsDoc.class);
}
//添加数据
@Test
public void addData(){
List<GoodsDoc> orderDocs = new ArrayList<>();
List<String> suggestList = new ArrayList<>();
String title = "笔记本电脑";
suggestList.add(title); //可以把多个内容作为suggest的数据源
Completion suggest = new Completion(suggestList.toArray(new String[suggestList.size()]));
GoodsDoc orderDoc = new GoodsDoc("1", suggest, 100);
orderDocs.add(orderDoc);
suggestList = new ArrayList<>();
title = "笔记本";
suggestList.add(title);
suggest = new Completion(suggestList.toArray(new String[suggestList.size()]));
orderDoc = new GoodsDoc("2", suggest, 100);
orderDocs.add(orderDoc);
suggestList = new ArrayList<>();
title = "笔记本支架";
suggestList.add(title);
suggest = new Completion(suggestList.toArray(new String[suggestList.size()]));
orderDoc = new GoodsDoc("3", suggest, 100);
orderDocs.add(orderDoc);
suggestList = new ArrayList<>();
title = "笔记本内存条";
suggestList.add(title);
suggest = new Completion(suggestList.toArray(new String[suggestList.size()]));
orderDoc = new GoodsDoc("4", suggest, 100);
orderDocs.add(orderDoc);
repository.saveAll(orderDocs);
}
@Test
public void search(){
// 使用suggest进行标题联想
CompletionSuggestionBuilder suggest = SuggestBuilders.completionSuggestion("title")
//根据什么前缀来联想
.prefix("笔记本")
// 跳过重复过滤
.skipDuplicates(true)
// 匹配数量
.size(10);
SuggestBuilder suggestBuilder = new SuggestBuilder();
suggestBuilder.addSuggestion("title-suggest",suggest);
//执行查询
SearchResponse suggestResp = restTemplate.suggest(suggestBuilder, GoodsDoc.class);
//拿到Suggest结果
Suggest.Suggestion<? extends Suggest.Suggestion.Entry<? extends Suggest.Suggestion.Entry.Option>> orderSuggest = suggestResp
.getSuggest().getSuggestion("title-suggest");
// 处理返回结果
List<String> suggests = orderSuggest.getEntries().stream()
.map(x -> x.getOptions().stream()
.map(y->y.getText().toString())
.collect(Collectors.toList())).findFirst().get();
// 输出内容
for (String str : suggests) {
System.out.println("自动补全 = " + str);
}
}
}
查询效果如下
自动补全 = 笔记本
自动补全 = 笔记本内存条
自动补全 = 笔记本支架
自动补全 = 笔记本电脑
这里是我们根据"笔记本"为前缀联想到的内容,我们把这个结果列表响应给前端就可以做成自动补全的效果了。