java中的ElasticSearch搜索引擎介绍。
ElasticSearch是一个基于Lucene的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。Elasticsearch是用Java开发的,并作为Apache许可条款下的开放源码发布,是当前流行的企业级搜索引擎。设计用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。
安装
以windows操作系统和ES0.19.7版本为例:
①下载elasticsearch-6.4.1.zip
②直接解压至某目录,设置该目录为ES_HOME环境变量
③安装JDK,并设置JAVA_HOME环境变量
④在windows下,运行 %ES_HOME%\bin\elasticsearch.bat即可运行
安装插件
以head插件为例:
联网时,直接运行%ES_HOME%\bin\plugin --install mobz/elasticsearch-head
不联网时,下载elasticsearch-head的zipball的master包,然后运行%ES_HOME%\bin\plugin --url file:///[path-to-downloadfile] --install head,其中[path-to-downloadfile]是下载后master包的绝对路径
安装完成,重启服务,在浏览器打开 http://localhost:9200/_plugin/head/ 即可
ES工作原理
当ElasticSearch的节点启动后,它会利用多播(multicast)(或者单播,如果用户更改了配置)寻找集群中的其它节点,并与之建立连接。这个过程如下图所示:
官方代码
1 RestHighLevelClient client = new RestHighLevelClient(RestClient.builder( 2 new HttpHost("localhost", 9200, "http"))); 3 SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); 4 searchSourceBuilder.query(QueryBuilders.matchAllQuery()); 5 searchSourceBuilder.aggregation(AggregationBuilders.terms("top_10_states").field("state").size(10)); 6 SearchRequest searchRequest = new SearchRequest(); 7 searchRequest.indices("social-*"); 8 searchRequest.source(searchSourceBuilder); 9 SearchResponse searchResponse = client.search(searchRequest);
创建索引
1 /** 2 * 创建索引 3 * 4 * @param index 5 * @return 6 */ 7 public boolean createIndex(String index) { 8 if (!isIndexExist(index)) { 9 logger.info("index is not exits!"); 10 } 11 CreateIndexResponse indexresponse = client.admin().indices().prepareCreate(index).execute().actionGet(); 12 logger.info("success to create index " + indexresponse.isAcknowledged()); 13 14 return indexresponse.isAcknowledged(); 15 }
删除索引
1 /** 2 * 删除索引 3 * 4 * @param index 5 * @return 6 */ 7 public boolean deleteIndex(String index) { 8 if (!isIndexExist(index)) { 9 logger.info("index is not exits!"); 10 } 11 DeleteIndexResponse dResponse = client.admin().indices().prepareDelete(index).execute().actionGet(); 12 if (dResponse.isAcknowledged()) { 13 logger.info("delete index " + index + " successfully!"); 14 } else { 15 logger.info("fail to delete index " + index); 16 } 17 return dResponse.isAcknowledged(); 18 }
判断
1 /** 2 * 判断索引是否存在 3 * 4 * @param index 5 * @return 6 */ 7 public boolean isIndexExist(String index) { 8 IndicesExistsResponse inExistsResponse = client.admin().indices().exists(new IndicesExistsRequest(index)).actionGet(); 9 if (inExistsResponse.isExists()) { 10 logger.info("index [" + index + "] is exist!"); 11 } else { 12 logger.info("index [" + index + "] is not exist!"); 13 } 14 return inExistsResponse.isExists(); 15 }
1 /** 2 * 通过ID获取数据 3 * 4 * @param index 索引,类似数据库 5 * @param type 类型,类似表 6 * @param id 数据ID 7 * @param fields 需要显示的字段,逗号分隔(缺省为全部字段) 8 * @return 9 */ 10 public Map<String, Object> searchDataById(String index, String type, String id, String fields) { 11 GetRequestBuilder getRequestBuilder = client.prepareGet(index, type, id); 12 if (StringUtils.isNotEmpty(fields)) { 13 getRequestBuilder.setFetchSource(fields.split(","), null); 14 } 15 GetResponse getResponse = getRequestBuilder.execute().actionGet(); 16 return getResponse.getSource(); 17 } 18 19 /** 20 * 使用分词查询 21 * 22 * @param index 索引名称 23 * @param type 类型名称,可传入多个type逗号分隔 24 * @param fields 需要显示的字段,逗号分隔(缺省为全部字段) 25 * @param matchStr 过滤条件(xxx=111,aaa=222) 26 * @return 27 */ 28 public List<Map<String, Object>> searchListData(String index, String type, String fields, String matchStr) { 29 return searchListData(index, type, 0, 0, null, fields, null, false, null, matchStr); 30 } 31 32 /** 33 * 使用分词查询 34 * 35 * @param index 索引名称 36 * @param type 类型名称,可传入多个type逗号分隔 37 * @param fields 需要显示的字段,逗号分隔(缺省为全部字段) 38 * @param sortField 排序字段 39 * @param matchPhrase true 使用,短语精准匹配 40 * @param matchStr 过滤条件(xxx=111,aaa=222) 41 * @return 42 */ 43 public List<Map<String, Object>> searchListData(String index, String type, String fields, String sortField, boolean matchPhrase, String matchStr) { 44 return searchListData(index, type, 0, 0, null, fields, sortField, matchPhrase, null, matchStr); 45 } 46 47 48 /** 49 * 使用分词查询 50 * 51 * @param index 索引名称 52 * @param type 类型名称,可传入多个type逗号分隔 53 * @param size 文档大小限制 54 * @param fields 需要显示的字段,逗号分隔(缺省为全部字段) 55 * @param sortField 排序字段 56 * @param matchPhrase true 使用,短语精准匹配 57 * @param highlightField 高亮字段 58 * @param matchStr 过滤条件(xxx=111,aaa=222) 59 * @return 60 */ 61 public List<Map<String, Object>> searchListData(String index, String type, Integer size, String fields, String sortField, boolean matchPhrase, String highlightField, String matchStr) { 62 return searchListData(index, type, 0, 0, size, fields, sortField, matchPhrase, highlightField, matchStr); 63 } 64 65 66 /** 67 * 使用分词查询 68 * 69 * @param index 索引名称 70 * @param type 类型名称,可传入多个type逗号分隔 71 * @param startTime 开始时间 72 * @param endTime 结束时间 73 * @param size 文档大小限制 74 * @param fields 需要显示的字段,逗号分隔(缺省为全部字段) 75 * @param sortField 排序字段 76 * @param matchPhrase true 使用,短语精准匹配 77 * @param highlightField 高亮字段 78 * @param matchStr 过滤条件(xxx=111,aaa=222) 79 * @return 80 */ 81 public List<Map<String, Object>> searchListData(String index, String type, long startTime, long endTime, Integer size, String fields, String sortField, boolean matchPhrase, String highlightField, String matchStr) { 82 SearchRequestBuilder searchRequestBuilder = client.prepareSearch(index); 83 if (StringUtils.isNotEmpty(type)) { 84 searchRequestBuilder.setTypes(type.split(",")); 85 } 86 BoolQueryBuilder boolQuery = QueryBuilders.boolQuery(); 87 88 if (startTime > 0 && endTime > 0) { 89 boolQuery.must(QueryBuilders.rangeQuery("processTime") 90 .format("epoch_millis") 91 .from(startTime) 92 .to(endTime) 93 .includeLower(true) 94 .includeUpper(true)); 95 } 96 97 //搜索的的字段 98 if (StringUtils.isNotEmpty(matchStr)) { 99 for (String s : matchStr.split(",")) { 100 String[] ss = s.split("="); 101 if (ss.length > 1) { 102 if (matchPhrase == Boolean.TRUE) { 103 boolQuery.must(QueryBuilders.matchPhraseQuery(s.split("=")[0], s.split("=")[1])); 104 } else { 105 boolQuery.must(QueryBuilders.matchQuery(s.split("=")[0], s.split("=")[1])); 106 } 107 } 108 } 109 } 110 111 // 高亮(xxx=111,aaa=222) 112 if (StringUtils.isNotEmpty(highlightField)) { 113 HighlightBuilder highlightBuilder = new HighlightBuilder(); 114 115 //highlightBuilder.preTags("<span style='color:red' >");//设置前缀 116 //highlightBuilder.postTags("</span>");//设置后缀 117 118 // 设置高亮字段 119 highlightBuilder.field(highlightField); 120 searchRequestBuilder.highlighter(highlightBuilder); 121 } 122 123 searchRequestBuilder.setQuery(boolQuery); 124 125 if (StringUtils.isNotEmpty(fields)) { 126 searchRequestBuilder.setFetchSource(fields.split(","), null); 127 } 128 searchRequestBuilder.setFetchSource(true); 129 130 if (StringUtils.isNotEmpty(sortField)) { 131 searchRequestBuilder.addSort(sortField, SortOrder.DESC); 132 } 133 134 if (size != null && size > 0) { 135 searchRequestBuilder.setSize(size); 136 } 137 138 //打印的内容 可以在 Elasticsearch head 和 Kibana 上执行查询 139 // logger.info("\n{}", searchRequestBuilder); 140 141 SearchResponse searchResponse = searchRequestBuilder.execute().actionGet(); 142 143 long totalHits = searchResponse.getHits().totalHits; 144 long length = searchResponse.getHits().getHits().length; 145 146 // logger.info("共查询到[{}]条数据,处理数据条数[{}]", totalHits, length); 147 148 if (searchResponse.status().getStatus() == 200) { 149 // 解析对象 150 return setSearchResponse(searchResponse, highlightField); 151 } 152 return null; 153 } 154 155 /** 156 * 使用分词查询,并分页 157 * 158 * @param index 索引名称 159 * @param type 类型名称,可传入多个type逗号分隔 160 * @param currentPage 当前页 161 * @param pageSize 每页显示条数 162 * @param startTime 开始时间 163 * @param endTime 结束时间 164 * @param fields 需要显示的字段,逗号分隔(缺省为全部字段) 165 * @param sortField 排序字段 166 * @param matchPhrase true 使用,短语精准匹配 167 * @param highlightField 高亮字段 168 * @param matchStr 过滤条件(xxx=111,aaa=222) 169 * @return 170 */ 171 public EsPage searchDataPage(String index, String type, int currentPage, int pageSize, long startTime, long endTime, String fields, String sortField, boolean matchPhrase, String highlightField, String matchStr) { 172 SearchRequestBuilder searchRequestBuilder = client.prepareSearch(index); 173 if (StringUtils.isNotEmpty(type)) { 174 searchRequestBuilder.setTypes(type.split(",")); 175 } 176 searchRequestBuilder.setSearchType(SearchType.QUERY_THEN_FETCH); 177 178 // 需要显示的字段,逗号分隔(缺省为全部字段) 179 if (StringUtils.isNotEmpty(fields)) { 180 searchRequestBuilder.setFetchSource(fields.split(","), null); 181 } 182 183 //排序字段 184 if (StringUtils.isNotEmpty(sortField)) { 185 searchRequestBuilder.addSort(sortField, SortOrder.DESC); 186 } 187 188 BoolQueryBuilder boolQuery = QueryBuilders.boolQuery(); 189 190 if (startTime > 0 && endTime > 0) { 191 boolQuery.must(QueryBuilders.rangeQuery("@timestamp") 192 .format("epoch_millis") 193 .from(startTime) 194 .to(endTime) 195 .includeLower(true) 196 .includeUpper(true)); 197 } 198 199 // 查询字段 200 if (StringUtils.isNotEmpty(matchStr)) { 201 for (String s : matchStr.split(",")) { 202 String[] ss = s.split("="); 203 if (ss.length > 1) { 204 if (matchPhrase == Boolean.TRUE) { 205 boolQuery.must(QueryBuilders.matchPhraseQuery(s.split("=")[0], s.split("=")[1])); 206 } else { 207 boolQuery.must(QueryBuilders.matchQuery(s.split("=")[0], s.split("=")[1])); 208 } 209 } 210 } 211 } 212 213 // 高亮(xxx=111,aaa=222) 214 if (StringUtils.isNotEmpty(highlightField)) { 215 HighlightBuilder highlightBuilder = new HighlightBuilder(); 216 217 //highlightBuilder.preTags("<span style='color:red' >");//设置前缀 218 //highlightBuilder.postTags("</span>");//设置后缀 219 220 // 设置高亮字段 221 highlightBuilder.field(highlightField); 222 searchRequestBuilder.highlighter(highlightBuilder); 223 } 224 225 searchRequestBuilder.setQuery(QueryBuilders.matchAllQuery()); 226 searchRequestBuilder.setQuery(boolQuery); 227 228 // 分页应用 229 searchRequestBuilder.setFrom(currentPage).setSize(pageSize); 230 231 // 设置是否按查询匹配度排序 232 searchRequestBuilder.setExplain(true); 233 234 //打印的内容 可以在 Elasticsearch head 和 Kibana 上执行查询 235 // logger.info("\n{}", searchRequestBuilder); 236 237 // 执行搜索,返回搜索响应信息 238 SearchResponse searchResponse = searchRequestBuilder.execute().actionGet(); 239 240 long totalHits = searchResponse.getHits().totalHits; 241 long length = searchResponse.getHits().getHits().length; 242 243 // logger.debug("共查询到[{}]条数据,处理数据条数[{}]", totalHits, length); 244 245 if (searchResponse.status().getStatus() == 200) { 246 // 解析对象 247 List<Map<String, Object>> sourceList = setSearchResponse(searchResponse, highlightField); 248 249 return new EsPage(currentPage, pageSize, (int) totalHits, sourceList); 250 } 251 return null; 252 } 253 254 /** 255 * 高亮结果集 特殊处理 256 * 257 * @param searchResponse 258 * @param highlightField 259 */ 260 private List<Map<String, Object>> setSearchResponse(SearchResponse searchResponse, String highlightField) { 261 List<Map<String, Object>> sourceList = new ArrayList<Map<String, Object>>(); 262 StringBuffer stringBuffer = new StringBuffer(); 263 264 for (SearchHit searchHit : searchResponse.getHits().getHits()) { 265 searchHit.getSource().put("id", searchHit.getId()); 266 267 if (StringUtils.isNotEmpty(highlightField)) { 268 269 System.out.println("遍历 高亮结果集,覆盖 正常结果集" + searchHit.getSource()); 270 Text[] text = searchHit.getHighlightFields().get(highlightField).getFragments(); 271 272 if (text != null) { 273 for (Text str : text) { 274 stringBuffer.append(str.string()); 275 } 276 //遍历 高亮结果集,覆盖 正常结果集 277 searchHit.getSource().put(highlightField, stringBuffer.toString()); 278 } 279 } 280 sourceList.add(searchHit.getSource()); 281 } 282 return sourceList; 283 }