文章目录
- 前言
- 一、概念
- 二、拼音分词器
- 三、自定义拼音分词器
- 四、自动补全查询
- 五、自动补全嵌入项目
- 5.1 修改索引库映射结构
- 5.2 修改实体类
- 5.3 重新导入数据
- 5.4 自动补全的JavaAPI
前言
ES自动补全
当用户在搜索框输入字符时,我们应该提示出与该字符有关的搜索项。这种根据用户输入的字母,提示完整词条的功能,就是自动补全了。
一、概念
当用户在搜索框输入字符时,我们应该提示出与该字符有关的搜索项。这种根据用户输入的字母,提示完整词条的功能,就是自动补全了。
① 设置创建索引库(设置一个自动补全字段,类型为:completion)
② 重新插入数据
③ 查询(查询时要设置这个自动补全操作的名称,并且指定那个类型为completion的字段)
④ 分解结果(结果也需要根据之前设置这个自动查询操作的名称来取)
二、拼音分词器
下载拼音分词器记得版本要和ES对应,不对应会报错
要实现根据字母做补全,就必须对文档按照拼音分词。在GitHub上恰好有Elasticsearch的拼音分词插件。地址:https://github.com/medcl/Elasticsearch-analysis-pinyin
课前资料中也提供了拼音分词器的安装包:
安装方式与IK分词器一样,分三步:
①解压
②上传到虚拟机中,Elasticsearch的plugin目录
③重启Elasticsearch
④测试
详细安装步骤可以参考IK分词器的安装过程。
三、自定义拼音分词器
如何使用拼音分词器?
①下载pinyin分词器
②解压并放到Elasticsearch的plugin目录
③重启即可
如何自定义分词器?
①创建索引库时,在settings中配置,可以包含三部分
②character filter
③tokenizer
④filter
拼音分词器注意事项?
- 为了避免搜索到同音字,搜索时不要使用拼音分词器
默认的拼音分词器会将每个汉字单独分为拼音,而我们希望的是每个词条形成一组拼音,需要对拼音分词器做个性化定制,形成自定义分词器。官网文档查询地址:https://github.com/medcl/Elasticsearch-analysis-pinyin
Elasticsearch中分词器(analyzer)的组成包含三部分:
- character filters:在tokenizer之前对文本进行处理。例如删除字符、替换字符
- tokenizer:将文本按照一定的规则切割成词条(term)。例如keyword,就是不分词;还有ik_smart
- tokenizer filter:将tokenizer输出的词条做进一步处理。例如大小写转换、同义词处理、拼音处理等
文档分词时会依次由这三部分来处理文档:
声明自定义分词器的语法如下:
PUT/test{"settings":{"analysis":{"analyzer":{// 自定义分词器"my_analyzer":{// 分词器名称"tokenizer":"ik_max_word","filter":"py"}},"filter":{// 自定义tokenizer filter"py":{// 过滤器名称"type":"pinyin",// 过滤器类型,这里是pinyin"keep_full_pinyin":false,"keep_joined_full_pinyin":true,"keep_original":true,"limit_first_letter_length":16,"remove_duplicated_term":true,"none_chinese_pinyin_tokenize":false}}}},"mappings":{"properties":{"name":{"type":"text","analyzer":"my_analyzer","search_analyzer":"ik_smart"}}}}测试:
四、自动补全查询
三步骤:
① 创建索引库
② 插入数据
③ 查询的DSL语句
Elasticsearch提供了Completion Suggester查询来实现自动补全功能。这个查询会匹配以用户输入内容开头的词条并返回。为了提高补全查询的效率,对于文档中字段的类型有一些约束:
参与补全查询的字段必须是completion类型。
字段的内容一般是用来补全的多个词条形成的数组。
比如,一个这样的索引库:
// 创建索引库PUTtest{"mappings":{"properties":{"title":{"type":"completion"}}}}然后插入下面的数据:
// 示例数据POSTtest/_doc{"title":["Sony","WH-1000XM3"]}POSTtest/_doc{"title":["SK-II","PITERA"]}POSTtest/_doc{"title":["Nintendo","switch"]}查询的DSL语句如下:
// 自动补全查询GET/test/_search{"suggest":{"title_suggest":{//设置这个自动查询操作的名称"text":"s",// 关键字"completion":{"field":"title",// 补全查询的字段名"skip_duplicates":true,// 跳过重复的"size":10// 获取前10条结果}}}}五、自动补全嵌入项目
5.1 修改索引库映射结构
重点注意:
① all、name字段等要 分词设置为自定义分词器(“analyzer”: “text_anlyzer”)(一般要分词,然后再对分词后的词语进行拼音处理),查询设置为最精简分词器(“search_analyzer”: “ik_smart”)
② 设置一个自动补全字段(如 suggestion) 类型必须为:completion,并且使用自定义分词器(一般不分词直接对整个词语进行拼音处理)
先删除之前的索引库,再设置如下:
// 酒店数据索引库PUT/hotel{"settings":{"analysis":{"analyzer":{"text_anlyzer":{"tokenizer":"ik_max_word","filter":"py"},"completion_analyzer":{"tokenizer":"keyword","filter":"py"}},"filter":{"py":{"type":"pinyin","keep_full_pinyin":false,"keep_joined_full_pinyin":true,"keep_original":true,"limit_first_letter_length":16,"remove_duplicated_term":true,"none_chinese_pinyin_tokenize":false}}}},"mappings":{"properties":{"id":{"type":"keyword"},"name":{"type":"text","analyzer":"text_anlyzer","search_analyzer":"ik_smart","copy_to":"all"},"address":{"type":"keyword","index":false},"price":{"type":"integer"},"score":{"type":"integer"},"brand":{"type":"keyword","copy_to":"all"},"city":{"type":"keyword"},"starName":{"type":"keyword"},"business":{"type":"keyword","copy_to":"all"},"location":{"type":"geo_point"},"pic":{"type":"keyword","index":false},"all":{"type":"text","analyzer":"text_anlyzer","search_analyzer":"ik_smart"},"suggestion":{"type":"completion","analyzer":"completion_analyzer"}}}}5.2 修改实体类
类型为completion的字段需要在改造方法里做组装
HotelDoc中要添加一个字段,用来做自动补全,内容可以是酒店品牌、城市、商圈等信息。按照自动补全字段的要求,最好是这些字段的数组。
因此我们在HotelDoc中添加一个suggestion字段,类型为List<String>,然后将brand、city、business等信息放到里面。
代码如下:
packagecn.itcast.hotel.pojo;importlombok.Data;importlombok.NoArgsConstructor;importjava.util.ArrayList;importjava.util.Arrays;importjava.util.Collections;importjava.util.List;@Data@NoArgsConstructorpublicclassHotelDoc{privateLongid;privateStringname;privateStringaddress;privateIntegerprice;privateIntegerscore;privateStringbrand;privateStringcity;privateStringstarName;privateStringbusiness;privateStringlocation;privateStringpic;privateObjectdistance;privateBooleanisAD;privateList<String>suggestion;publicHotelDoc(Hotelhotel){this.id=hotel.getId();this.name=hotel.getName();this.address=hotel.getAddress();this.price=hotel.getPrice();this.score=hotel.getScore();this.brand=hotel.getBrand();this.city=hotel.getCity();this.starName=hotel.getStarName();this.business=hotel.getBusiness();this.location=hotel.getLatitude()+", "+hotel.getLongitude();this.pic=hotel.getPic();// 组装suggestionif(this.business.contains("/")){// business有多个值,需要切割String[]arr=this.business.split("/");// 添加元素this.suggestion=newArrayList<>();this.suggestion.add(this.brand);Collections.addAll(this.suggestion,arr);}else{this.suggestion=Arrays.asList(this.brand,this.business);}}}5.3 重新导入数据
先删除数据,再重新执行之前编写的导入数据功能,可以看到新的酒店数据中包含了suggestion:
5.4 自动补全的JavaAPI
示例:(这两幅图有点乱,看不懂就忽略)
查询代码如下:
解析结果代码如下:
1)在cn.itcast.hotel.web包下的HotelController中添加新接口,接收新的请求:
@GetMapping("suggestion")publicList<String>getSuggestions(@RequestParam("key")Stringprefix){returnhotelService.getSuggestions(prefix);}2)在cn.itcast.hotel.service包下的IhotelService中添加方法:
List<String>getSuggestions(Stringprefix);3)在cn.itcast.hotel.service.impl.HotelService中实现该方法:
@OverridepublicList<String>getSuggestion(Stringprefix){try{// 1.准备RequestSearchRequestrequest=newSearchRequest("hotel");// 2.准备DSLrequest.source().suggest(newSuggestBuilder().addSuggestion("hotelSuggestion",//设置这个自动补全操作的名称SuggestBuilders.completionSuggestion("suggestion")//类型为completion的字段名.prefix(prefix).skipDuplicates(true).size(10)));// 3.发起请求SearchResponseresponse=client.search(request,RequestOptions.DEFAULT);// 4.解析结果Suggestsuggest=response.getSuggest();// 4.1.根据补全查询名称,获取补全结果(这里的参数是索引库里类型为completion的字段名)CompletionSuggestionsuggestions=suggest.getSuggestion("hotelSuggestion");//之前设置的这个自动查询操作的名称// 4.2.获取optionsList<CompletionSuggestion.Entry.Option>options=suggestions.getOptions();// 4.3.遍历List<String>list=newArrayList<>(options.size());for(CompletionSuggestion.Entry.Optionoption:options){Stringtext=option.getText().toString();list.add(text);}returnlist;}catch(IOExceptione){thrownewRuntimeException(e);}}本文的引用仅限自我学习如有侵权,请联系作者删除。
参考知识
ElasticSearch (ES从入门到精通一篇就够了)
ELK介绍