0


微服务技术栈SpringCloud+RabbitMQ+Docker+Redis+搜索+分布式(五):分布式搜索 ES-中

文章目录


一、DSL查询文档

在这里插入图片描述

1.1 简单查询

# 1.DSL查询
# 1.1 查询所有

GET/hotel/_search
{"query":{"match_all":{}}}

# 1.2 全文检索查询:对用户输入的内容分词后查询,常用于搜索框查询
# 1)match查询 :all字段是在创建hotel索引库时创建的,里面包括brand name busiess字段(copy to)
# 例子:查询hotel中brand、name、businiss中有"外滩"二字的文档
GET/hotel/_search
{"query":{"match":{"all":"外滩"}}}
# 2)muiti_match查询:效果和上面一样
# 例子:查询hotel中brand、name、businiss中有"外滩如家"四字的文档
GET/hotel/_search
{"query":{"multi_match":{"query":"外滩如家","fields":["brand","name","business"]}}}
# 3)match与multi_match的区别在于:match是单字段查询;而multi_match是多字段查询,字段越多性能越差;建议用copy to将多个字段拷到一个字段用match查询

# 1.3 精确查询:一般查找类型为keyword、boolean、数值、日期等字段,不分词
# 1)term:根据词条的精确值查询
# 例子:查询hotel中city="上海"的文档
GET/hotel/_search
{"query":{"term":{"city":{"value":"上海"}}}}
# 2)range:根据值的范围查询
# 例子:查询price在(1000,2000]的文档
GET/hotel/_search
{"query":{"range":{"price":{"gt":1108,"lte":2000}}}}

# 1.4 经纬度查询
# 1)geo_bounding_box:查询geo_point值落在某个矩形范围的所有文档
# 例子:查询hotel中location两个经纬度点矩形范围内内的文档
GET/hotel/_search
{"query":{"geo_bounding_box":{"location":{"top_left":{"lat":31.1,"lon":121.5},"bottom_right":{"lat":30.9,"lon":121.7}}}}}

# 2)geo_distance:查询到指定中心点小于某个距离值的所有文档
# 例子:查询(31.21,121.5)范围内5km的的文档
GET/hotel/_search
{"query":{"geo_distance":{"distance":"5km","location":"31.21, 121.5"}}}

1.2 复合查询

在这里插入图片描述
在这里插入图片描述

# 1.5 复合查询
# 1)function socre:算分函数查询,可以控制文档相关性算分,控制文档的排名
# 例:在all为"外滩"的查询中将"如家"这个品牌的酒店排名靠前一些
GET/hotel/_search
{"query":{"function_score":{"query":{"match":{"all":"外滩"}},"functions":[{"filter":{"term":{"brand":"如家"}},"weight":10}],"boost_mode":"sum"}}}

# 2)布尔查询:组合多个子查询
# must:必须匹配每个子查询,相当于“与”
# should:选择性匹配子查询,相当于“或”
# must_not:必须不匹配【不参与算分】,相当于“非”
# filter:必须匹配【不参与算法】

# 例:查询name包含"如家",价格不高于400,坐标(31.21,121.5)范围内10km的hotel
# 下面代码中,如果将price和location放入must中会参与算分,为了节省性能,一般放在must_not或者filter中
GET/hotel/_search
{"query":{"bool":{"must":[{"match":{"name":"如家"}}],"must_not":[{"range":{"price":{"gt":400}}}],"filter":[{"geo_distance":{"distance":"10km","location":{"lat":31.21,"lon":121.5}}}]}}}

二、搜索结果处理

深度分页问题
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

# 2. 搜索结果处理
# 2.1 排序:es默认根据算分排序。可以用来排序的字段有:keyword、数值、坐标、日期
# 例1:对hotel数据按用户评价score降序,相同评价按价格price升序
GET/hotel/_search
{"query":{"match_all":{}},"sort":[{"score":"desc"},{"price":"asc"}]}

