天天看點

RestHighLevelClient查詢es準備工作分頁查詢條件查詢逾時設定排序指定傳回列模拟一個post擷取es資料

本篇分享的是es官網推薦的es用戶端元件RestHighLevelClient的使用,其封裝了操作es的crud方法,底層原理就是模拟各種es需要的請求,如put,delete,get等方式;本篇主要分享常用查詢,希望能給大家帶來好的幫助;

  • 分頁查詢
  • 條件查詢
    1. 文本模糊比對
    2. 時間範圍比對
  • 逾時設定
    1. es逾時時間
    2. RestHighLevelClient發送請求的http響應逾時時間
  • 排序
  • 指定傳回列
  • 模拟一個post擷取es資料

準備工作

本人es服務端的版本是5.6.x,是以RestHighLevelClient建議同樣引入相同主版本的包,相關參考文檔(

link

):

<dependency>
            <groupId>org.elasticsearch.client</groupId>
            <artifactId>elasticsearch-rest-high-level-client</artifactId>
            <version>5.6.16</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.56</version>
            <scope>compile</scope>
        </dependency>           

作為用戶端來說,我們需要知道es服務的ip:端口,以此來連接配接到服務端,對于es來說連接配接服務端其實就是往這個ip:端口上發送各種格式請求參數,如下建立個restclient:

public RestHighLevelClient client() {
        Assert.requireNonEmpty(this.hosts, "無效的es連接配接");
        return new RestHighLevelClient(
                RestClient.builder(this.hosts).build()
        );
    }
其他的優化配置參考官網           

作為用戶端來說resthighlevelclient的查詢操作及其簡單,隻需要如下簡短代碼即可操作查詢:

SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
            SearchRequest rq = new SearchRequest();
            //索引
            rq.indices(index);
            //各種組合條件
            rq.source(sourceBuilder);

            //請求
            System.out.println(rq.source().toString());
            SearchResponse rp = client().search(rq);           

如上模闆我們知道要完成一次查詢,需要知道對應的索引和各種業務查詢條件;索引是必須的,至于條件這裡先來分頁吧,可以通過如下方式設定頁碼:

from = from <= -1 ? 0 : from;
            size = size >= 1000 ? 1000 : size;
            size = size <= 0 ? 15 : size;
            //其實位置
            sourceBuilder.from(from);
            //每頁數量
            sourceBuilder.size(size);           

對于查詢來說通常都有各種and和or的條件,可以通過SearchSourceBuilder的must和should來設定and和or關系,這裡用到了must;

  1. 對于es關鍵字或單詞的查詢我們可以借助QueryBuilders.wildcardQuery方法來操作,隻需要指定es中對應的列和要查詢的内容即可:
//模糊比對
            boolQueryBuilder.must(QueryBuilders.wildcardQuery(k, v.toString()));           
  1. 相對時間條件來說,通常的需求都是按照範圍來查詢的,這裡可以借助QueryBuilders.rangeQuery指定es中某列(k)的範圍比對:
boolQueryBuilder.must(
                        QueryBuilders.rangeQuery(k).
                                gte(format.format(mapV.get("start"))).
                                lt(format.format(mapV.get("end"))));           

使用RestHighLevelClient作為查詢端,需要注意的逾時時間有兩種:es本身需要的時間和Rest發送http的響應時間

  1. 對于es服務端來說往往需要設定一下查詢逾時時間,盡管es是分布式查詢較快并建立在多個lucence基礎上聚合的查詢,也需要設定逾時時間,避免由于資料量過大或叢集過大導緻查詢緩慢問題;
sourceBuilder.timeout(new TimeValue(timeOut, TimeUnit.SECONDS));           
  1. 對應http來說往往會有一個響應的時長,超過時長後将不能再擷取到資料,RestHighLevelClient作為以http方式請求es用戶端這點需要注意;比如es查詢需要10s,但是http設定了5s,資料就無法正常傳回;
return new RestHighLevelClient(
                RestClient.builder(this.hosts).
                        setMaxRetryTimeoutMillis(60 * 1000).  //設定http客戶請求時長
                        build()
        );           

排序在業務中也是常用,es提供了預設排序和自定義排序,預設排序通常都是_score來排的,如下參數:

RestHighLevelClient查詢es準備工作分頁查詢條件查詢逾時設定排序指定傳回列模拟一個post擷取es資料

如果自定義列來排序,可以通過如下方式:

sourceBuilder.sort(new FieldSortBuilder(k).order(v ? SortOrder.ASC : SortOrder.DESC));           

