RestClient查询文档(*)

match_all查询

代码解读:

  • 第一步,创建SearchRequest对象,指定索引库名

  • 第二步,利用request.source()构建DSL,DSL中可以包含查询、分页、排序、高亮等

    • query():代表查询条件,利用QueryBuilders.matchAllQuery()构建一个match_all查询的DSL
  • 第三步,利用client.search()发送请求,得到响应

这里关键的API有两个,一个是request.source(),其中包含了查询、排序、分页、高亮等所有功能:

另一个是QueryBuilders,其中包含match、term、function_score、bool等各种查询:

解析响应

elasticsearch返回的结果是一个JSON字符串,结构包含:

  • hits:命中的结果
    • total:总条数,其中的value是具体的总条数值
    • max_score:所有结果中得分最高的文档的相关性算分
    • hits:搜索结果的文档数组,其中的每个文档都是一个json对象
      • _source:文档中的原始数据,也是json对象

因此,我们解析响应结果,就是逐层解析JSON字符串,流程如下:

  • SearchHits:通过response.getHits()获取,就是JSON中的最外层的hits,代表命中的结果
    • SearchHits#getTotalHits().value:获取总条数信息
    • SearchHits#getHits():获取SearchHit数组,也就是文档数组
      • SearchHit#getSourceAsString():获取文档结果中的_source,也就是原始的json文档数据.

完整代码

复制代码
@Test
void testMatchAll() throws IOException {
    // 1.准备Request
    SearchRequest request = new SearchRequest("hotel");
    // 2.准备DSL
    request.source()
        .query(QueryBuilders.matchAllQuery());
    // 3.发送请求
    SearchResponse response = client.search(request, RequestOptions.DEFAULT);

    // 4.解析响应
    handleResponse(response);
}

private void handleResponse(SearchResponse response) {
    // 4.解析响应
    SearchHits searchHits = response.getHits();
    // 4.1.获取总条数
    long total = searchHits.getTotalHits().value;
    System.out.println("共搜索到" + total + "条数据");
    // 4.2.文档数组
    SearchHit[] hits = searchHits.getHits();
    // 4.3.遍历
    for (SearchHit hit : hits) {
        // 获取文档source
        String json = hit.getSourceAsString();
        // 反序列化
        HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);
        System.out.println("hotelDoc = " + hotelDoc);
    }
}
复制代码

查询的基本步骤是:

  1. 创建SearchRequest对象

  2. 准备Request.source(),也就是DSL。

    ① QueryBuilders来构建查询条件

    ② 传入Request.source() 的 query() 方法

  3. 发送请求,得到结果

  4. 解析结果(参考JSON结果,从外到内,逐层解析)

全文检索

复制代码
@Test
void testMatch() throws IOException {
    // 1.准备Request
    SearchRequest request = new SearchRequest("hotel");
    // 2.准备DSL
    request.source()
        .query(QueryBuilders.matchQuery("all", "如家"));
    // 3.发送请求
    SearchResponse response = client.search(request, RequestOptions.DEFAULT);
    // 4.解析响应
    handleResponse(response);

}
复制代码

精确查询

布尔查询

复制代码
@Test
void testBool() throws IOException {
    // 1.准备Request
    SearchRequest request = new SearchRequest("hotel");
    // 2.准备DSL
    // 2.1.准备BooleanQuery
    BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
    // 2.2.添加term
    boolQuery.must(QueryBuilders.termQuery("city", "杭州"));
    // 2.3.添加range
    boolQuery.filter(QueryBuilders.rangeQuery("price").lte(250));

    request.source().query(boolQuery);
    // 3.发送请求
    SearchResponse response = client.search(request, RequestOptions.DEFAULT);
    // 4.解析响应
    handleResponse(response);

}
复制代码

排序、分页

复制代码
@Test
void testPageAndSort() throws IOException {
    // 页码,每页大小
    int page = 1, size = 5;

    // 1.准备Request
    SearchRequest request = new SearchRequest("hotel");
    // 2.准备DSL
    // 2.1.query
    request.source().query(QueryBuilders.matchAllQuery());
    // 2.2.排序 sort
    request.source().sort("price", SortOrder.ASC);
    // 2.3.分页 from、size
    request.source().from((page - 1) * size).size(5);
    // 3.发送请求
    SearchResponse response = client.search(request, RequestOptions.DEFAULT);
    // 4.解析响应
    handleResponse(response);

}
复制代码

高亮

高亮的代码与之前代码差异较大,有两点:

  • 查询的DSL:其中除了查询条件,还需要添加高亮条件,同样是与query同级。
  • 结果解析:结果除了要解析_source文档数据,还要解析高亮结果

高亮请求构建

高亮请求的构建API如下:

上述代码省略了查询条件部分,但是大家不要忘了:高亮查询必须使用全文检索查询,并且要有搜索关键字,将来才可以对关键字高亮

复制代码
@Test
void testHighlight() throws IOException {
    // 1.准备Request
    SearchRequest request = new SearchRequest("hotel");
    // 2.准备DSL
    // 2.1.query
    request.source().query(QueryBuilders.matchQuery("all", "如家"));
    // 2.2.高亮
    request.source().highlighter(new HighlightBuilder().field("name").requireFieldMatch(false));
    // 3.发送请求
    SearchResponse response = client.search(request, RequestOptions.DEFAULT);
    // 4.解析响应
    handleResponse(response);
}
复制代码

高亮结果解析

高亮的结果与查询的文档结果默认是分离的,并不在一起。

因此解析高亮的代码需要额外处理:

 

代码解读:

  • 第一步:从结果中获取source。hit.getSourceAsString(),这部分是非高亮结果,json字符串。还需要反序列为HotelDoc对象
  • 第二步:获取高亮结果。hit.getHighlightFields(),返回值是一个Map,key是高亮字段名称,值是HighlightField对象,代表高亮值
  • 第三步:从map中根据高亮字段名称,获取高亮字段值对象HighlightField
  • 第四步:从HighlightField中获取Fragments,并且转为字符串。这部分就是真正的高亮字符串了
  • 第五步:用高亮的结果替换HotelDoc中的非高亮结果

完整代码如下:

复制代码
private void handleResponse(SearchResponse response) {
    // 4.解析响应
    SearchHits searchHits = response.getHits();
    // 4.1.获取总条数
    long total = searchHits.getTotalHits().value;
    System.out.println("共搜索到" + total + "条数据");
    // 4.2.文档数组
    SearchHit[] hits = searchHits.getHits();
    // 4.3.遍历
    for (SearchHit hit : hits) {
        // 获取文档source
        String json = hit.getSourceAsString();
        // 反序列化
        HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);
        // 获取高亮结果
        Map<String, HighlightField> highlightFields = hit.getHighlightFields();
        if (!CollectionUtils.isEmpty(highlightFields)) {
            // 根据字段名获取高亮结果
            HighlightField highlightField = highlightFields.get("name");
            if (highlightField != null) {
                // 获取高亮值
                String name = highlightField.getFragments()[0].string();
                // 覆盖非高亮结果
                hotelDoc.setName(name);
            }
        }
        System.out.println("hotelDoc = " + hotelDoc);
    }
}
复制代码

示例代码(使用复合查询):

注意:原始查询条件:可以是全文检索、精确查询、地理查询、复合查询

复制代码
  1 @Slf4j
  2 @Service
  3 public class HotelService extends ServiceImpl<HotelMapper, Hotel> implements IHotelService {
  4 
  5     @Autowired
  6     private RestHighLevelClient restHighLevelClient;
  7 
  8     @Override
  9     public PageResult search(RequestParams params) {
 10         try {
 11             // 1.准备Request
 12             SearchRequest request = new SearchRequest("hotel");
 13             // 2.准备请求参数
 14             // 2.1.query
 15             buildBasicQuery(params, request);
 16             // 2.2.分页
 17             int page = params.getPage();
 18             int size = params.getSize();
 19             request.source().from((page - 1) * size).size(size);
 20             // 2.3.距离排序
 21             String location = params.getLocation();
 22             if (StringUtils.isNotBlank(location)) {
 23                 request.source().sort(SortBuilders
 24                         .geoDistanceSort("location", new GeoPoint(location))
 25                         .order(SortOrder.ASC)
 26                         .unit(DistanceUnit.KILOMETERS)
 27                 );
 28             }
 29             // 3.发送请求
 30             SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
 31             // 4.解析响应
 32             return handleResponse(response);
 33         } catch (IOException e) {
 34             throw new RuntimeException("搜索数据失败", e);
 35         }
 36     }
 37 
 38     private void buildBasicQuery(RequestParams params, SearchRequest request) {
 39         // 1.准备Boolean查询
 40         BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
 41 
 42         // 1.1.关键字搜索,match查询,放到must中
 43         String key = params.getKey();
 44         if (StringUtils.isNotBlank(key)) {
 45             // 不为空,根据关键字查询
 46             boolQuery.must(QueryBuilders.matchQuery("all", key));
 47         } else {
 48             // 为空,查询所有
 49             boolQuery.must(QueryBuilders.matchAllQuery());
 50         }
 51 
 52         // 1.2.品牌
 53         String brand = params.getBrand();
 54         if (StringUtils.isNotBlank(brand)) {
 55             boolQuery.filter(QueryBuilders.termQuery("brand", brand));
 56         }
 57         // 1.3.城市
 58         String city = params.getCity();
 59         if (StringUtils.isNotBlank(city)) {
 60             boolQuery.filter(QueryBuilders.termQuery("city", city));
 61         }
 62         // 1.4.星级
 63         String starName = params.getStarName();
 64         if (StringUtils.isNotBlank(starName)) {
 65             boolQuery.filter(QueryBuilders.termQuery("starName", starName));
 66         }
 67         // 1.5.价格范围
 68         Integer minPrice = params.getMinPrice();
 69         Integer maxPrice = params.getMaxPrice();
 70         if (minPrice != null && maxPrice != null) {
 71             maxPrice = maxPrice == 0 ? Integer.MAX_VALUE : maxPrice;
 72             boolQuery.filter(QueryBuilders.rangeQuery("price").gte(minPrice).lte(maxPrice));
 73         }
 74 
 75         // 2.算分函数查询
 76         FunctionScoreQueryBuilder functionScoreQuery = QueryBuilders.functionScoreQuery(
 77                 boolQuery, // 原始查询,boolQuery
 78                 new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{ // function数组
 79                         new FunctionScoreQueryBuilder.FilterFunctionBuilder(
 80                                 QueryBuilders.termQuery("isAD", true), // 过滤条件
 81                                 ScoreFunctionBuilders.weightFactorFunction(10) // 算分函数
 82                         )
 83                 }
 84         );
 85 
 86         // 3.设置查询条件
 87         request.source().query(functionScoreQuery);
 88     }
 89 
 90     private PageResult handleResponse(SearchResponse response) {
 91         SearchHits searchHits = response.getHits();
 92         // 4.1.总条数
 93         long total = searchHits.getTotalHits().value;
 94         // 4.2.获取文档数组
 95         SearchHit[] hits = searchHits.getHits();
 96         // 4.3.遍历
 97         List<HotelDoc> hotels = new ArrayList<>(hits.length);
 98         for (SearchHit hit : hits) {
 99             // 4.4.获取source
100             String json = hit.getSourceAsString();
101             // 4.5.反序列化,非高亮的
102             HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);
103             // 4.6.处理高亮结果
104             // 1)获取高亮map
105             Map<String, HighlightField> map = hit.getHighlightFields();
106             if (map != null && !map.isEmpty()) {
107                 // 2)根据字段名,获取高亮结果
108                 HighlightField highlightField = map.get("name");
109                 if (highlightField != null) {
110                     // 3)获取高亮结果字符串数组中的第1个元素
111                     String hName = highlightField.getFragments()[0].toString();
112                     // 4)把高亮结果放到HotelDoc中
113                     hotelDoc.setName(hName);
114                 }
115             }
116             // 4.8.排序信息
117             Object[] sortValues = hit.getSortValues();
118             if (sortValues.length > 0) {
119                 hotelDoc.setDistance(sortValues[0]);
120             }
121 
122             // 4.9.放入集合
123             hotels.add(hotelDoc);
124         }
125         return new PageResult(total, hotels);
126     }
127 }
View Code
复制代码

 

posted @   一杯水M  阅读(4)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
点击右上角即可分享
微信分享提示