经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » Elasticsearch » 查看文章
ElasticSearch 实现分词全文检索 - 搜素关键字自动补全(Completion Suggest)
来源:cnblogs  作者:VipSoft  时间:2023/3/22 9:24:11  对本文有异议

目录

ElasticSearch 实现分词全文检索 - 概述
ElasticSearch 实现分词全文检索 - ES、Kibana、IK安装
ElasticSearch 实现分词全文检索 - Restful基本操作
ElasticSearch 实现分词全文检索 - Java SpringBoot ES 索引操作
ElasticSearch 实现分词全文检索 - Java SpringBoot ES 文档操作
ElasticSearch 实现分词全文检索 - 测试数据准备
ElasticSearch 实现分词全文检索 - term、terms查询
ElasticSearch 实现分词全文检索 - match、match_all、multimatch查询
ElasticSearch 实现分词全文检索 - id、ids、prefix、fuzzy、wildcard、range、regexp 查询
ElasticSearch 实现分词全文检索 - Scroll 深分页
ElasticSearch 实现分词全文检索 - delete-by-query
ElasticSearch 实现分词全文检索 - 复合查询
ElasticSearch 实现分词全文检索 - filter查询
ElasticSearch 实现分词全文检索 - 高亮查询
ElasticSearch 实现分词全文检索 - 聚合查询 cardinality
ElasticSearch 实现分词全文检索 - 经纬度查询
ElasticSearch 实现分词全文检索 - 搜素关键字自动补全(suggest)
ElasticSearch 实现分词全文检索 - SpringBoot 完整实现 Demo 附源码

需求

搜素关键字自动补全(suggest)
输入“人工” 自动带出人工开头的关键字
image

Kibana 界面操作 实现 搜素关键字自动补全(suggest)

ES使用Completion Suggest 做关键字自动补全时,实际应用中搜索性能更加高效,建议多开一个子字段,如下示例,假设要根据title字段做关键字自动补全,不要改原字段的类型,多开一个子字段title.suggest,类型设置为completion,然后之后的suggest针对title.suggest字段做操作

  • Term Suggester:词条建议器。对给输入的文本进进行分词,为每个分词提供词项建议, 基于编辑距离,对analyze过的单个term去提供建议,并不会考虑多个term/词组之间的关系。quert -> query
  • Phrase Suggester:短语建议器,在term的基础上,会考量多个term之间的关系在Term Suggester的基础上,通过ngram以词组为单位返回建议。noble prize -> nobel prize
  • Completion Suggester:它主要针对的应用场景就是"Auto Completion",FST数据结构,类似Trie树,不用打开倒排,快速返回,前缀匹配
  • Context Suggester:上下文建议器,在Completion Suggester的基础上,用于filter和boost

创建索引

  1. ## 创建索引并指定结构
  2. PUT /article-index
  3. {
  4. "settings": {
  5. "number_of_shards": 3,
  6. "number_of_replicas": 0
  7. },
  8. "mappings": {
  9. "properties":{
  10. "id":{
  11. "type":"keyword"
  12. },
  13. "title":{
  14. "type":"text",
  15. "analyzer":"ik_max_word",
  16. "fields": { # 扩展一个字段,用于关键字自动补全查询
  17. "suggest" : {
  18. "type" : "completion",
  19. "analyzer": "ik_max_word"
  20. }
  21. }
  22. },
  23. "summary":{
  24. "type":"text",
  25. "analyzer":"ik_max_word"
  26. },
  27. "createDate":{
  28. "type":"date",
  29. "format":"yyyy-MM-dd HH:mm:ss||yyyy-MM-dd"
  30. }
  31. }
  32. }
  33. }

添加数据

JSON { 括号里面的内容,不能换行 }

  1. # _bulk 批量添加文档
  2. POST /article-index/_doc/_bulk
  3. {"index":{"_id":1}}
  4. {"id":1,"title":"人工智能技术","summary":"ElasticSearch 实现分词全文检索 - ES、Kibana、IK安装","createDate":"2023-02-23"}
  5. {"index":{"_id":2}}
  6. {"id":2,"title":"人工智能软件 Chart GTP","summary":"太极生两仪,两仪生四象,四象生八卦","createDate":"2023-02-23"}
  7. {"index":{"_id":3}}
  8. {"id":3,"title":"Restful基本操作","summary":"ElasticSearch 实现分词全文检索 - Java SpringBoot ES 索引操作","createDate":"2023-02-23"}
  9. {"index":{"_id":4}}
  10. {"id":4,"title":"人工呼吸","summary":"ElasticSearch 实现分词全文检索 - 经纬度查询","createDate":"2023-02-23"}
  11. {"index":{"_id":5}}
  12. {"id":5,"title":"SpringBoot 全文检索实战","summary":"ElasticSearch 实现分词全文检索 - SpringBoot 全文检索实战","createDate":"2023-02-23"}