# 例2:对hotel数据按你的坐标位置(115.450059,38.866053)距离升序排序
# 获取经纬度的方式:https://lbs.amap.com/demo/jsapi-v2/example/map/click-to-get-lnglat/GET/hotel/_search
{"query":{"match_all":{}},"sort":[{"_geo_distance":{"location":{"lat":38.866053,"lon":115.45005},"order":"asc","unit":"km"}}]}

# 2.2 分页:es默认返回top10的数据,想要查询更多需要设置
# from表示分页开始位置,默认为0;size表示期望获取文档数

GET/hotel/_search
{"query":{"match_all":{}},"from":5,"size":1,"sort":[{"price":"asc"}]}

# 2.3 高亮:将搜索结果的搜索关键字突出显示
# 原理:将搜索结果的关键字用标签标记出来,在页面中给标签添加css样式
# 注意:默认情况下ES搜索字段必须与高亮字段保持一致,而下面搜索字段为all,高亮字段为name,虽然all包括name,但是需要设置require_field_match=falseGET/hotel/_search
{"query":{"match":{"all":"如家"}},"highlight":{"fields":{"name":{"require_field_match":"false","pre_tags":"<em>","post_tags":"</em>"}}}}

在这里插入图片描述

三、RestClient演示 查询与结果分析

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

