ElasticSearch-SearchRequest

代码解析

service层代码,大致做了4步骤:

  1. 准备Request
  2. DSL语句
  3. 发起Request请求得到响应Response
  4. 响应体解析(排序、高亮)

想都不用向回答我,哪一个是最重要的步骤?是的DSL语句,也就是具体的查询,那么具体的查询怎么写呢?
是的就是这么几行,一个BoolQuery搞定,有点像MyBatisPlus的QueryWrapper,只需要你会关键字(逻辑:must和、should或)(match模糊搜索,term精确搜索,range范围搜索)

        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        boolQuery.must(QueryBuilders.matchQuery("all", requestParam.getKey()));
        boolQuery.filter(QueryBuilders.termQuery("brand", requestParam.getBrand()));
        boolQuery.filter(QueryBuilders.termQuery("city", requestParam.getCity()));
        boolQuery.filter(QueryBuilders.termQuery("starName", requestParam.getStarName()));
        boolQuery.filter(QueryBuilders.rangeQuery("price").gte(requestParam.getMinPrice()).lte(requestParam.getMaxPrice()));

好了会了基础下面的代码能看懂了,

    @Autowired
    private RestHighLevelClient restHighLevelClient;
    /**
     * 完成关键字搜索和分页
     *
     * @param requestParam
     * @return cn.itcast.hotel.pojo.PageResult
     * @author lst
     * @date 2023/11/27 17:03
     */
    public PageResult getHotelList(RequestParams requestParam) throws IOException {
        //1、准备Request
        SearchRequest request = new SearchRequest("hotel");
        //2、DSL语句
        handleDSL(request, requestParam);
        //3、发起Request请求得到响应Response
        SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
        //4、响应体解析,高亮
        return handleResponse(response, HotelDoc.class);
    }
    private void handleDSL(SearchRequest request, RequestParams requestParam) {
        //根据关键字搜索
        keywordSearch(request, requestParam);
        //分页搜索
        fenye(request, requestParam.getPage(), requestParam.getSize());
        //距离排序
        locationOrder(request, requestParam);
    }
    /**
     * 关键字为空查询所有,不为空根据关键字查询
     *
     * @param request
     * @param requestParam
     * @author lst
     * @date 2023/11/28 10:34
     */
    private void keywordSearch(SearchRequest request, RequestParams requestParam) {
        // 1.准备Boolean查询
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        // 1.1.关键字搜索,match查询,放到must中
        String key = requestParam.getKey();
        if (StringUtils.isNotBlank(key)) {
            // 不为空,根据关键字查询
            boolQuery.must(QueryBuilders.matchQuery("all", key));
        } else {
            // 为空,查询所有
            boolQuery.must(QueryBuilders.matchAllQuery());
        }

        // 1.2.品牌
        String brand = requestParam.getBrand();
        if (StringUtils.isNotBlank(brand)) {
            boolQuery.filter(QueryBuilders.termQuery("brand", brand));
        }
        // 1.3.城市
        String city = requestParam.getCity();
        if (StringUtils.isNotBlank(city)) {
            boolQuery.filter(QueryBuilders.termQuery("city", city));
        }
        // 1.4.星级
        String starName = requestParam.getStarName();
        if (StringUtils.isNotBlank(starName)) {
            boolQuery.filter(QueryBuilders.termQuery("starName", starName));
        }
        // 1.5.价格范围
        Integer minPrice = requestParam.getMinPrice();
        Integer maxPrice = requestParam.getMaxPrice();
        if (minPrice != null && maxPrice != null) {
            maxPrice = maxPrice == 0 ? Integer.MAX_VALUE : maxPrice;
            boolQuery.filter(QueryBuilders.rangeQuery("price").gte(minPrice).lte(maxPrice));
        }

        // 2.算分函数查询
        FunctionScoreQueryBuilder functionScoreQuery = QueryBuilders.functionScoreQuery(boolQuery, // 原始查询,boolQuery
                new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.termQuery("isAD", true), ScoreFunctionBuilders.weightFactorFunction(10))});

        // 3.设置查询条件
        request.source().query(functionScoreQuery);
    }

      private void fenye(SearchRequest request, int page, int size) {
        request.source().from((page - 1) * size).size(size);
    }
      private void locationOrder(SearchRequest request, RequestParams requestParam) {
        String location = requestParam.getLocation();
        if (StringUtils.isNotBlank(location)) {
            request.source().sort(SortBuilders.geoDistanceSort("location", new GeoPoint(location)).order(SortOrder.ASC).unit(DistanceUnit.KILOMETERS));
        }
    }

题外话

