1、主要使用的技术及开发工具
- Elasticsearch 7.17.3
 
- REST API
 
- Elasticsearch java API Client 7.17.3
 
- Kibana 7.17.3
 
- Jackson 2.12.3
 
2、Elasticsearch简介
Elasticsearch 是一个分布式的免费开源搜索和分析引擎,适用于包括文本、数字、地理空间、结构化和非结构化数据等在内的所有类型的数据。可用于应用程序搜索,网站搜索等诸多使用场景。
使用ES,我们可以快速的开发一个自己的搜索引擎,ES会帮我们在庞大的数据中建索
3、mapping的设计思路
有四个数据要存进ES建索,分别是url、title、text、declareTime,他们的数据类型分别是keyword、text、text、date;其中date的格式为xxxx-xx-xx 其中text类型均采用ik_max_word保证最大限度的分词,为了实现搜索提示,可以定义title的第二个type为completion,方便自动补全。综上,新建索引的操作如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
   | PUT /link-repo2 {   "mappings": {     "properties": {       "url":{"type": "keyword"},       "title":{         "type": "text",         "analyzer": "ik_max_word",          "fields": {           "suggest": {             "type": "completion",             "analyzer": "ik_max_word"           }         }       },       "text":{         "type": "text",         "analyzer": "ik_max_word"       },       "declareTime": {         "type": "date"       }     }   } }
   | 
 
4、ES的搜索策略
4.1 Rest操作
在此课程设计中,我主要采用match分词搜索,分别在title和text中搜索,并附带相关的highlight和filler策略以作为高亮显示和按时间范围搜索的基础。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
   | GET link-repo2/_search {   "query": {     "bool": {       "must": [         {           "multi_match": {             "query": "软银机器人杯”2019中国机器人技能大赛:我院学子获1项季军、1项二等奖",             "fields": ["title", "text"],             "analyzer": "ik_smart"           }         }       ],       "filter": [         {           "range": {             "declareTime": {               "gte": "2022-01-01"             }           }         }       ]     }   },   "highlight": {     "pre_tags": "<span class=\"hit-result\">",     "post_tags": "</span>",     "fields": {       "title": {},       "text": {}     }   },   "from": 0,   "size": 10 }
   | 
 
4.2 全文检索功能
multi_match表示多字段的分词搜索,query为搜索内容,fields为搜索字段,analyzer表示搜索文本的分词器。
4.3 按时间范围检索功能
在bool块里,通过must和filter的组合,must实现分词搜索,filter实现过滤时间;range块中的field为字段,gte表示大于等于。这样就可实现时间范围检索
4.4 高亮检索
highlight,搜索中关注title和text,当有匹配分词出现,就套上pre_tags和post_tags标签,“高亮”出来。这样就可以实现高亮查询的功能
4.5 分页搜索
from和size的组合,可以实现分页功能
5、使用Elasticsearch java API Client连接ES
团队项目的ES是有使用xpack插件保证安全性的,连接的时候要创建使用许可证才能成功连接
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
   | public static ElasticsearchClient getConnect() {          final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();     credentialsProvider.setCredentials(         AuthScope.ANY, new UsernamePasswordCredentials(USERNAME, PASSWORD));          RestClientBuilder builder = RestClient.builder(new HttpHost(URL, PORT))         .setHttpClientConfigCallback(httpAsyncClientBuilder -> httpAsyncClientBuilder                                     .setDefaultCredentialsProvider(credentialsProvider));          restClient = builder.build();     transport = new RestClientTransport(         restClient, new JacksonJsonpMapper());     return new ElasticsearchClient(transport); }
  | 
 
6、代码实现
Search.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
   | package es;
  import bean.ResultEntry;
  import java.io.Reader; import java.util.List;
 
 
 
 
 
  public interface Search {     
 
 
      long getSearchCount();
      
 
 
 
      boolean newIndex(Reader reader);
      
 
 
      boolean deleteIndex();
      
 
 
 
      ResultEntry add(ResultEntry entry);
      
 
 
 
      List<ResultEntry> search(String searchText);
      
 
 
 
 
      List<ResultEntry> search(String searchText, int page);
      
 
 
 
 
 
      List<ResultEntry> search(String searchText, int page, String beginDate);
      
 
 
 
 
 
 
      List<ResultEntry> search(String searchText, int page, String beginDate, String endDate);
      
 
 
 
      List<String> getSearchSuggest(String prefix);     
 
      void close();    }
   | 
 
新建索引
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
   | public boolean newIndex(Reader reader) {     CreateIndexRequest createIndexRequest = new CreateIndexRequest.Builder()         .withJson(reader)         .index(EsUtil.index)         .build();
      CreateIndexResponse response = null;     try {         response = client.indices().create(createIndexRequest);     } catch (Exception e) {              }     if(response != null) {         return Objects.requireNonNullElse(response.acknowledged(), false);     } else {         return false;     } }
  | 
 
删除索引
1 2 3 4 5 6 7 8 9 10 11 12 13 14
   | public boolean deleteIndex() {     DeleteIndexResponse deleteIndexResponse = null;     try {         deleteIndexResponse = client.indices().delete(d -> d                                                       .index(EsUtil.index));     } catch (Exception e) {              }     if(deleteIndexResponse == null) {         return false;     } else {         return deleteIndexResponse.acknowledged();     } }
  | 
 
添加文档
1 2 3 4 5 6 7 8 9
   | public ResultEntry add(ResultEntry entry) {         try {             client.index(i -> i                     .index(EsUtil.index).document(entry));         } catch (IOException e) {             return null;         }         return entry;     }
  | 
 
全文检索
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
   | public List<ResultEntry> search(String searchText, int page) {          int value = (page - 1) * 10;     SearchResponse<ResultEntry> search = null;
      try {         search = client.search(s -> s                                .index(EsUtil.index)                                .query(q -> q                                       .multiMatch(m -> m                                                   .query(searchText)                                                   .fields("title", "text")                                                   .analyzer("ik_smart")))                                .highlight(h -> h                                           .preTags("<span class=\"hit-result\">")                                           .postTags("</span>")                                           .fields("title", builder -> builder)                                           .fields("text", builder -> builder))                                .from(value)                                .size(10)                                , ResultEntry.class);     } catch (IOException e) {         e.printStackTrace();     }
      return dealSearchResponse(search); }
  | 
 
按时间范围全文检索
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
   | public List<ResultEntry> search(String searchText, int page, String beginDate) {          int value = (page - 1) * 10;     JsonData jsonBeginDate = JsonData.of(beginDate);     SearchResponse<ResultEntry> search = null;     try {         search = client.search(s -> s                                .index(EsUtil.index)                                .query(q -> q                                       .bool(b -> b                                             .must(b1 -> b1                                                   .multiMatch(b2 -> b2                                                               .query(searchText)                                                               .fields("title", "text")                                                               .analyzer("ik_smart")))                                             .filter(b3 -> b3                                                     .range(b4 -> b4                                                            .field("declareTime")                                                            .gte(jsonBeginDate)))))                                .highlight(h -> h                                           .preTags("<span class=\"hit-result\">")                                           .postTags("</span>")                                           .fields("title", builder -> builder)                                           .fields("text", builder -> builder))                                .from(value)                                .size(10)                                , ResultEntry.class);     } catch (IOException e) {         e.printStackTrace();     }
      return dealSearchResponse(search); }
  | 
 
7、参考链接
官方新版java API文档
新版java api 操作示例
ES英文文档
ES中文文档
ES设置账号密码
Java连接ES有账号密码的情况