@SpringBootTestclassHotelSearchTest{privateRestHighLevelClient client;@BeforeEachvoidsetUp(){
        client =newRestHighLevelClient(RestClient.builder(HttpHost.create("http://192.168.1.101:9200")));}@AfterEachvoidtearDown()throwsIOException{
        client.close();}/**
     * 解析json文档
     */privatevoidhandleResponse(SearchResponse response){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){// 4.4.获取sourceString json = hit.getSourceAsString();// 4.5.反序列化,非高亮的HotelDoc hotelDoc =JSON.parseObject(json,HotelDoc.class);// 4.6.处理高亮结果// 1)获取高亮mapMap<String,HighlightField> map = hit.getHighlightFields();// 2)根据字段名,获取高亮结果HighlightField highlightField = map.get("name");// 3)获取高亮结果字符串数组中的第1个元素String hName = highlightField.getFragments()[0].toString();// 4)把高亮结果放到HotelDoc中
            hotelDoc.setName(hName);// 4.7.打印System.out.println(hotelDoc);System.out.println(json);}}/**
     * 查询所有文档
     */@TestvoidtestMatchAll()throwsIOException{// 1.准备requestSearchRequest request =newSearchRequest("hotel");// 2.准备请求参数
        request.source().query(QueryBuilders.matchAllQuery());// 3.发送请求,得到响应SearchResponse response = client.search(request,RequestOptions.DEFAULT);// 4.结果解析handleResponse(response);}/**
     * 全文检索查询:match、multi_match
     */@TestvoidtestMatch()throwsIOException{// 1.准备requestSearchRequest request =newSearchRequest("hotel");// 2.准备请求参数//request.source().query(QueryBuilders.matchQuery("all", "外滩如家"));
        request.source().query(QueryBuilders.multiMatchQuery("外滩如家","name","brand","city"));// 3.发送请求,得到响应SearchResponse response = client.search(request,RequestOptions.DEFAULT);// 4.结果解析handleResponse(response);}/**
     * 1.精确查询:term、range
     * 2.boolean组合查询
     * 查询city为杭州,price>=250的文档
     */@TestvoidtestBool()throwsIOException{// 1.准备requestSearchRequest request =newSearchRequest("hotel");// 2.准备请求参数/*
         BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        // 2.1.must
        boolQuery.must(QueryBuilders.termQuery("city", "上海"));
        // 2.2.filter
        boolQuery.filter(QueryBuilders.rangeQuery("price").lte(250));
        request.source().query(boolQuery);
        */

        request.source().query(QueryBuilders.boolQuery().must(QueryBuilders.termQuery("city","上海")).filter(QueryBuilders.rangeQuery("price").lte(250)));// 3.发送请求,得到响应SearchResponse response = client.search(request,RequestOptions.DEFAULT);// 4.结果解析handleResponse(response);}/**
     * 排序和分页
     */@TestvoidtestSortAndPage()throwsIOException{int page =2,size =5;// 1.准备requestSearchRequest request =newSearchRequest("hotel");// 2.准备请求参数// 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(size);// 3.发送请求,得到响应SearchResponse response = client.search(request,RequestOptions.DEFAULT);// 4.结果解析handleResponse(response);}/**
     * 结果高亮
     */@TestvoidtestHighlight()throwsIOException{// 1.准备requestSearchRequest request =newSearchRequest("hotel");// 2.准备请求参数// 2.1.query
        request.source().query(QueryBuilders.matchQuery("all","外滩如家"));// 2.2.高亮
        request.source().highlighter(newHighlightBuilder().field("name").requireFieldMatch(false));// 3.发送请求,得到响应SearchResponse response = client.search(request,RequestOptions.DEFAULT);// 4.结果解析handleResponse(response);}}

四、案例

4.1 问题解析

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

4.2 代码

在这里插入图片描述

4.2.1 实体bean

PageResult.java

响应结果类:由搜索框得到的查询结果类

/**
 * 响应结果类:由搜索框得到的查询结果类
 */@DatapublicclassPageResult{privateLong total;// 总条数privateList<HotelDoc> hotels;// 酒店信息publicPageResult(){}publicPageResult(Long total,List<HotelDoc> hotels){this.total = total;this.hotels = hotels;}}
RequestParams.java

请求参数类:搜索框中有哪些参数

/**
 * 请求参数类:搜索框中有哪些参数
 */@DatapublicclassRequestParams{privateString key;// 搜索关键字privateInteger page;// 当前页码privateInteger size;// 每页大小privateString sortBy;// 排序字段privateString brand;// 品牌privateString city;// 城市privateString starName;// 星级privateInteger minPrice;// 最低价格privateInteger maxPrice;// 最高价格privateString location;// 位置}

4.2.2 控制层

@RestController@RequestMapping("hotel")publicclassHotelController{@AutowiredprivateIHotelService hotelService;@PostMapping("list")publicPageResultsearch(@RequestBodyRequestParams params){return hotelService.search(params);}}

4.2.3 业务service

publicinterfaceIHotelServiceextendsIService<Hotel>{PageResultsearch(RequestParams params);}
@Slf4j@ServicepublicclassHotelServiceextendsServiceImpl<HotelMapper,Hotel>implementsIHotelService{@AutowiredprivateRestHighLevelClient restHighLevelClient;@OverridepublicPageResultsearch(RequestParams params){try{// 1.准备RequestSearchRequest request =newSearchRequest("hotel");// 2.准备请求参数// 2.1.多条件查询和过滤buildBasicQuery(params, request);// 2.2.分页int page = params.getPage();int size = params.getSize();
            request.source().from((page -1)* size).size(size);/**
             * 2.3.距离排序
             */String location = params.getLocation();if(StringUtils.isNotBlank(location)){// 不为空则查询
                request.source().sort(SortBuilders.geoDistanceSort("location",newGeoPoint(location)).order(SortOrder.ASC).unit(DistanceUnit.KILOMETERS));}// 3.发送请求SearchResponse response = restHighLevelClient.search(request,RequestOptions.DEFAULT);// 4.解析响应returnhandleResponse(response);}catch(IOException e){thrownewRuntimeException("搜索数据失败", e);}}privatevoidbuildBasicQuery(RequestParams params,SearchRequest request){// 1.准备Boolean复合查询BoolQueryBuilder boolQuery =QueryBuilders.boolQuery();/**
         * 1.查询关键字
         * must参与 算分
         */// 1.1.关键字搜索,match查询,放到must中String key = params.getKey();if(StringUtils.isNotBlank(key)){// 不为空,根据关键字查询
            boolQuery.must(QueryBuilders.matchQuery("all", key));}else{// 为空,查询所有
            boolQuery.must(QueryBuilders.matchAllQuery());}/**
         * 2.条件过滤:多条件复合查询
         * 根据 “品牌 城市 星级 价格范围” 过滤数据
         * filter不参与 算分
         */// 1.2.品牌String brand = params.getBrand();if(StringUtils.isNotBlank(brand)){// 不为空则查询
            boolQuery.filter(QueryBuilders.termQuery("brand", brand));}// 1.3.城市String city = params.getCity();if(StringUtils.isNotBlank(city)){// 不为空则查询
            boolQuery.filter(QueryBuilders.termQuery("city", city));}// 1.4.星级String starName = params.getStarName();if(StringUtils.isNotBlank(starName)){// 不为空则查询
            boolQuery.filter(QueryBuilders.termQuery("starName", starName));}// 1.5.价格范围Integer minPrice = params.getMinPrice();Integer maxPrice = params.getMaxPrice();if(minPrice !=null&& maxPrice !=null){// 不为空则查询
            maxPrice = maxPrice ==0?Integer.MAX_VALUE: maxPrice;
            boolQuery.filter(QueryBuilders.rangeQuery("price").gte(minPrice).lte(maxPrice));}/**
         * 3.算分函数查询
         * 置顶功能:给你置顶的酒店添加一个标记,并按其算分
         */FunctionScoreQueryBuilder functionScoreQuery =QueryBuilders.functionScoreQuery(
                boolQuery,// 原始查询,boolQuerynewFunctionScoreQueryBuilder.FilterFunctionBuilder[]{// function数组newFunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.termQuery("isAD",true),// 过滤条件ScoreFunctionBuilders.weightFactorFunction(10)// 算分函数)});/**
         * 4.设置查询条件
          */
        request.source().query(functionScoreQuery);}privatePageResulthandleResponse(SearchResponse response){SearchHits searchHits = response.getHits();// 4.1.总条数long total = searchHits.getTotalHits().value;// 4.2.获取文档数组SearchHit[] hits = searchHits.getHits();// 4.3.遍历List<HotelDoc> hotels =newArrayList<>(hits.length);for(SearchHit hit : hits){// 4.4.获取sourceString json = hit.getSourceAsString();// 4.5.反序列化,非高亮的HotelDoc hotelDoc =JSON.parseObject(json,HotelDoc.class);// 4.6.处理高亮结果// 1)获取高亮mapMap<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)把高亮结果放到HotelDoc中
                    hotelDoc.setName(hName);}}// 4.8.排序信息Object[] sortValues = hit.getSortValues();// 获取排序结果if(sortValues.length >0){/**
                 * 由于该程序是根据距离[酒店距你选择位置的距离]进行排序,所以排序结果为距离
                 */
                hotelDoc.setDistance(sortValues[0]);}// 4.9.放入集合
            hotels.add(hotelDoc);}returnnewPageResult(total, hotels);}}

4.2.4 启动类

@MapperScan("cn.itcast.hotel.mapper")@SpringBootApplicationpublicclassHotelDemoApplication{publicstaticvoidmain(String[] args){SpringApplication.run(HotelDemoApplication.class, args);}@BeanpublicRestHighLevelClientrestHighLevelClient(){returnnewRestHighLevelClient(RestClient.builder(HttpHost.create("http://192.168.1.101:9200")));// 服务器IP+端口9200}}

本文转载自: https://blog.csdn.net/shendaiyan/article/details/136616190
版权归原作者 一万年可长否 所有, 如有侵权,请联系我们删除。

“微服务技术栈SpringCloud+RabbitMQ+Docker+Redis+搜索+分布式(五):分布式搜索 ES-中”的评论:

还没有评论