查询数据

  1. ## 查询
  2. GET /article-index/_doc/_search
  3. {
  4. "suggest": {
  5. "my-suggest" : {
  6. "prefix" : "人",
  7. "completion" : {
  8. "field" : "title.suggest"
  9. }
  10. }
  11. }
  12. }

返回值--自动带出人开头的关键字

  1. {
  2. "took" : 3,
  3. "timed_out" : false,
  4. "_shards" : {
  5. "total" : 3,
  6. "successful" : 3,
  7. "skipped" : 0,
  8. "failed" : 0
  9. },
  10. "hits" : {
  11. "total" : {
  12. "value" : 0,
  13. "relation" : "eq"
  14. },
  15. "max_score" : null,
  16. "hits" : [ ]
  17. },
  18. "suggest" : {
  19. "my-suggest" : [
  20. {
  21. "text" : "人",
  22. "offset" : 0,
  23. "length" : 1,
  24. "options" : [
  25. {
  26. "text" : "人工呼吸",
  27. "_index" : "article-index",
  28. "_type" : "_doc",
  29. "_id" : "4",
  30. "_score" : 1.0,
  31. "_source" : {
  32. "id" : 4,
  33. "title" : "人工呼吸",
  34. "summary" : "ElasticSearch 实现分词全文检索 - 经纬度查询",
  35. "createDate" : "2023-02-23"
  36. }
  37. },
  38. {
  39. "text" : "人工智能技术",
  40. "_index" : "article-index",
  41. "_type" : "_doc",
  42. "_id" : "1",
  43. "_score" : 1.0,
  44. "_source" : {
  45. "id" : 1,
  46. "title" : "人工智能技术",
  47. "summary" : "ElasticSearch 实现分词全文检索 - ES、Kibana、IK安装",
  48. "createDate" : "2023-02-23"
  49. }
  50. },
  51. {
  52. "text" : "人工智能软件 Chart GTP",
  53. "_index" : "article-index",
  54. "_type" : "_doc",
  55. "_id" : "2",
  56. "_score" : 1.0,
  57. "_source" : {
  58. "id" : 2,
  59. "title" : "人工智能软件 Chart GTP",
  60. "summary" : "太极生两仪,两仪生四象,四象生八卦",
  61. "createDate" : "2023-02-23"
  62. }
  63. }
  64. ]
  65. }
  66. ]
  67. }
  68. }

JAVA SpringBoot 实现 搜素关键字自动补全(suggest)

创建索引

  1. /**
  2. * 第一步:系统初始化,创建索引
  3. * 如果索引不存在,创建,输出
  4. */
  5. @Test
  6. void createIndexTest() throws Exception {
  7. boolean indexExists = elasticSearchUtil.indexExists(INDEX_NAME);
  8. if (!indexExists) {
  9. try {
  10. createIndex(INDEX_NAME);
  11. logger.info("索引【{}】,创建成功", INDEX_NAME);
  12. //测试效果 可再次查询验证。
  13. indexExists = elasticSearchUtil.indexExists(INDEX_NAME);
  14. logger.info("索引【{}】, {}", INDEX_NAME, indexExists ? "验证存在" : "验证不存在");
  15. } catch (Exception e) {
  16. logger.error(e.getMessage(), e);
  17. }
  18. } else {
  19. logger.info("索引【{}】已存在,无需创建", INDEX_NAME);
  20. }
  21. }
  22. /**
  23. * 创建索引
  24. *
  25. * @param indexName
  26. * @throws Exception
  27. */
  28. void createIndex(String indexName) throws Exception {
  29. //准备索引的 settings
  30. Settings.Builder settings = Settings.builder()
  31. .put("number_of_shards", INDEX_NUMBER_OF_SHARDS) //分片数,可以使用常量
  32. .put("number_of_replicas", esProperties.getReplicasNum()); //是否集群,需要多少副本,在配置文件中配置
  33. //准备索引的结构 Mappings
  34. XContentBuilder mappings = JsonXContent.contentBuilder()
  35. .startObject()
  36. .startObject("properties")
  37. .startObject("id").field("type", "keyword").endObject()
  38. .startObject("title").field("type", "text").field("analyzer", "ik_max_word")
  39. .startObject("fields").startObject("suggest").field("type", "completion").field("analyzer", "ik_max_word").endObject().endObject()
  40. .endObject() //对该字段进行分词
  41. .startObject("summary").field("type", "text").field("analyzer", "ik_max_word").endObject() //对该字段进行分词
  42. .startObject("createDate").field("type", "date").field("format", "yyyy-MM-dd HH:mm:ss").endObject()
  43. .endObject()
  44. .endObject();
  45. CreateIndexResponse resp = elasticSearchUtil.createIndex(indexName, settings, mappings);
  46. //输出
  47. logger.info("CreateIndexResponse => {} ", resp.toString());
  48. }