这里handleResponse方法有一个演变过程,起初我的想法是为了让其更有通用性写成泛型吧,但是考虑到泛型还是得不停的if else 判断是什么实体类强转,与其如此不如让实现某一接口的实体类都能用此方法(这个思想在原公司的分页查询中就有体现),最开始泛型代码如下所示:

private <T> PageResult handleResponse(SearchResponse response, Class<T> documentType) {
        SearchHits searchHits = response.getHits();
        // 4.1.总条数
        long total = searchHits.getTotalHits().value;
        // 4.2.获取文档数组
        SearchHit[] hits = searchHits.getHits();
        // 4.3.遍历
        List<T> documents = new ArrayList<>(hits.length);
        for (SearchHit hit : hits) {
            // 4.4.获取source
            String json = hit.getSourceAsString();
            // 4.5.反序列化,非高亮的
            T document = JSON.parseObject(json, documentType);
            // 4.6.处理高亮结果
            // 1)获取高亮map
            Map<String, HighlightField> map = hit.getHighlightFields();
            if (map != null && !map.isEmpty()) {
                // 2)根据字段名,获取高亮结果
                HighlightField highlightField = map.get("name");
                if (highlightField != null) {
                    // 3)获取高亮结果字符串数组中的第1个元素
                    String hName = highlightField.getFragments()[0].toString();
                    // 4)将高亮结果设置到文档中
                    if (document instanceof HotelDoc) {
                        ((HotelDoc) document).setName(hName);
                    }
                }
            }
            // 4.8.排序信息
            Object[] sortValues = hit.getSortValues();
            if (sortValues.length > 0) {
                if (document instanceof HotelDoc) {
                    ((HotelDoc) document).setDistance(sortValues[0]);
                }
            }
            // 4.9.放入集合
            documents.add(document);
        }
        return new PageResult(total, documents);
    }

后来我问chatgpt如果传入的documentType每增加一种,就得多一个if else 判断这样不好吧。
他告诉我是对的,为了避免使用多个 if 语句进行类型检查,你可以使用 Java 的泛型方法和接口。首先,你可以定义一个接口,表示文档类型的通用接口,然后让 HotelDoc 实现这个接口。接下来,在 handleResponse 方法中,你可以将 T 限定为实现了该接口的类型,并调用接口的方法进行处理。这样,你就可以在不同类型之间共享通用的逻辑,而无需硬编码具体的类型检查。

public interface BaseDoc {
    void setName(String name);

    void setDistance(Object distance);
}
package cn.itcast.hotel.pojo;

import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
public class HotelDoc implements BaseDoc{
    private Long id;
    private String name;
    private String address;
    private Integer price;
    private Integer score;
    private String brand;
    private String city;
    private String starName;
    private String business;
    private String location;
    private String pic;

    private Object distance;

    private Boolean isAD;

    public HotelDoc(Hotel hotel) {
        this.id = hotel.getId();
        this.name = hotel.getName();
        this.address = hotel.getAddress();
        this.price = hotel.getPrice();
        this.score = hotel.getScore();
        this.brand = hotel.getBrand();
        this.city = hotel.getCity();
        this.starName = hotel.getStarName();
        this.business = hotel.getBusiness();
        this.location = hotel.getLatitude() + ", " + hotel.getLongitude();
        this.pic = hotel.getPic();
    }
}

private <T extends Document> PageResult handleResponse(SearchResponse response, Class<T> documentType) {
    SearchHits searchHits = response.getHits();
    // 4.1.总条数
    long total = searchHits.getTotalHits().value;
    // 4.2.获取文档数组
    SearchHit[] hits = searchHits.getHits();
    // 4.3.遍历
    List<T> documents = new ArrayList<>(hits.length);
    for (SearchHit hit : hits) {
        // 4.4.获取source
        String json = hit.getSourceAsString();
        // 4.5.反序列化,非高亮的
        T document = JSON.parseObject(json, documentType);
        // 4.6.处理高亮结果
        // 1)获取高亮map
        Map<String, HighlightField> map = hit.getHighlightFields();
        if (map != null && !map.isEmpty()) {
            // 2)根据字段名,获取高亮结果
            HighlightField highlightField = map.get("name");
            if (highlightField != null) {
                // 3)获取高亮结果字符串数组中的第1个元素
                String hName = highlightField.getFragments()[0].toString();
                // 4)将高亮结果设置到文档中
                document.setName(hName);
            }
        }
        // 4.8.排序信息
        Object[] sortValues = hit.getSortValues();
        if (sortValues.length > 0) {
            document.setDistance(sortValues[0]);
        }
        // 4.9.放入集合
        documents.add(document);
    }
    return new PageResult(total, documents);
}

  • 8
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值