ElasticSearch构建分词与自动补全功能
什么是分词功能呢(Analyzer)?
举一个分词简单的例子:比如你输入:Auto Suggestion
,会自动帮你分成两个单词,一个是auto
,另一个是suggestion
,可以看出单词也是被转换成小写的。
如图:
standard analyzer是ElasticSearch自带的分词器,可以看出该分词器对大小写不敏感,你输入大写最后也会被转换成小写
也可以安装一些第三方插件比如说:ik分词器/pinyin分词器
pinyin分词器
顾名思义,看到他的名字就知道他是根据字母来进行补全的
如图:
pinyin分词器地址:
https://github.com/medcl/elasticsearch-analysis-pinyin
了解了在ElasticSearch如果进行分词功能的使用,那么在使用Java中Spring Data ElasticSearch又该如何操作呢?
- 首先新建一个
spring boot
项目,在xml
文件中引入相关依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
- 在
application.yml
文件中写入以下配置:
# elasticsearch ip地址
spring:
elasticsearch:
uris: elasticsearch地址
-
接下来就是分词功能的实现了
1. 目前我们只能通过原生API的方式来进行分词功能的实现,过程比较繁琐,但编写方式统一。
2. 首先要引入RestHighLevelClient
这个对象,是通过这个对象来发送分词请求的
3. 分词功能步骤主要分为三步:- 创建分词请求
- 发送分词请求获取响应结果
- 处理响应结果
- 举个例子:例如你输入
ik analyzer
,会分成两个单词,所以返回的结果集肯定是一个泛型为String类型的集合
- 举个例子:例如你输入
-
在发送分词请求的时候会抛出一个异常,这里选择lombok下的
@SneakyThrows
会抛出已检查异常代码如下:
@Autowired
private RestHighLevelClient highLevelClient;
@SneakyThrows // 抛出已检查异常
public List<String> analyze(String text,String analyzer){
// 创建分词请求
AnalyzeRequest analyzeRequest = AnalyzeRequest.withIndexAnalyzer("goods", analyzer, text);
// 发送分词请求获取响应结果
AnalyzeResponse response = highLevelClient.indices().analyze(analyzeRequest, RequestOptions.DEFAULT);
// 处理响应结果
List<String> words = new ArrayList();
List<AnalyzeResponse.AnalyzeToken> tokens = response.getTokens();
for (AnalyzeResponse.AnalyzeToken token : tokens) {
String tokenTerm = token.getTerm();
words.add(tokenTerm);
}
return words;
}
测试效果如图:
什么是自动补全功能呢?
当用户在搜索框输入字符时,我们应该提示出与该字符相关的搜索项。
我们举一个很常见的例子:当我们在百度中输入字符时,会有一个下拉列表来给出用户提示,这个功能就是自动补全
在ElasticSearch中该如何操作呢?
如图:
当输入iph时就可以查询出iphone
通过Spring Data ElasticSearch
实现自动补全功能,该功能同分词一样,使用的是原生API的方式,过程较繁琐,但比较统一
过程分为三步:
- 创建补全请求
- 发送补全请求,获取响应结果
- 处理结果(该过程比较繁琐)
代码如下:
处理结果集采用jdk8的stream流的形式
如上图
@Autowired
private ElasticsearchRestTemplate template;
@Override
public List<String> autoSuggest(String keyword) {
SuggestBuilder suggestBuilder = new SuggestBuilder();
SuggestionBuilder suggestionBuilder = SuggestBuilders
.completionSuggestion("tags") // 查找的域
.prefix(keyword) // 关键词前缀
.skipDuplicates(true) // 去除重复项
.size(10);// 最多显示10条数据
suggestBuilder.addSuggestion("prefix_suggestion",suggestionBuilder);
SearchResponse response = template.suggest(suggestBuilder, IndexCoordinates.of("goods"));
// 处理响应结果
List<String> stringList = response
.getSuggest()
.getSuggestion("prefix_suggestion")
.getEntries()
.get(0)
.getOptions()
.stream()
.map(Suggest.Suggestion.Entry.Option::getText)
.map(Text::toString)
.collect(Collectors.toList());
return stringList;
}