添加数据

  1. /**
  2. * 第二步:模拟后台管理员,在添加文章时,将要检查的字段内容,同步到ES中
  3. */
  4. @Test
  5. void addArticleTest() throws Exception {
  6. Map<Integer, String> titleMap = new HashMap<>();
  7. titleMap.put(1, "人工智能技术");
  8. titleMap.put(2, "人工智能软件 Chart GTP");
  9. titleMap.put(3, "Restful基本操作");
  10. titleMap.put(4, "Java SpringBoot ES 索引操作");
  11. titleMap.put(5, "Java SpringBoot ES 文档操作");
  12. titleMap.put(6, "人工呼吸");
  13. titleMap.put(7, "SpringBoot 全文检索实战");
  14. Map<Integer, String> introMap = new HashMap<>();
  15. introMap.put(1, "ElasticSearch 实现分词全文检索 - 概述");
  16. introMap.put(2, "ElasticSearch 实现分词全文检索 - ES、Kibana、IK安装");
  17. introMap.put(3, "ElasticSearch 实现分词全文检索 - Restful基本操作");
  18. introMap.put(4, "ElasticSearch 实现分词全文检索 - Java SpringBoot ES 索引操作");
  19. introMap.put(5, "ElasticSearch 实现分词全文检索 - Java SpringBoot ES 文档操作");
  20. introMap.put(6, "ElasticSearch 实现分词全文检索 - 经纬度查询");
  21. introMap.put(7, "ElasticSearch 实现分词全文检索 - SpringBoot 全文检索实战");
  22. //内容
  23. Map<Integer, String> contentMap = new HashMap<>();
  24. contentMap.put(1, "【阿里云】尊敬的vipsoft:您有2台云服务器ECS配置升级成功。如有CPU、内存变更或0Mbps带宽升级,您需要在ECS控制台手动重启云服务器后才能生效。");
  25. contentMap.put(2, "为更好地为您提供服务,温馨提醒:您本月有1次抽奖机会,赢取大额通用流量,月月抽月月领,点击掌厅链接 原URL:http://wap.js.10086.cn/Mq 快来试试你的运气吧,如本月已参与请忽略【江苏移动心级服务,让爱连接】");
  26. contentMap.put(3, "国家反诈中心提醒:公检法机关会当面向涉案人员出示证件或法律文书,绝对不会通过网络给当事人发送通缉令、拘留证、逮捕证等法律文书,并要求转账汇款。\n" +
  27. "切记:公检法机关不存在所谓“安全账户”,更不会让你远程转账汇款!");
  28. contentMap.put(4, "【江苏省公安厅、江苏省通信管理局】温馨提示:近期利用苹果手机iMessage消息冒充熟人、冒充领导换号、添加新微信号等诈骗形式多发。如有收到类似短信,请您谨慎判断,苹果手机用户如无需要可关闭iMessage功能,以免上当受骗。");
  29. contentMap.put(5, "多一点快乐,少一点懊恼,不管钞票有多少,只有天天开心就好,累了就睡觉,生活的甜苦,自己来调味。收到信息就要开心的笑");
  30. contentMap.put(6, "黄金周好运每天交,我把祝福来送到:愿您生活步步高,彩票期期中,股票每天涨,生意年年旺,祝您新年新景象!");
  31. contentMap.put(7, "【阿里云】当你手机响,那是我的问候;当你收到短信,那有我的心声;当你翻阅短信,那有我的牵挂;当你筹备关机时,记得我今天说过周末快乐!");
  32. contentMap.put(8, "我刚去了一趟银行,取了无数的幸福黄金好运珠宝平安翡翠成功股票健康基金。嘘!别作声,统统的送给你,因为我想提“钱”祝你国庆节快乐!");
  33. contentMap.put(9, "一个人的精彩,一个人的打拼,一个人的承载,一个人的舞蹈。光棍节送你祝福,不因你是光棍,只因你生活色彩。祝你:快乐打拼,生活出彩!");
  34. contentMap.put(10, "爆竹响激情燃放,雪花舞祥风欢畅,烟火腾期待闪亮,感动涌心中激荡,心情美春节冲浪,愿景好心中珍藏,祝与福短信奉上:祝您身体健康,兔年吉祥!");
  35. //模似7次 添加文章
  36. for (int i = 1; i <= 7; i++) {
  37. ArticleInfo article = new ArticleInfo();
  38. article.setId(String.valueOf(i));
  39. article.setTitle(titleMap.get(i));
  40. article.setAuthor("VipSoft");
  41. article.setSummary(introMap.get(i));
  42. article.setContent(contentMap.get(i));
  43. article.setCreateTime(new Date());
  44. //将article 保存到 MySQL --- 省略
  45. boolean flag = true; //保存数据到 MySQL 数据库成功
  46. if (flag) {
  47. //将需要查询的数据,赋给DTO,更新到 ES中
  48. ArticleDTO articleDTO = new ArticleDTO();
  49. BeanUtils.copyProperties(article, articleDTO);
  50. String json = JSON.toJSONStringWithDateFormat(articleDTO, "yyyy-MM-dd HH:mm:ss"); //FastJson 将日期格式化
  51. IndexResponse resp = elasticSearchUtil.createDoc(INDEX_NAME, articleDTO.getId(), json);
  52. logger.info(" {}", resp.getResult().toString());
  53. }
  54. }
  55. }

