這里之所以選擇high level client方式是因為transportClient在7.X版本中廢棄,預計會在8版本徹底刪除。
可參考官方文檔地址:https://www.elastic.co/guide/en/elasticsearch/client/java-rest/7.6/java-rest-high-getting-started-maven.html
配置文件
pom.xml
這里有個小坑,在使用官方文檔的依賴包時,發現maven下載的es相關jar包竟然是6.4版本的,導致出現了很多問題。
通過 https://www.jianshu.com/p/acc8e86cc772 可解決jar包版本不一致的問題,再次感謝大佬分享學習的精神。
<!-- es --> <dependency> <groupId>org.elasticsearch.client</groupId> <artifactId>elasticsearch-rest-high-level-client</artifactId> <version>7.6.1</version> <exclusions> <exclusion> <groupId>org.elasticsearch</groupId> <artifactId>elasticsearch</artifactId> </exclusion> <exclusion> <groupId>org.elasticsearch.client</groupId> <artifactId>elasticsearch-rest-client</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.elasticsearch.client</groupId> <artifactId>elasticsearch-rest-client</artifactId> <version>7.6.1</version> </dependency> <dependency> <groupId>org.elasticsearch</groupId> <artifactId>elasticsearch</artifactId> <version>7.6.1</version> </dependency>
application.yml
es:
host: 10.32.16.179 port: 9200 schema: http
索引管理
判斷索引是否存在
@Service
public class ElasticSearchServiceImpl implements ElasticSearchService { private static final Logger logger = LoggerFactory.getLogger(ElasticSearchServiceImpl.class); @Autowired private RestHighLevelClient restHighLevelClient; /** * 根據索引名稱判斷索引是否已存在 * @param indexName * @return * @throws IOException */ @Override public boolean isExistsIndex(String indexName) throws IOException { return restHighLevelClient.indices().exists(new GetIndexRequest(indexName), RequestOptions.DEFAULT); } }
創建索引
官方提供了三種創建mapping的方式,這里在后端寫死了mapping,如果需要索引管理,可通過前端傳入參數構建。
/**
* 創建索引
* @param indexName
* @throws IOException
*/
@Override
public void createIndex(String indexName) throws IOException { if(isExistsIndex(indexName)){ logger.error("indexName={} 已存在,無法創建", indexName); return; } CreateIndexRequest request = new CreateIndexRequest(indexName); //設置分片和備份 request.settings(Settings.builder() .put("index.number_of_shards",3) .put("index.number_of_replicas",2) .build()); //第一種,json字符串 request.mapping("{\n" + "\t\"properties\": {\n" + "\t\t\"username\": {\n" + "\t\t\t\"type\": \"text\"\n" + "\t\t},\n" + "\t\t\"city\": {\n" + "\t\t\t\"type\": \"keyword\"\n" + "\t\t}\n" + "\t}\n" + "}", XContentType.JSON); //第二種,Map // Map<String,Object> username = new HashMap<>(); // username.put("type","text"); // Map<String,Object> city = new HashMap<>(); // city.put("type","keyword"); // Map<String,Object> properties = new HashMap<>(); // properties.put("username",username); // properties.put("city",city); // Map<String,Object> mapping = new HashMap<>(); // mapping.put("properties",properties); // request.mapping(mapping); //第三種,XContentBuilder // XContentBuilder builder = XContentFactory.jsonBuilder(); // builder.startObject(); // { // builder.startObject("properties"); // { // builder.startObject("username"); // { // builder.field("type","text"); // } // builder.endObject(); // // builder.startObject("city"); // { // builder.field("type","keyword"); // } // builder.endObject(); // } // builder.endObject(); // } // builder.endObject(); // restHighLevelClient.indices().create(request,RequestOptions.DEFAULT); }
刪除索引
/**
* 刪除索引
* @param indexName
* @throws IOException
*/
@Override
public void deleteIndex(String indexName) throws IOException { if(!isExistsIndex(indexName)){ logger.error("indexName={} 索引不存在", indexName); return; } restHighLevelClient.indices().delete(new DeleteIndexRequest(indexName),RequestOptions.DEFAULT); }
文檔管理
新增或更新文檔
id為空則新增,不為空則更新
ElasticEntity
package com.wk.entity;
/**
* 泛型,es入參實體類
* @param <T>
*/
public class ElasticEntity<T> { private String id; private T data; public String getId() { return id; } public void setId(String id) { this.id = id; } public T getData() { return data; } public void setData(T data) { this.data = data; } }
EsTestEntity
package com.wk.entity;
/**
* controller接收實體類
*/
public class EsTestEntity { private String id; private String username; private String city; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getCity() { return city; } public void setCity(String city) { this.city = city; } }
業務類
官方新增文檔也有三種方式,和創建索引類似,這里不一一列舉了
/**
* 新增或更新文檔
* @param indexName
* @param elasticEntity
* @throws IOException
*/
@Override
public void insertOrUpdateDocument(String indexName, ElasticEntity elasticEntity) throws IOException { IndexRequest indexRequest = new IndexRequest(indexName); indexRequest.id(elasticEntity.getId()); indexRequest.source(JSONObject.toJSONString(elasticEntity.getData()),XContentType.JSON); restHighLevelClient.index(indexRequest,RequestOptions.DEFAULT); }
controller
還可在插入之前校驗一下index是否存在,這里省略了只簡單實現了demo
@PostMapping("insertOrUpdateDocument/{indexName}")
public Map<String,String> insertOrUpdateDocument(@PathVariable String indexName,@RequestBody EsTestEntity esTestEntity){
ElasticEntity<EsTestEntity> entity = new ElasticEntity<>();
//注冊生成UUID插入實體類的ID
if(StringUtils.isEmpty(esTestEntity.getId())){
String id = UUID.randomUUID().toString();
entity.setId(id);
esTestEntity.setId(id);
}
entity.setId(esTestEntity.getId());
entity.setData(esTestEntity);
try {
elasticSearchServiceImpl.insertOrUpdateDocument(indexName,entity);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
條件查詢
controller
@PostMapping("searchDocument/{indexName}")
public List<EsTestEntity> searchDocument(@PathVariable String indexName, @RequestBody EsTestEntity esTestEntity){ try { SearchSourceBuilder builder = new SearchSourceBuilder(); Map<String,Object> requestMap = BeanUtil.convertToMap(esTestEntity); for(Map.Entry<String,Object> entry:requestMap.entrySet()){ builder.query(QueryBuilders.matchQuery(entry.getKey(),entry.getValue())); } //分頁 builder.from(0); builder.size(5); //排序 builder.sort("city", SortOrder.ASC); //設置超時時間 builder.timeout(new TimeValue(60, TimeUnit.SECONDS)); return elasticSearchServiceImpl.searchDocument(indexName,builder,EsTestEntity.class); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return null; }
service
/**
* 條件查詢
* @param indexName
* @param builder
* @param c
* @param <T>
* @return
* @throws IOException
*/
@Override
public <T> List<T> searchDocument(String indexName, SearchSourceBuilder builder, Class<T> c) throws IOException { SearchRequest searchRequest = new SearchRequest(indexName); searchRequest.source(builder); SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT); SearchHit[] searchHits = searchResponse.getHits().getHits(); List<T> res = new ArrayList<>(); for(SearchHit searchHit:searchHits){ res.add(JSONObject.parseObject(searchHit.getSourceAsString(),c)); } return res; }
高亮條件查詢
controller
@PostMapping("searchHighlightDocument/{indexName}")
public List<EsTestEntity> searchHighlightDocument(@PathVariable String indexName, @RequestBody EsTestEntity esTestEntity){ try { SearchSourceBuilder builder = new SearchSourceBuilder(); Map<String,Object> requestMap = BeanUtil.convertToMap(esTestEntity); for(Map.Entry<String,Object> entry:requestMap.entrySet()){ builder.query(QueryBuilders.matchQuery(entry.getKey(),entry.getValue())); } builder.from(0); builder.size(5); builder.sort("city", SortOrder.ASC); builder.timeout(new TimeValue(60, TimeUnit.SECONDS)); HighlightBuilder highlightBuilder = new HighlightBuilder(); highlightBuilder.preTags("<front color = 'red'>"); highlightBuilder.postTags("</front>"); HighlightBuilder.Field field = new HighlightBuilder.Field("username"); highlightBuilder.field(field); builder.highlighter(highlightBuilder); return elasticSearchServiceImpl.searchHighlightDocument(indexName,builder,EsTestEntity.class); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return null; }
service
/**
* 高亮條件查詢
* @param indexName
* @param builder
* @param c
* @param <T>
* @return
* @throws IOException
*/
@Override
public <T> List<T> searchHighlightDocument(String indexName, SearchSourceBuilder builder, Class<T> c) throws IOException { SearchRequest searchRequest = new SearchRequest(indexName); searchRequest.source(builder); SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT); SearchHit[] searchHits = searchResponse.getHits().getHits(); List<T> res = new ArrayList<>(); for(SearchHit searchHit:searchHits){ //返回結果轉換成Map Map<String,Object> sourceMap = searchHit.getSourceAsMap(); //獲取高亮的返回結果 Map<String, HighlightField> map = searchHit.getHighlightFields(); //循環設置的高亮字段 for(Map.Entry<String, HighlightField> entry:map.entrySet()){ //將高亮字段格式替換原結果中的值 sourceMap.put(entry.getKey(),entry.getValue().getFragments()[0].toString()); } res.add(JSONObject.parseObject(JSONObject.toJSONString(sourceMap),c)); } return res; }
返回結果:
刪除文檔
/**
* 根據ID刪除文檔
* @param indexName
* @param id
* @throws IOException
*/
@Override
public void deleteDocumentById(String indexName, String id) throws IOException { DeleteRequest deleteRequest = new DeleteRequest(indexName,id); restHighLevelClient.delete(deleteRequest,RequestOptions.DEFAULT); }
還有條件刪除等API,可以參考官方文檔,這里不一一列舉了
批量操作
批量新增
/**
* 批量新增
* @param indexName
* @param elasticEntities
* @throws IOException
*/
@Override
public void insertDocumentBatch(String indexName, List<ElasticEntity> elasticEntities) throws IOException { BulkRequest bulkRequest = new BulkRequest(); elasticEntities.forEach(item ->{ bulkRequest.add(new IndexRequest(indexName).id(item.getId()).source(JSONObject.toJSONString(item.getData()),XContentType.JSON)); }); restHighLevelClient.bulk(bulkRequest,RequestOptions.DEFAULT); }
批量刪除
/**
* 批量刪除文檔
* @param indexName
* @param idList
* @param <T>
* @throws IOException
*/
public <T> void deleteDocumentBatch(String indexName, Collection<T> idList) throws IOException {
BulkRequest bulkRequest = new BulkRequest(); idList.forEach(item ->{ bulkRequest.add(new DeleteRequest(indexName, item.toString())); }); restHighLevelClient.bulk(bulkRequest,RequestOptions.DEFAULT); }