SpringCloud学习笔记(十)ElasticSearch5~6DSL+Demo

10.5 DSL查询语法

10.5.1 DSL Query的分类

ES提供了基于JSON的DSL(Domain Specific Language)来定义查询。常见查询类型:

  • 查询所有:一般测试用 eg.match_all
  • 全文检索查询:利用分词器对用户输入分词,去倒排索引库中匹配 eg.match_query/multi_match_query
  • 精确查询:根据精确词条值查找数据,一般是keyword、数值、日期、boolean等字段eg.ids/range/term
  • 地理查询:eg.geo_distance/geo_bounding_box
  • 复合查询:组合查询条件 eg.bool/funciton_score
image-20230705212426593
image-20230705213153080

不算分效率要高于算分,所以可以尽量把查询条件放在must_notfilter里。

image-20230705215229975

10.5.2 Java API

Java API Client提供的接口方便很多,和原生请求高度一致

image-20230706153601173
image-20230706153655865

10.6 搜索结果处理

排序

RESTful请求

es支持对搜索结果排序,默认根据相关度算分(_score)排序,可排序字段类型:keyword、数值、geo、日期等。

image-20230705220637471
Java API
image-20230706153628263

高亮

搜索结果中的关键字突出显示。

原理:

  • 将搜索结果中的关键字用标签标记出来
  • 在页面中给标签添加css样式
RESTful请求
image-20230705223518609
Java API
image-20230706153947639
image-20230706154028282

分页

RESTful请求

es默认返回top10数据,若查询更多数据则需要分页处理,es通过修改form、size参数控制返回的分页结果。

image-20230705221547531
深度分页问题

es分页原理:eg.from=990,size=10,则es需要查出前1000条数据,然后截取990-1000的数据。

image-20230705221838396

解决方案:

  • search after:分页时需要排序,原理是从上一次的排序值开始查询下一页数据。官方推荐。
  • scroll:原理是将排序数据形成快照保存在内存。官方不推荐。
image-20230705222109300
Java API
image-20230706153814903

Demo

实现功能如下:

1.酒店搜索和分页

分页页码和大小从前端请求中获取,使用RestParams接收前端参数。

2.酒店结果过滤

根据RestParam接收的前端请求参数过滤

3.周边酒店

前端使用高德接口获取当前定位并将位置通过RestParams传到后端,后端通过geoDistance排序。

4.酒店竞价排名

ES的实体类添加isAD字段,更改isAD=true的算分权重。

核心代码如下:

 @Slf4j
 @Service
 public class HotelService extends ServiceImpl<HotelMapper, Hotel> implements IHotelService {
 ​
     @Autowired
     private ElasticsearchClient esClient;
     @Override
     public PageResult search(RequestParams params) {
         try {
             SearchRequest.Builder searchRequestBuilder = new SearchRequest.Builder();
             searchRequestBuilder.index("hotel");
             SearchResponse<HotelDoc> searchResponse = esClient.search(s -> {
                     s.index("hotel");
                     //添加查询条件和广告算分/排序
                     buildBasicQuery(params,s);
                     //分页
                     s.from((params.getPage()-1)* params.getSize()).size(params.getSize());
 ​
                     return s;
                }
                ,HotelDoc.class);
             return handleResponse(searchResponse);
        } catch (IOException e) {
             throw new RuntimeException(e);
        }
    }
 ​
     private void buildBasicQuery(RequestParams params, SearchRequest.Builder searchRequestBuilder) {
         //搜索条件
         BoolQuery.Builder boolQueryBuilder = new BoolQuery.Builder();
         //关键字搜索
         String key = params.getKey();
         if(null ==key|| "".equals(key)){
             boolQueryBuilder.must(m->m.matchAll(new MatchAllQuery.Builder().build()));
        }else{
             boolQueryBuilder.must(m->m.match(mat->mat.field("all").query(key)));
        }
         //城市过滤条件
         if(null!= params.getCity() && !params.getCity().equals("")){
             boolQueryBuilder.filter(f->f.term(t->t.field("city").value(params.getCity())));
        }
         //品牌过滤条件
         if(params.getBrand()!=null && "".equals(params.getBrand())){
             boolQueryBuilder.filter(f->f.term(t->t.field("brand").value(params.getBrand())));
        }
         //星级过滤条件
         if(params.getStarName()!=null && "".equals(params.getStarName())){
             boolQueryBuilder.filter(f->f.term(t->t.field("starName").value(params.getStarName())));
        }
         //价格过滤条件
         if(params.getMinPrice()!=null && params.getMaxPrice()!=null){
             boolQueryBuilder.filter(f->f.range(r->r
                    .field("price")
                    .gte(JsonData.of(params.getMinPrice()))
                    .lte(JsonData.of(params.getMaxPrice()))));
        }
         // searchRequestBuilder.query(q->q.bool(boolQueryBuilder.build()));
 ​
         //广告算分
         FunctionScoreQuery.Builder functionScoreBuilder = new FunctionScoreQuery.Builder();
         functionScoreBuilder
                 //原始查询
                .query(q->q.bool(boolQueryBuilder.build()))
                 //算分条件数组
                .functions(f->f
                         //其中一个算分条件
                        .filter(fl->fl.term(t->t.field("isAD").value(true))).weight(10d)
                )
                 //算分函数
                .boostMode(FunctionBoostMode.Multiply);
         searchRequestBuilder.query(q->q.functionScore(functionScoreBuilder.build()));
 ​
         //排序
         if(null!= params.getLocation() && !params.getLocation().equals("")){
             SortOptions.Builder sortBuilder = new SortOptions.Builder();
             sortBuilder.geoDistance(geo->geo
                    .field("location")
                    .location(new GeoLocation.Builder().text(params.getLocation()).build())
                    .order(SortOrder.Asc)
                    .unit(DistanceUnit.Kilometers)
            );
             searchRequestBuilder.sort(sortBuilder.build());
        }
 ​
    }
 ​
     private PageResult handleResponse(SearchResponse<HotelDoc> response) {
         HitsMetadata<HotelDoc> searchHits = response.hits();
         assert searchHits.total() != null;
         long total = searchHits.total().value();
         List<HotelDoc> hotels = new ArrayList<>();
         for (Hit<HotelDoc> hit : searchHits.hits()) {
             HotelDoc hotelDoc = hit.source();
             //判断是否有距离排序
             List<FieldValue> sort = hit.sort();
             if(sort.size()>0){
                 assert hotelDoc != null;
                 hotelDoc.setDistance(sort.get(0).doubleValue());
            }
             hotels.add(hotelDoc);
        }
         return new PageResult(total,hotels);
    }
 ​
 }
 @RestController
 @RequestMapping("hotel")
 public class HotelController {
 ​
     @Autowired
     private IHotelService hotelService;
 ​
     @PostMapping("list")
     public PageResult search(@RequestBody RequestParams params) {
         return hotelService.search(params);
    }
 }

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注