對應查詢通常會指定一些傳回列,就sql查詢來說select *通常都比select cols要慢,一個原因是資料量少了有可能走了索引;es其實也同樣,指定傳回列可減少傳回資料體量;

//傳回和排除列
            if (!CollectionUtils.isEmpty(includeFields) || !CollectionUtils.isEmpty(excludeFields)) {
                sourceBuilder.fetchSource(includeFields, excludeFields);
            }           

如上就是查詢es常用一些參數說明,配置及心得;下面給出完整的示例代碼:

/**
     * @param index
     * @param from
     * @param size
     * @param where
     * @param sortFieldsToAsc
     * @param includeFields
     * @param excludeFields
     * @param timeOut
     * @return
     */
    public List<Map<String, Object>> searchIndex(String index, int from, int size, Map<String, Object> where,
                                                 Map<String, Boolean> sortFieldsToAsc, String[] includeFields, String[] excludeFields,
                                                 int timeOut) {
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
        try {
            SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
            //條件
            if (where != null && !where.isEmpty()) {
                BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
                where.forEach((k, v) -> {
                    if (v instanceof Map) {
                        //範圍選擇map  暫定時間
                        Map<String, Date> mapV = (Map<String, Date>) v;
                        if (mapV != null) {
                            boolQueryBuilder.must(
                                    QueryBuilders.rangeQuery(k).
                                            gte(format.format(mapV.get("start"))).
                                            lt(format.format(mapV.get("end"))));
                        }
                    } else {
                        //普通模糊比對
                        boolQueryBuilder.must(QueryBuilders.wildcardQuery(k, v.toString()));
                    }
                });
                sourceBuilder.query(boolQueryBuilder);
            }

            //分頁
            from = from <= -1 ? 0 : from;
            size = size >= 1000 ? 1000 : size;
            size = size <= 0 ? 15 : size;
            sourceBuilder.from(from);
            sourceBuilder.size(size);

            //逾時
            sourceBuilder.timeout(new TimeValue(timeOut, TimeUnit.SECONDS));

            //排序
            if (sortFieldsToAsc != null && !sortFieldsToAsc.isEmpty()) {
                sortFieldsToAsc.forEach((k, v) -> {
                    sourceBuilder.sort(new FieldSortBuilder(k).order(v ? SortOrder.ASC : SortOrder.DESC));
                });
            } else {
                sourceBuilder.sort(new ScoreSortBuilder().order(SortOrder.DESC));
            }

            //傳回和排除列
            if (!CollectionUtils.isEmpty(includeFields) || !CollectionUtils.isEmpty(excludeFields)) {
                sourceBuilder.fetchSource(includeFields, excludeFields);
            }

            SearchRequest rq = new SearchRequest();
            //索引
            rq.indices(index);
            //各種組合條件
            rq.source(sourceBuilder);

            //請求
            System.out.println(rq.source().toString());
            SearchResponse rp = client().search(rq);

            //解析傳回
            if (rp.status() != RestStatus.OK || rp.getHits().getTotalHits() <= 0) {
                return Collections.emptyList();
            }

            //擷取source
            return Arrays.stream(rp.getHits().getHits()).map(b -> {
                return b.getSourceAsMap();
            }).collect(Collectors.toList());

        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return Collections.emptyList();
    }           

分享通過RestClient方式往es發送的請求參數以及模拟post方式發送參數擷取es資料:

{
  "from" : 0,
  "size" : 5,
  "timeout" : "60s",
  "query" : {
    "bool" : {
      "must" : [
        {
          "range" : {
            "timeStamp" : {
              "from" : "2019-05-23T19:17:59.000+0800",
              "to" : "2019-05-23T19:18:00.000+0800",
              "include_lower" : true,
              "include_upper" : false,
              "boost" : 1.0
            }
          }
        },
        {
          "wildcard" : {
            "data" : {
              "wildcard" : "虎",
              "boost" : 1.0
            }
          }
        }
      ],
      "disable_coord" : false,
      "adjust_pure_negative" : true,
      "boost" : 1.0
    }
  },
  "_source" : {
    "includes" : [ ],
    "excludes" : [
      "serverIp"
    ]
  },
  "sort" : [
    {
      "timeStamp" : {
        "order" : "desc"
      }
    }
  ]
}
           

有了上面參數,我們完全可以通過postman直接發post給es服務端,讓其傳回響應的資料而;resthighlevelclient作為http用戶端就是幫我們完成了這部分封裝:

RestHighLevelClient查詢es準備工作分頁查詢條件查詢逾時設定排序指定傳回列模拟一個post擷取es資料

繼續閱讀