本篇分享的是es官網推薦的es客戶端元件RestHighLevelClient的使用,其封裝了操作es的crud方法,底層原理就是模擬各種es需要的請求,如put,delete,get等方式;本篇主要分享常用查詢,希望能給大家帶來好的幫助;
- 分頁查詢
-
條件查詢
- 文字模糊匹配
- 時間範圍匹配
-
超時設定
- es超時時間
- RestHighLevelClient傳送請求的http響應超時時間
- 排序
- 指定返回列
- 模擬一個post獲取es資料
準備工作
本人es服務端的版本是5.6.x,因此RestHighLevelClient建議同樣引入相同主版本的包,相關參考文件(link):
1 <dependency> 2 <groupId>org.elasticsearch.client</groupId> 3 <artifactId>elasticsearch-rest-high-level-client</artifactId> 4 <version>5.6.16</version> 5 </dependency> 6 7 <dependency> 8 <groupId>com.alibaba</groupId> 9 <artifactId>fastjson</artifactId> 10 <version>1.2.56</version> 11 <scope>compile</scope> 12 </dependency>
作為客戶端來說,我們需要知道es服務的ip:埠,以此來連線到服務端,對於es來說連線服務端其實就是往這個ip:埠上傳送各種格式請求引數,如下建立個restclient:
1 public RestHighLevelClient client() { 2 Assert.requireNonEmpty(this.hosts, "無效的es連線"); 3 return new RestHighLevelClient( 4 RestClient.builder(this.hosts).build() 5 ); 6 }
分頁查詢
作為客戶端來說resthighlevelclient的查詢操作及其簡單,只需要如下簡短程式碼即可操作查詢:
1 SearchSourceBuilder sourceBuilder = new SearchSourceBuilder(); 2 SearchRequest rq = new SearchRequest(); 3 //索引 4 rq.indices(index); 5 //各種組合條件 6 rq.source(sourceBuilder); 7 8 //請求 9 System.out.println(rq.source().toString()); 10 SearchResponse rp = client().search(rq);
如上模板我們知道要完成一次查詢,需要知道對應的索引和各種業務查詢條件;索引是必須的,至於條件這裡先來分頁吧,可以通過如下方式設定頁碼:
1 from = from <= -1 ? 0 : from; 2 size = size >= 1000 ? 1000 : size; 3 size = size <= 0 ? 15 : size; 4 //其實位置 5 sourceBuilder.from(from); 6 //每頁數量 7 sourceBuilder.size(size);
條件查詢
對於查詢來說通常都有各種and和or的條件,可以通過SearchSourceBuilder的must和should來設定and和or關係,這裡用到了must;
1.文字模糊匹配
對於es關鍵字或單詞的查詢我們可以藉助QueryBuilders.wildcardQuery方法來操作,只需要指定es中對應的列和要查詢的內容即可:
1 //模糊匹配 2 boolQueryBuilder.must(QueryBuilders.wildcardQuery(k, v.toString()));
2.時間範圍匹配
相對時間條件來說,通常的需求都是按照範圍來查詢的,這裡可以藉助QueryBuilders.rangeQuery指定es中某列(k)的範圍匹配:
1 boolQueryBuilder.must( 2 QueryBuilders.rangeQuery(k). 3 gte(format.format(mapV.get("start"))). 4 lt(format.format(mapV.get("end"))));
超時設定
使用RestHighLevelClient作為查詢端,需要注意的超時時間有兩種:es本身需要的時間和Rest傳送http的響應時間
1.es超時時間
對於es服務端來說往往需要設定一下查詢超時時間,儘管es是分散式查詢較快並建立在多個lucence基礎上聚合的查詢,也需要設定超時時間,避免由於資料量過大或叢集過大導致查詢緩慢問題;
1 sourceBuilder.timeout(new TimeValue(timeOut, TimeUnit.SECONDS));
2.RestHighLevelClient傳送請求的http響應超時時間
對應http來說往往會有一個響應的時長,超過時長後將不能再獲取到資料,RestHighLevelClient作為以http方式請求es客戶端這點需要注意;比如es查詢需要10s,但是http設定了5s,資料就無法正常返回;
1 return new RestHighLevelClient( 2 RestClient.builder(this.hosts). 3 setMaxRetryTimeoutMillis(60 * 1000). //設定http客戶請求時長 4 build() 5 );
排序
排序在業務中也是常用,es提供了預設排序和自定義排序,預設排序通常都是_score來排的,如下引數:
如果自定義列來排序,可以通過如下方式:
1 sourceBuilder.sort(new FieldSortBuilder(k).order(v ? SortOrder.ASC : SortOrder.DESC));
指定返回列
對應查詢通常會指定一些返回列,就sql查詢來說select *通常都比select cols要慢,一個原因是資料量少了有可能走了索引;es其實也同樣,指定返回列可減少返回資料體量;
1 //返回和排除列 2 if (!CollectionUtils.isEmpty(includeFields) || !CollectionUtils.isEmpty(excludeFields)) { 3 sourceBuilder.fetchSource(includeFields, excludeFields); 4 }
模擬一個post獲取es資料
如上就是查詢es常用一些引數說明,配置及心得;下面給出完整的示例程式碼:
1 /** 2 * @param index 3 * @param from 4 * @param size 5 * @param where 6 * @param sortFieldsToAsc 7 * @param includeFields 8 * @param excludeFields 9 * @param timeOut 10 * @return 11 */ 12 public List<Map<String, Object>> searchIndex(String index, int from, int size, Map<String, Object> where, 13 Map<String, Boolean> sortFieldsToAsc, String[] includeFields, String[] excludeFields, 14 int timeOut) { 15 SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ"); 16 try { 17 SearchSourceBuilder sourceBuilder = new SearchSourceBuilder(); 18 //條件 19 if (where != null && !where.isEmpty()) { 20 BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); 21 where.forEach((k, v) -> { 22 if (v instanceof Map) { 23 //範圍選擇map 暫定時間 24 Map<String, Date> mapV = (Map<String, Date>) v; 25 if (mapV != null) { 26 boolQueryBuilder.must( 27 QueryBuilders.rangeQuery(k). 28 gte(format.format(mapV.get("start"))). 29 lt(format.format(mapV.get("end")))); 30 } 31 } else { 32 //普通模糊匹配 33 boolQueryBuilder.must(QueryBuilders.wildcardQuery(k, v.toString())); 34 } 35 }); 36 sourceBuilder.query(boolQueryBuilder); 37 } 38 39 //分頁 40 from = from <= -1 ? 0 : from; 41 size = size >= 1000 ? 1000 : size; 42 size = size <= 0 ? 15 : size; 43 sourceBuilder.from(from); 44 sourceBuilder.size(size); 45 46 //超時 47 sourceBuilder.timeout(new TimeValue(timeOut, TimeUnit.SECONDS)); 48 49 //排序 50 if (sortFieldsToAsc != null && !sortFieldsToAsc.isEmpty()) { 51 sortFieldsToAsc.forEach((k, v) -> { 52 sourceBuilder.sort(new FieldSortBuilder(k).order(v ? SortOrder.ASC : SortOrder.DESC)); 53 }); 54 } else { 55 sourceBuilder.sort(new ScoreSortBuilder().order(SortOrder.DESC)); 56 } 57 58 //返回和排除列 59 if (!CollectionUtils.isEmpty(includeFields) || !CollectionUtils.isEmpty(excludeFields)) { 60 sourceBuilder.fetchSource(includeFields, excludeFields); 61 } 62 63 SearchRequest rq = new SearchRequest(); 64 //索引 65 rq.indices(index); 66 //各種組合條件 67 rq.source(sourceBuilder); 68 69 //請求 70 System.out.println(rq.source().toString()); 71 SearchResponse rp = client().search(rq); 72 73 //解析返回 74 if (rp.status() != RestStatus.OK || rp.getHits().getTotalHits() <= 0) { 75 return Collections.emptyList(); 76 } 77 78 //獲取source 79 return Arrays.stream(rp.getHits().getHits()).map(b -> { 80 return b.getSourceAsMap(); 81 }).collect(Collectors.toList()); 82 83 } catch (Exception ex) { 84 ex.printStackTrace(); 85 } 86 return Collections.emptyList(); 87 }
分享通過RestClient方式往es傳送的請求引數以及模擬post方式傳送引數獲取es資料:
1 { 2 "from" : 0, 3 "size" : 5, 4 "timeout" : "60s", 5 "query" : { 6 "bool" : { 7 "must" : [ 8 { 9 "range" : { 10 "timeStamp" : { 11 "from" : "2019-05-23T19:17:59.000+0800", 12 "to" : "2019-05-23T19:18:00.000+0800", 13 "include_lower" : true, 14 "include_upper" : false, 15 "boost" : 1.0 16 } 17 } 18 }, 19 { 20 "wildcard" : { 21 "data" : { 22 "wildcard" : "虎", 23 "boost" : 1.0 24 } 25 } 26 } 27 ], 28 "disable_coord" : false, 29 "adjust_pure_negative" : true, 30 "boost" : 1.0 31 } 32 }, 33 "_source" : { 34 "includes" : [ ], 35 "excludes" : [ 36 "serverIp" 37 ] 38 }, 39 "sort" : [ 40 { 41 "timeStamp" : { 42 "order" : "desc" 43 } 44 } 45 ] 46 }
有了上面引數,我們完全可以通過postman直接發post給es服務端,讓其返回響應的資料而;resthighlevelclient作為http客戶端就是幫我們完成了這部分封裝: