Spring Boot集成Elasticsearch复杂查询

发布于:2022-10-13 ⋅ 阅读:(323) ⋅ 点赞:(0)

前言

项目中经常会遇到一些比较复杂的查询,如何使用RestHighLevelClient来实现查询呢,本文将讲解Spring Boot采用RestHighLevelClient如何实现高级查询。
 

数据准备 

我们准备相关的数据来展示相关的示例。

具体实现

分页组合查询

分页查询:采用from、size的方式进行分页和Mysql的limit分页原理是一样的,from代表是数据从那条开始,size代表是查询多少条数据。

Controller实现

@GetMapping("/pageSearch")
    public Result pageSearch(String indexName,String key,String value) throws IOException 
    {
        //设置多个文档
        SearchRequest request =new SearchRequest(indexName);
        SearchSourceBuilder sourceBuilder =new SearchSourceBuilder().query(QueryBuilders.termQuery(key, value));
        //表示从什么位置开始,查询多少条数据
        sourceBuilder.from(0).size(2);
        //按照价格排序
        sourceBuilder.sort("price",SortOrder.DESC);
        request.source(sourceBuilder);
        SearchResponse response =client.search(request, RequestOptions.DEFAULT);
        SearchHits hits = response.getHits();
        SearchHit[] searchHits = hits.getHits();
        for (SearchHit hit : hits) 
        {
            logger.info("search Id:{},hit data:{}",hit.getId(),hit.getSourceAsString());
        }
        return Result.success(searchHits);
    }

输出结果

c.s.f.e.c.RestHightController - search Id:12,hit data:{"createDate":1655132070891,"id":"12","price":110.0,"skuNo":"sku0012","title":"java设计模式"}c.s.f.e.c.RestHightController - search Id:10,hit data:{"createDate":1655132070891,"id":"10","price":105.0,"skuNo":"sku0011","title":"Effive java"}
2022-06-14 21:41:55.994 [http-nio-9090-exec-1] INFO  [] c.s.f.e.c.RestHightController - search Id:11,hit data:{"createDate":1655132070891,"id":"11","price":100.0,"skuNo":"sku0011","title":"java架构设计"}
2022-06-14 21:41:55.994 [http-nio-9090-exec-1] INFO  [] c.s.f.e.c.RestHightController - search Id:2,hit data:{"createDate":1655132070891,"id":"2","price":99.0,"skuNo":"sku0002","title":"java从入门到精通"}
2022-06-14 21:41:55.994 [http-nio-9090-exec-1] INFO  [] c.s.f.e.c.RestHightController - search Id:3,hit data:{"createDate":1655132070891,"id":"3","price":89.0,"skuNo":"sku0003","title":"java编程思想"}

说明:从输出结果可以看出,通过查询包含java的数据,分页显示5条数据,且按照价格进行排序。

分组查询

分组查询:采用TermAggregation针对某个字段进行分组查询。

Controller实现

 @GetMapping("/aggSearch")
    public Result aggSearch(String indexName,String key,String value) throws IOException 
    {
        SearchRequest request =new SearchRequest(indexName);
        SearchSourceBuilder sourceBuilder =new SearchSourceBuilder().query(QueryBuilders.termQuery(key, value));
        //按照价格进行聚合
        AggregationBuilder ageAgg = AggregationBuilders.terms("priceGroup").field("price").size(10);
        sourceBuilder.aggregation(ageAgg);
        request.source(sourceBuilder);
        SearchResponse response =client.search(request, RequestOptions.DEFAULT);
        SearchHits hits = response.getHits();
        SearchHit[] searchHits = hits.getHits();
        for (SearchHit hit : hits) 
        {
            logger.info("search Id:{},hit data:{}",hit.getId(),hit.getSourceAsString());
        }
        Aggregations aggregations = response.getAggregations();
        //数据为double类型,采用ParsedDoubleTerms
        ParsedDoubleTerms parsedDoubleTerms = aggregations.get("priceGroup");
        // 输出匹配的结果数据
        List<? extends Terms.Bucket> buckets = parsedDoubleTerms.getBuckets();
        for (Terms.Bucket bucket:buckets)
        {
           logger.info("key:{},count:{}",bucket.getKeyAsString(),bucket.getDocCount());
        }
        return Result.success(searchHits);
    }

说明:针对price字段进行分组查询,如果是double类型的数据,需要采用ParsedDoubleTerms进行接收,否则会报类型转换错误。

输出结果

c.s.f.e.c.RestHightController - key:89.0,count:2
c.s.f.e.c.RestHightController - key:99.0,count:1
c.s.f.e.c.RestHightController - key:100.0,count:1
c.s.f.e.c.RestHightController - key:105.0,count:1
c.s.f.e.c.RestHightController - key:110.0,count:1

聚合查询

聚合查询通常包含:Max、Min、AVG、Sum等查询

Controller实现

 /**
     * 聚合查询
     * @param indexName
     * @param key
     * @param value
     * @return
     * @throws IOException
     */
    @GetMapping("/sumSearch")
    public Result sumSearch(String indexName,String type,String key,String value) throws IOException 
    {
        //设置多个文档
        SearchRequest request =new SearchRequest(indexName);
        SearchSourceBuilder sourceBuilder =new SearchSourceBuilder().query(QueryBuilders.termQuery(key, value));
        AggregationBuilder ageAgg  =null;
        //按照条件进行聚合
        if("max".equals(type))
        {
            ageAgg=AggregationBuilders.max("priceMax").field("price");
        }
        else if("min".equals(type))
        {
            ageAgg=AggregationBuilders.min("priceMin").field("price");
        }
        else if("avg".equals(type))
        {
            ageAgg=AggregationBuilders.avg("priceAvg").field("price");
        }
        else
        {
          ageAgg= AggregationBuilders.sum("priceSum").field("price");
        }
              
        sourceBuilder.aggregation(ageAgg);
        request.source(sourceBuilder);
        SearchResponse response =client.search(request, RequestOptions.DEFAULT);
        SearchHits hits = response.getHits();
        SearchHit[] searchHits = hits.getHits();
        for (SearchHit hit : hits) 
        {
            logger.info("search Id:{},hit data:{}",hit.getId(),hit.getSourceAsString());
        }
        
        Aggregations aggregations = response.getAggregations();
        //价格之和
        /**
         max 使用ParsedMax处理
         min 使用ParsedMin处理
         avg 使用 ParsedAvg处理
         sum 使用ParsedSum处理
        */
        if("max".equals(type))
        {
            ParsedMax preMax = aggregations.get("priceMax");
            logger.info("求最大为:"+preMax.value());
        }
        else if("min".equals(type))
        {
            ParsedMin preMin = aggregations.get("priceMin");
            logger.info("求最小为:"+preMin.value());
        }
        else if("avg".equals(type))
        {
            ParsedAvg preAvg = aggregations.get("priceAvg");
            logger.info("求平均值为:"+preAvg.value());
        }
        else
        {
           ParsedSum parsedSum = aggregations.get("priceSum");
           logger.info("求和值为:"+parsedSum.value());
        }
        return Result.success(searchHits);
    }

说明:

  • Type:类型:通过传入不同的值,进行不同的聚合查询,
  • ParsedSum :处理求和后的结果
  • ParsedAvg :处理求平均值的结果
  • ParsedMin :处理最小值的结果
  • ParsedMax:处理最大值的结果

输出结果

例如求平均值:

2022-06-14 22:28:52.938 [http-nio-9090-exec-6] INFO  [] c.s.f.e.c.RestHightController - 求平均值为:98.66666666666667

Top查询

Controller实现

 @GetMapping("/topSearch")
    public Result topSearch(String indexName,String key,String value) throws IOException 
    {
        SearchRequest request =new SearchRequest(indexName);
        SearchSourceBuilder sourceBuilder =new SearchSourceBuilder().query(QueryBuilders.termQuery(key, value));
        //按照价格排序获取前2条
        AggregationBuilder aggregationBuilder = AggregationBuilders.topHits("price_top").sort(SortBuilders.fieldSort("price").order(SortOrder.DESC)).size(2);
        sourceBuilder.aggregation(aggregationBuilder);
        request.source(sourceBuilder);
        SearchResponse response =client.search(request, RequestOptions.DEFAULT);
        SearchHits hits = response.getHits();
        SearchHit[] searchHits = hits.getHits();
        for (SearchHit hit : hits) 
        {
            logger.info("search Id:{},hit data:{}",hit.getId(),hit.getSourceAsString());
        }
        //输出结果
        Aggregations aggregations = response.getAggregations();
        TopHits topHits = aggregations.get("price_top");
        SearchHits hitsRes = topHits.getHits();
        for (SearchHit hit : hitsRes.getHits())
        {
            logger.info("id:{} ,Top data:{}",hit.getId(),hit.getSourceAsString());
        }
        return Result.success(searchHits);
    }

说明:通过price进行排序获取前2条记录。

输出结果

c.s.f.e.c.RestHightController - id:12 ,Top data:{"createDate":1655132070891,"id":"12","price":110.0,"skuNo":"sku0012","title":"java设计模式"}
c.s.f.e.c.RestHightController - id:10 ,Top data:{"createDate":1655132070891,"id":"10","price":105.0,"skuNo":"sku0010","title":"Effive java"}

高亮显示

Controller实现

 @GetMapping("/hightSearch")
    public Result hightSearch(String indexName,String key,String value) throws IOException 
    {
        SearchRequest request =new SearchRequest(indexName);
        //构造查询条件
        SearchSourceBuilder sourceBuilder =new SearchSourceBuilder().query(QueryBuilders.termQuery(key, value));
        //设置高亮显示
        HighlightBuilder highlightBuilder = new HighlightBuilder().preTags("<span style='color:red'>").postTags("</span>");
        highlightBuilder.highlighterType("unified"); // 设置字段高光色类型。
        highlightBuilder.field("title").field("remark");   //将字段高光色添加到高亮构建器。
        //设置值
        sourceBuilder.highlighter(highlightBuilder);
        request.source(sourceBuilder);
        SearchResponse response =client.search(request, RequestOptions.DEFAULT);
        SearchHits hits = response.getHits();
        SearchHit[] searchHits = hits.getHits();
        for (SearchHit hit: searchHits)
        {
            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
           if(highlightFields.containsKey("remark"))
           {
               logger.info("remark高亮的结果:{}",highlightFields.get("remark").fragments()[0]);
           }
           
            if(highlightFields.containsKey("title"))
            {
                logger.info("title高亮的结果:{}",highlightFields.get("title").fragments()[0]);
            }
        }
        return Result.success(searchHits);
    }

说明:通过查询包含java的标题显示为红色。

输出结果

c.s.f.e.c.RestHightController - title高亮的结果:<span style='color:red'>java</span>架构设计
c.s.f.e.c.RestHightController - title高亮的结果:<span style='color:red'>java</span>从入门到精通

总结

本文重点讲解了Spring Boot集成Elasticsearch实现一些复杂的查询,如有疑问可以随时反馈。