RestHighLevelClient查询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服务端来说往往需要设置一下查询超时时间,尽管es是分布式查询较快并建立在多个lucence基础上聚合的查询,也需要设置超时时间,避免由于数据量过大或集群过大导致查询缓慢问题;
            sourceBuilder.timeout(new TimeValue(timeOut, TimeUnit.SECONDS));
  1. RestHighLevelClient发送请求的http响应超时时间
    对应http来说往往会有一个响应的时长,超过时长后将不能再获取到数据,RestHighLevelClient作为以http方式请求es客户端这点需要注意;比如es查询需要10s,但是http设置了5s,数据就无法正常返回;
        return new RestHighLevelClient(
                RestClient.builder(this.hosts).
                        setMaxRetryTimeoutMillis(60 * 1000).  //设置http客户请求时长
                        build()
        );

排序

排序在业务中也是常用,es提供了默认排序和自定义排序,默认排序通常都是_score来排的,如下参数:
RestHighLevelClient查询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);
            }

模拟一个post获取es数据

如上就是查询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

上一篇:分库分表?如何做到永不迁移数据和避免热点?


下一篇:springboot+redis分布式锁-模拟抢单