网站开发制作费入会计科目,做毕业设计个人网站任务书,怎么做网站的内链,百度竞价个人开户文章目录 1. 什么是自动补全2. 拼音分词器2.1 初识拼音分词器2.2 下载拼音分词器2.3 安装拼音分词器2.4 测试拼音分词器 3. 自定义分词器3.1 拼音分词器存在的问题3.2 分词器#xff08;analyzer#xff09;的组成3.3 如何自定义分词器3.4 拼音分词器的可选参数3.5 配置自定义… 文章目录 1. 什么是自动补全2. 拼音分词器2.1 初识拼音分词器2.2 下载拼音分词器2.3 安装拼音分词器2.4 测试拼音分词器 3. 自定义分词器3.1 拼音分词器存在的问题3.2 分词器analyzer的组成3.3 如何自定义分词器3.4 拼音分词器的可选参数3.5 配置自定义分词器的tokenizer和filter3.6 如何使用自定义分词器3.7 测试自定义分词器3.7.1 直接测试3.7.2 插入文档测试 3.8 使用自定义分词器要注意的事项 4. DSL实现自动补全查询4.1 字段的类型的约束4.2 查询语法 5. 自动补全案例5.1 准备工作5.1.1 创建hotel索引库5.1.2 导入测试工程5.1.3 导入酒店数据到数据库中5.1.4 将数据库中的数据导入到ElasticSearch 5.2 测试自动补全功能 6. RestAPI实现自动补全查询7. 综合案例实现搜索框自动补全 视频教程SpringCloudRabbitMQDockerRedis搜索分布式系统详解springcloud微服务技术栈课程|黑马程序员Java微服务 阅读本文前可以先阅读以下文章
ElasticSearch快速入门——上篇认识ElasticSearch、安装ElasticSearch、安装kibana、IK分词器、ElasticSearch中的基本概念、索引库操作、文档操作ElasticSearch快速入门——下篇在Java代码中操作ElasticSearch、JavaRestClient、操作索引库、操作文档、DSL查询、JavaRestClient查询、数据聚合通过docker启动ElasticSearch后为ElasticSearch设置用户和密码
1. 什么是自动补全
ElasticSearch 中的自动补全跟我们理解的自动补全不太一样为了大家理解我们来看一个案例 当我们在搜索框输入 sj 时搜索框下方会显示以 sj 拼音首字母开头的词条如手机、湿巾、数据线、史记、书架等这个功能被称为自动补全
自动补全功能可以让用户尽可能地搜索到想要的东西而不需要打出完整的内容
2. 拼音分词器
要想实现自动补全功能我们需要先学习一下拼音分词器因为自动补全功能是基于拼音分词器实现的
2.1 初识拼音分词器
拼音分词器的官网analysis-pinyin
拼音分词器跟我们学过的 IK 分词器相似都是 ElasticSearch 的一个插件
2.2 下载拼音分词器
下载地址v7.17.18 本次演示使用的 ElasticSearch 版本为 7.17.18
其它 ElasticSearch 版本对应的拼音分词器的下载地址Tags
2.3 安装拼音分词器
解压完成之后将拼音分词器上传到 ElasticSearch 的 plugin 目录下本次演示是通过 docker 安装 ElasticSearch 的 先将拼音分词器上传到服务器一般是当前用户的目录
cd ~接着将拼音分词器复制到 ElasticSearch 的 plugin 的目录下
sudo cp elasticsearch-analysis-pinyin-7.17.18 -r /var/lib/docker/volumes/elasticsearch-plugins/_data最后重启 ElasticSearch 容器
sudo docker restart elasticsearch2.4 测试拼音分词器
我们在 Kibana 提供的控制台中测试拼音分词器是否生效 在浏览器打开 Kibana 提供的控制台
http://127.0.0.1:5601/app/dev_tools#/console输入以下内容测试拼音分词器是否生效
POST /_analyze
{text: [练习时长两年半],analyzer: pinyin
}测试结果如下主要包含两部分内容
每个字的完整拼音每个字的拼音首字母的合并 {tokens : [{token : lian,start_offset : 0,end_offset : 0,type : word,position : 0},{token : lxsclnb,start_offset : 0,end_offset : 0,type : word,position : 0},{token : xi,start_offset : 0,end_offset : 0,type : word,position : 1},{token : shi,start_offset : 0,end_offset : 0,type : word,position : 2},{token : chang,start_offset : 0,end_offset : 0,type : word,position : 3},{token : liang,start_offset : 0,end_offset : 0,type : word,position : 4},{token : nian,start_offset : 0,end_offset : 0,type : word,position : 5},{token : ban,start_offset : 0,end_offset : 0,type : word,position : 6}]
}3. 自定义分词器
3.1 拼音分词器存在的问题
拼音分词器还无法正常用于生产环境因为拼音分词器存在一些问题
以 “练习时长两年半” 这句话为例拼音分词器存在以下问题
“练习时长两年半” 这句话没有被分词而是作为一个整体出现把 “练习时长两年半” 这句话中的每一个字都形成了一个拼音用处不大分词后的结果只剩下拼音没有汉字 其实我们很少使用拼音搜索大多数情况下我们都是使用中文去搜索的分词后有拼音只是锦上添花分词后的结果中汉字是必须保留的所以我们需要对拼音分词器做一些配置也就是自定义分词器
3.2 分词器analyzer的组成
ElasticSearch 中分词器analyzer的组成有三部分
character filters在 tokenizer 之前对文本进行处理例如删除字符、替换字符tokenizer将文本按照一定的规则切割成词条term例如 keyword不分词、ik_smart 等tokenizer filter将 tokenizer 输出的词条做进一步处理例如大小写转换、同义词处理、拼音处理等 3.3 如何自定义分词器 要想自定义分词器一定要在创建索引库的时候去设置 我们可以在创建索引库时通过 settings 来配置自定义的 analyzer分词器
自定义分词器时可以只设置分词器analyzer的某个部分 PUT /test
{settings: {analysis: {analyzer: {my_analyzer: {tokenizer: ik_max_word,filter: pinyin}}}}
}tokenizer 我们使用 ik_max_word先分词分好词后再将词条交给拼音分词器处理这样做可以解决拼音分词器没有分词的问题
但是拼音分词器还存在两个问题分词后的每一个字都形成了一个拼音、分词后的结果只剩下拼音没有汉字
3.4 拼音分词器的可选参数
我们需要对拼音分词器做进一步的定制
在拼音分词器的官网上给出了很多的可选参数Optional Parameters 参数名称含义keep_first_letter启用后只保留每个汉字的第一个字母。例如刘德华变为ldh。默认true。keep_separate_first_letter启用后保留每个汉字的第一个字母并分别显示。例如刘德华变为l,d,h。默认false。注意这可能会因词频增加查询的模糊度。limit_first_letter_length设置第一个字母结果的最大长度。默认16。keep_full_pinyin启用后保留每个汉字的完整拼音。例如刘德华变为[liu,de,hua]。默认true。keep_joined_full_pinyin启用后将每个汉字的完整拼音连接起来。例如刘德华变为[liudehua]。默认false。keep_none_chinese保留结果中的非汉字字母或数字。默认true。keep_none_chinese_together保留非汉字字母在一起。默认true。例如DJ音乐家变为DJ,yin,yue,jia。当设置为false时DJ音乐家变为D,J,yin,yue,jia。注意需要先启用keep_none_chinese。keep_none_chinese_in_first_letter在首字母中保留非汉字字母。例如刘德华AT2016变为ldhat2016。默认true。keep_none_chinese_in_joined_full_pinyin在连接的完整拼音中保留非汉字字母。例如刘德华2016变为liudehua2016。默认false。none_chinese_pinyin_tokenize如果非汉字字母是拼音将其拆分为单独的拼音词。默认true。例如liudehuaalibaba13zhuanghan变为liu,de,hua,a,li,ba,ba,13,zhuang,han。注意需要先启用keep_none_chinese和keep_none_chinese_together。keep_original启用后保留原始输入。默认false。lowercase将非汉字字母转换为小写。默认true。trim_whitespace默认true。remove_duplicated_term启用后移除重复的词以节省索引空间。例如de的变为de。默认false。注意可能与位置相关的查询受到影响。ignore_pinyin_offset在6.0版本之后偏移量受到严格限制不允许重叠的词。通过此参数将允许重叠的词忽略偏移量。请注意所有与位置相关的查询或高亮将变得不正确。如果需要偏移量请设置为false。默认true。
3.5 配置自定义分词器的tokenizer和filter PUT /test
{settings: {analysis: {analyzer: {my_analyzer: {tokenizer: ik_max_word,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}}}}
}创建一个自定义的分词器my_analyzer使用ik_max_word分词器进行中文分词并通过pinyin过滤器将中文词条转换为拼音保留了原始中文词条和连接起来的全拼同时限制了首字母长度并移除重复的词条
3.6 如何使用自定义分词器
自定义分词器创建好了之后该怎么使用呢
要使用自定义分词器我们需要在定义索引库字段Mapping的时候使用 PUT /test
{settings: {analysis: {analyzer: {my_analyzer: {tokenizer: ik_max_word,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: {name: {type: text,analyzer: my_analyzer}}}
}3.7 测试自定义分词器
3.7.1 直接测试
POST /test/_analyze
{text: [练习时长两年半],analyzer: my_analyzer
}测试结果 {tokens : [{token : 练习,start_offset : 0,end_offset : 2,type : CN_WORD,position : 0},{token : lianxi,start_offset : 0,end_offset : 2,type : CN_WORD,position : 0},{token : lx,start_offset : 0,end_offset : 2,type : CN_WORD,position : 0},{token : 时长,start_offset : 2,end_offset : 4,type : CN_WORD,position : 1},{token : shichang,start_offset : 2,end_offset : 4,type : CN_WORD,position : 1},{token : sc,start_offset : 2,end_offset : 4,type : CN_WORD,position : 1},{token : 两年,start_offset : 4,end_offset : 6,type : CN_WORD,position : 2},{token : liangnian,start_offset : 4,end_offset : 6,type : CN_WORD,position : 2},{token : ln,start_offset : 4,end_offset : 6,type : CN_WORD,position : 2},{token : 两,start_offset : 4,end_offset : 5,type : COUNT,position : 3},{token : liang,start_offset : 4,end_offset : 5,type : COUNT,position : 3},{token : l,start_offset : 4,end_offset : 5,type : COUNT,position : 3},{token : 年半,start_offset : 5,end_offset : 7,type : CN_WORD,position : 4},{token : nianban,start_offset : 5,end_offset : 7,type : CN_WORD,position : 4},{token : nb,start_offset : 5,end_offset : 7,type : CN_WORD,position : 4}]
}3.7.2 插入文档测试
测试数据如下狮子和虱子的拼音是一样的
POST /test/_doc/1
{id: 1,name: 狮子
}
POST /test/_doc/2
{id: 2,name: 虱子
}我们先通过拼音 shizi 来搜索
GET /test/_search
{query: {match: {name: shizi}}
}成功搜索出狮子和虱子 但如果我们搜索的内容是掉入狮子笼怎么办呢
GET /test/_search
{query: {match: {name: 掉入狮子笼怎么办}}
}从搜索结果中我们可以发现我们明明搜索的是狮子怎么虱子也搜索出来了
这说明我们自定义的分词器有问题在用拼音搜索时确实没问题但是在用中文搜索时却搜出了同音词
3.8 使用自定义分词器要注意的事项
拼音分词器适合在创建倒排索引的时候使用但不能在搜索的时候使用 创建倒排索引时 用户搜索狮子搜索结果中居然出现了虱子 所以我们在创建倒排索引时使用的分词器要和搜索时使用的分词器分开
怎么分开呢在创建倒排索引时使用 my_analyzer 分词器搜索时使用 ik_smart 分词器 PUT /test
{settings: {analysis: {analyzer: {my_analyzer: {tokenizer: ik_max_word,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: {name: {type: text,analyzer: my_analyzer,search_analyzer: ik_smart}}}
}我们删除 test 索引库之后重写创建 test 索引库进行测试
DELETE /testGET /test/_search
{query: {match: {name: 掉入狮子笼怎么办}}
}测试结果如下可以看到搜索结果中没有虱子了 4. DSL实现自动补全查询
ElasticSearch 提供了 Completion suggester 查询来实现自动补全功能这个查询会匹配以用户输入内容开头的词条并 返回
4.1 字段的类型的约束
为了提高补全查询的效率对于文档中字段的类型有一些约束
参与补全查询的字段必须是 completion 类型字段的内容一般是用来补全的多个词条形成的数组 4.2 查询语法 索引库
PUT test2
{mappings: {properties: {title:{type: completion}}}
}测试数据
POST test2/_doc
{title: [Sony, WH-1000XM3]
}POST test2/_doc
{title: [SK-II, PITERA]
}POST test2/_doc
{title: [Nintendo, switch]
}执行查询操作
POST /test2/_search
{suggest: {title_suggest: {text: s,completion: {field: title,skip_duplicates: true,size: 10}}}
}查询结果查询结果中包含了文档的原始信息 5. 自动补全案例
我们来做一个关于酒店数据的自动补全案例
5.1 准备工作
5.1.1 创建hotel索引库
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,search_analyzer: ik_smart}}}
}5.1.2 导入测试工程
测试工程的 Gitee 地址hotel-demo
5.1.3 导入酒店数据到数据库中
SQL 脚本在测试工程的 doc 目录下 5.1.4 将数据库中的数据导入到ElasticSearch
导入数据前更改与连接 ElasticSearch 相关的信息如果 ElasticSearch 没有设置密码可以去除 setHttpClientConfigCallback 代码 运行 HotelDocumentTest 测试类中的 testBulkRequest 方法将数据库中的数据导入到 ElasticSearch 在 Kibana 提供的控制台检查数据是否导入成功
GET /hotel/_search
{query: {match_all: {}}
}5.2 测试自动补全功能
在 Kibana 提供的控制台测试自动补全功能
GET /hotel/_search
{suggest: {suggestions: {text: s,completion: {field: suggestion,skip_duplicates: true,size: 10}}}
}测试结果 6. RestAPI实现自动补全查询
构建请求参数的 API 结果解析 import cn.itcast.hotel.service.IHotelService;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.search.suggest.Suggest;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.elasticsearch.search.suggest.SuggestBuilders;
import org.elasticsearch.search.suggest.completion.CompletionSuggestion;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import java.io.IOException;
import java.util.List;SpringBootTest
class HotelSuggestionTest {private RestHighLevelClient restHighLevelClient;Autowiredprivate IHotelService hotelService;Testvoid testSuggestion() throws IOException {// 1.准备SearchRequestSearchRequest searchRequest new SearchRequest(hotel);// 2.准备DSLsearchRequest.source().suggest(new SuggestBuilder().addSuggestion(suggestions,SuggestBuilders.completionSuggestion(suggestion).prefix(h).skipDuplicates(true).size(10)));// 3.发送请求SearchResponse searchResponse restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);// 4.解析结果// 4.1.获取suggest对象Suggest suggest searchResponse.getSuggest();// 4.2.根据名称获取suggestion对象CompletionSuggestion suggestion suggest.getSuggestion(suggestions);// 4.3.获取optionsListCompletionSuggestion.Entry.Option options suggestion.getOptions();// 4.4.遍历for (CompletionSuggestion.Entry.Option option : options) {System.out.println(option.getText().string() option.getText().string());}}BeforeEachvoid setUp() {// 用户名和密码String username elastic;String password tF8RGg2vd0FAzgkK;final BasicCredentialsProvider credentialsProvider new BasicCredentialsProvider();credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password));RestClientBuilder restClientBuilder RestClient.builder(new HttpHost(127.0.0.1, 9200, http)).setHttpClientConfigCallback(httpClientBuilder - httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider));restHighLevelClient new RestHighLevelClient(restClientBuilder);}AfterEachvoid tearDown() throws IOException {restHighLevelClient.close();}}7. 综合案例实现搜索框自动补全
测试工程已实现搜索框自动补全启动测试工程后在浏览器中查看搜索框的自动补全效果
http://localhost:8089/前端源代码的 Gitee 地址auto-complete