查询数据

  1. /**
  2. * 第三步:模拟用户搜索,输入关键词“人”,带出和人有关的关键词
  3. */
  4. @Test
  5. void earchTest() throws Exception {
  6. List<String> resp = elasticSearchUtil.suggest(INDEX_NAME, "title.suggest", "人", 2);
  7. //4. 获取到 _source 中的数据,并展示
  8. for (String hit : resp) {
  9. System.out.println(hit);
  10. }
  11. }
  12. /**
  13. * 自动补全 根据用户的输入联想到可能的词或者短语
  14. *
  15. * @param indexName 索引名称
  16. * @param field 搜索条件字段
  17. * @param keywords 搜索关键字
  18. * @param size 匹配数量
  19. * @return
  20. * @throws Exception
  21. */
  22. public List<String> suggest(String indexName, String field, String keywords, int size) throws Exception {
  23. //定义返回
  24. List<String> suggestList = new ArrayList<>();
  25. //构建查询请求
  26. SearchRequest searchRequest = new SearchRequest(indexName);
  27. //通过查询构建器定义评分排序
  28. SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
  29. searchSourceBuilder.sort(new ScoreSortBuilder().order(SortOrder.DESC));
  30. //构造搜索建议语句,搜索条件字段
  31. CompletionSuggestionBuilder completionSuggestionBuilder = new CompletionSuggestionBuilder(field);
  32. //搜索关键字
  33. completionSuggestionBuilder.prefix(keywords);
  34. //去除重复
  35. completionSuggestionBuilder.skipDuplicates(true);
  36. //匹配数量
  37. completionSuggestionBuilder.size(size);
  38. searchSourceBuilder.suggest(new SuggestBuilder().addSuggestion("my-suggest", completionSuggestionBuilder));
  39. //czbk-suggest为返回的字段,所有返回将在czbk-suggest里面,可写死,sort按照评分排序
  40. searchRequest.source(searchSourceBuilder);
  41. //定义查找响应
  42. SearchResponse suggestResponse = esClient.search(searchRequest, RequestOptions.DEFAULT);
  43. //定义完成建议对象
  44. CompletionSuggestion completionSuggestion = suggestResponse.getSuggest().getSuggestion("my-suggest");
  45. List<CompletionSuggestion.Entry.Option> optionsList = completionSuggestion.getEntries().get(0).getOptions();
  46. //从optionsList取出结果
  47. if (!CollectionUtils.isEmpty(optionsList)) {
  48. optionsList.forEach(item -> suggestList.add(item.getText().toString()));
  49. }
  50. return suggestList;
  51. }

image

ElasticSearchUtil 代码见 - SpringBoot 完整实现 Demo 附源码

原文链接:https://www.cnblogs.com/vipsoft/p/17221642.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号