elasticsearch搜索功能

  • 分布式搜索elasticsearch搜索功能
    • 1.DSL查询文档
      • 1.1 DSL查询分类
      • 1.2 全文检索查询
      • 1.3 精准查询
      • 1.4 地理坐标查询
      • 1.5 组合查询
        • 1.5.1 function score
        • 1.5.2 Boolean Query
    • 2.搜索结果处理
      • 2.1 排序
      • 2.2 分页
      • 2.3 高亮
      • 2.4 总结
    • 3.RestClient查询文档
      • 3.1 快速入门
      • 3.2 match查询
      • 3.3 精确查询
      • 3.4 复合查询
      • 3.5 排序、分页、高亮
    • 4.旅游网案例
      • 4.1 酒店搜索和分页
      • 4.2 酒店结果过滤
      • 4.3 我周边的酒店
      • 4.4 酒店竞价排名

学习地址

分布式搜索elasticsearch搜索功能

1.DSL查询文档

1.1 DSL查询分类

常见的查询类型包括:

  • 查询所有:查询出所有数据,一般测试试用。如:mathch_all
  • 全文检索(full text)查询:利用分词器对用户输入内容分词,然后去倒排索引库中匹配。例如:
    - match_query
    - multi_match_query
  • 精确查询:根据精确词条值查找数据,一般是查找keyword、数值、日期、boolean等类型字段。例如:
    • ids
    • range
    • term
  • 地理(geo)查询:根据经纬度查询。例如:
    • geo_distance
    • geo_bounding_box
  • 复合(compund)查询:复合查询可以将上述各种查询条件组合起来,合并查询条件。例如:
    • bool
    • function_score

查询的基本语法如下:

GET /索引库名/_search
{"query": {"查询条件": {"查询条件": "条件值"}}
}

例子:

// 查询所有
GET /indexName/_search
{"query": {"match_all: {}}
}

1.2 全文检索查询

全文检索查询,会对用户输入内容分词,常用于搜索框搜索,

match查询: 全文检索查询的一种,会对用户输入内容分词,然后去倒排索引库检索,语法:

GET  /indexName/_search
{"query": {"match": {"FIELD": "TEXT"}}
}

multi_match: 与match查询类似,只不过允许同时查询多个字段,语法:

GET /indexName/_search
{"query": {"multi_match": {"query": "TEXT","fields": ["FIELD1","FIELD2"]}}
}

总结:match和multi_match的区别是什么?

  • match:根据一个字段查询
  • multi_match:根据多个字段查询,参与查询字段越多,查询性能越差

1.3 精准查询

精确查找一般是查找keyword、数值、日期、boolean等类型字段。所以不会对搜索条件分词。常见的有:

  • term: 根据词条精确值查找
//term 查询
GET /indexName/_search
{"query":{"term": {"FIELD": { "value": "VAULE"}}}
}

示例:

# term查询
GET /hotel/_search
{"query": {"term": {"city": {"value": "上海"}}}
}
  • range: 根据值的范围查找
//range 查询
GET /indexName/_search
{"query":{"range": {"FIELD": {    "gte": 10,"lte": 20}}}
}

示例:

# range查询
GET /hotel/_search
{"query": {"range": {"price": {"gte": 100,"lte": 300}}}
}

总结:精确查询常见的有哪些?

  • term查询:根据词条精确匹配,一般搜索keyword类型、数值类型、布尔类型、日期类型字段
  • range查询:根据数值范围查询,可以是数值、日期的范围

1.4 地理坐标查询

根据经纬度查询。常见的使用场景包括:

  • 携程:查询附近的酒店
  • 滴滴:搜索附近的出租车
  • 微信:搜索附近的人

例如:

  • geo_bounding_box:查询geo_point值落在某个矩形范围的所有文档
//geo_bounding_box查询
GET /indexName/_search
{"query": {"geo_bounding_box": {"FIELD": {"top_left": {"lat": 31.1,"lon": 121.5},"bottom_right": {"lat": 30.9,"lon": 127.7}}}}
}

示例:

GET /hotel/_search
{"query": {"geo_bounding_box": {"location": {"top_left": {"lat": 31.1,"lon": 121.5},"bottom_right": {"lat": 30.9,"lon": 127.7}}}}
}
  • geo_distance:查询到指定中心点小于某个距离的所有文档
// geo_distance 查询
GET /indexName/_search
{"query": {"geo_distance": {"distance": "15km","FIELD": "31.21,125.5"       }}
}

示例:

# distance查询
GET /hotel/_search
{"query": {"geo_distance": {"distance": "15km","location" : "31.21, 121.5"}}
}

1.5 组合查询

复合(compound)查询:复合查询可以将其他简单查询组合起来,实现更复杂的搜索逻辑,例如:

1.5.1 function score

function score: 算分函数查询,可以控制文档相关性算分,控制文档排名。例如百度竞价

相关性算法

当我们利用macth查询时,文档结果会根据搜索词条的关联度打分(_score),返回结果是按照分值降序排列。例如,我们搜索“虹桥如家”,结果如下:


elasticsearch中的相关性打分算法是什么?

  • TF-IDF:在elasticsearch5.0之前,会随着词频增加而越来越大
  • BM25:在elasticsearch5.0之后,会随着词频增加而增大,但增大的曲线会趋于水平

使用function score query,可以修改文档相关性算法(query score),根据新得到的算分排序。

案例:给“如家”这个品牌的酒店排名靠前一点
把这个问题翻译一下,function score需要的三要素:
1.哪些文档需要算分加权?
     品牌为如家的酒店
2.算分函数是什么?
     weight就可以
3.加权模式是什么?
     求和

# function score查询
GET /hotel/_search
{"query": {"function_score": {"query": {"match": {"all": "外滩"}},"functions": [{"filter": {"term": {"brand": "如家"}},"weight": 10}],"boost_mode": "sum"}}
}

1.5.2 Boolean Query

布尔查询是一个或多个查询子句的组合。子查询的组合方式有:

  • must:必须匹配每个子查询,类似“与”
  • should:选择性匹配子查询,类似“或”
  • must_not:必须不匹配,不参与算分,类似“非”
  • filter:必须匹配,不参与算分

案例:利用bool查询实现功能

需求:搜索名字包含:“如家”,价格不高于400,在坐标31.21,121.5周围10km范围内的酒店。

# bool
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}}}]}}
}

总结:bool查询有几种逻辑关系?

  • must:必须匹配的条件,可以理解为“与”
  • should:选择性匹配的条件,可以理解为“或”
  • must_not:必须不匹配的条件,不参与打分
  • filter:必须匹配的条件,不参与打分

2.搜索结果处理

2.1 排序

elasticsearch支持对搜索结果排序,默认是根据相关度算分(_score)来排序。可以排序字段类型有:keyword类型、数值类型、地理坐标类型、日期类型等。

案例:对酒店数据按照用户评价降序排序,评价相同的按照价格升序排序

评价是score字段,价格是price字段,按照顺序添加两个排序规则即可。

# sort排序
GET /hotel/_search
{"query": {"match_all": {}},"sort": [{"score": "desc"},{"price": "asc"}]
}

案例:实现对酒店数据按照到你的位置坐标的距离升序排序

# 121.612085,31.034647周围的酒店,距离升序排序GET /hotel/_search
{"query": {"match_all": {}},"sort": [{"_geo_distance": {"location": {"lat": 31.034647,"lon": 121.612085},"order": "asc","unit": "km"}}]
}

2.2 分页

elasticserach默认情况下返回top10的数据。而如果要查询更多数据就需要修改分页参数了。
elasticserach中通过修改from、size参数来控制要返回的分页结果:

GET /hotel/_search
{"query": {"match_all": {}},"sort": [{"price": "asc"}],"from": 10,  //分页开始位置,默认0"size": 10 //期望获取的文档总数
}


ES是分布式的,所以会面临深度分页问题。例如按price排序后,获取from=990,size=10的数据:

  1. 首先在每个数据分片上都排序并查询前1000条文档。
  2. 然后将所有节点的结果聚合,在内存中重新排序选出前1000条文档
  3. 最后从这1000条中,选取从990开始的10条文档

    如果搜索页数过深,或者结果集(from + size)越大,对内存和CPU的效果也越高,因此ES设定结果集查询的上限是10000

深度分页解决方案

针对深度分页,ES提供了两种解决方案,

  • search after:分也时需要排序,原理是从上一次的排序值开始,查询下一页数据。官方推荐使用的方式。
  • scroll:原理将排序数据形成快照,保存在内存。官方已经不推荐使用。

总结:

  • from + size:

    1. 优点:支持随机翻页
    2. 缺点:深度分页问题,默认查询上限(from + size)是10000
    3. 场景:百度、经度、谷歌、淘宝这样的随机翻页搜索
  • after serach:
    1. 优点:没有查询上限(单次查询的size不超过10000)
    2. 缺点:只能向后逐步查询,不支持随机翻页
    3. 场景:没有随机翻页需求的搜索,例如手机向下滚动翻页
  • scroll:
    1. 优点:没有查询上限(单词查询的size不超过10000)
    2. 缺点:会有额外的内存消耗,并且搜索结果是非实时的
    3. 场景:海量数据的获取和迁移。从ES7.1开始不推荐,建议用after serach方案。

2.3 高亮

高亮:就是在搜索结果中把搜索关键字突出显示。
原理是这样的:

  • 将搜索结果中的关键字用标签标记起来
  • 在页面中给标签加css样式

    语法:
GET /hotel/_search
{"query": {"match": {"FIELD": "TEXT"}},"highlight": {"fields": {    //指定要高亮的字段"FIELD" {"pre_tags": "<em>",  //用来标记高亮字段的前置标签"post_tags": :"</em>"  //用来标记高亮字段的后置标签}}}
}

示例:

# 高亮查询
GET /hotel/_search
{"query": {"match": {"all": "如家"}},"highlight": {"fields": {"name": {"require_field_match": "false"}}}
}

2.4 总结

搜索结果处理整体语法:

GET /hotel/_search
{"query": {"match": {"all": "如家"}},"from": 0,"size": 20,"sort": [{"price": "asc"},{"_geo_distance": { "location": "31,121","order": "asc","unit": "km"}}],"highlight": {"fields": {"name": {"require_field_match": "false"}}}
}

3.RestClient查询文档

3.1 快速入门

通过match_all来演示基本的API,先看请求DSL的组织

通过match_all来演示下基本的API,再看结果的解析:

    @Testvoid testMatchAll() throws IOException {//1.准备RequestSearchRequest request = new SearchRequest("hotel");//2.准备DSLrequest.source().query(QueryBuilders.matchAllQuery());//3.发送请求SearchResponse response = client.search(request, RequestOptions.DEFAULT);//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) {//获取文档sourceString json = hit.getSourceAsString();//反序列化JSON.parseObject(json, HotelDoc.class);}System.out.println(response);}

RestAPI中其中构建DSL是通过HighLevelRestClient中的resource()来实现的,其中包含了查询、排序、分页、高亮等所有功能:

总结:查询的基本步骤是

  1. 创建SearchRequest对象
  2. 准备Request.source(),也就是DSL。
    ①QueryBuilders来构建查询条件
    ②传入Request.source()的query()方法
  3. 发送请求,得到结果
  4. 解析结果(参考JSON结果,从外到内,逐层解析)

3.2 match查询

全文检索的match和multi_match查询与macth_all的API基本一致。差别是查询条件,也就是query的部分。
同样是利用QueryBuilders提供的方法:

    @Testvoid testMatch() throws IOException {//1.准备RequestSearchRequest request = new SearchRequest("hotel");//2.准备DSLrequest.source().query(QueryBuilders.matchQuery("all", "如家"));//3.发送请求SearchResponse response = client.search(request, RequestOptions.DEFAULT);//4.解析响应SearchHits searchHits = response.getHits();//4.1获取总条数long value = searchHits.getTotalHits().value;System.out.println("共搜索到" + value + "条数据");//4.2文档数组SearchHit[] hits = searchHits.getHits();//4.3遍历for (SearchHit hit : hits) {//获取文档sourceString json = hit.getSourceAsString();//反序列化JSON.parseObject(json, HotelDoc.class);}System.out.println(response);}

3.3 精确查询

精确查找常见的有term查询和range查询,同样利用QueryBuilders实现:

   @Testvoid testMatchPrecise() throws IOException {//1.准备RequestSearchRequest request = new SearchRequest("hotel");//2.准备DSLrequest.source().query(QueryBuilders.termQuery("city", "杭州"));request.source().query(QueryBuilders.rangeQuery("price").gte(100).lte(150));//3.发送请求SearchResponse response = client.search(request, RequestOptions.DEFAULT);//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) {//获取文档sourceString json = hit.getSourceAsString();//反序列化JSON.parseObject(json, HotelDoc.class);System.out.println(response);}}

3.4 复合查询

复合查询-boolean query
精确查询常见的有term查询和range查询,同样利用QueryBuilders实现:

@Testvoid testBool() throws IOException {//1.准备requestSearchRequest request = new SearchRequest("hotel");//2.准备DSL//2.1准备BooleanQueryBoolQueryBuilder boolQuery = QueryBuilders.boolQuery();//2.2添加termboolQuery.must(QueryBuilders.termQuery("city", "上海"));//2.3添加filterboolQuery.filter(QueryBuilders.rangeQuery("price").lte(250));request.source().query(boolQuery);//3.发送请求SearchResponse response = client.search(request, RequestOptions.DEFAULT);//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) {//获取文档sourceString json = hit.getSourceAsString();//反序列化JSON.parseObject(json, HotelDoc.class);System.out.println(response);}}

3.5 排序、分页、高亮

搜索结果的排序和分页是与query同级的参数,对应的API如下:

    @Testvoid testPageAndSort() throws IOException {//页码,每页大小int page = 2, size = 5;//1.准备RequestSearchRequest request = new SearchRequest("hotel");//2.准备DSL//2.1 queryrequest.source().query(QueryBuilders.matchAllQuery());//2.2排序sortrequest.source().sort("price", SortOrder.ASC);//2.3分页from、sizerequest.source().from((page - 1) * size).size(5);//3.发送请求SearchResponse response = client.search(request, RequestOptions.DEFAULT);//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) {//获取文档sourceString json = hit.getSourceAsString();//反序列化JSON.parseObject(json, HotelDoc.class);System.out.println(response);}}

高亮API包括请求DSL构建和结果解析两部分。

高亮结果解析:
高亮的结果处理相对于比较麻烦:

    @Testvoid testHightLight() throws IOException {//1.准备RequestSearchRequest request = new SearchRequest("hotel");//2.准备DSL//2.1queryrequest.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.解析响应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) {//获取文档sourceString 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(response);}}

总结:

  • 所有搜索DSL的构建,记住一个API:SearchRequest的source()方法。
  • 高亮结果解析是参考JSON结果,逐层解析

4.旅游网案例

4.1 酒店搜索和分页

实现黑马旅游网的酒店搜索功能,完成关键字搜索和分页
实现步骤如下:

  1. 定义实体类,接收前端信息
@Data
public class RequestParams {private String key;private Integer page;private Integer size;private String sortBy;
}
  1. 定义Hotelcontroller接口,接收页面请求,调用IHotelService的search方法

    • 请求方式:Post
    • 请求路径:/hotel/list
    • 请求参数:对象,类型为RequestParam
    • 返回值:PageResult,包含两个属性
      ①Long total:总条数
      ②List hotels:酒店数据
@RestController
@RequestMapping("/hotel")
public class HotelController {@Autowiredprivate IHotelService hotelService;@RequestMapping("/list")public PageResult search(@RequestBody RequestParams params) throws IOException {return hotelService.search(params);}
}
@Data
public class PageResult {private Long total;private List<HotelDoc> hotels;public PageResult() {}public PageResult(Long total, List<HotelDoc> hotels) {this.total = total;this.hotels = hotels;}
}
  1. 定义IHotelService的search方法,利用match查询实现根据关键字酒店搜索酒店信息
@Service
public class HotelService extends ServiceImpl<HotelMapper, Hotel> implements IHotelService {@Autowiredprivate RestHighLevelClient client;@Overridepublic PageResult search(RequestParams params) {try {//1.准备RequestSearchRequest request = new SearchRequest("hotel");//2.准备DSL//2.1关键字搜索queryString key = params.getKey();if (key == null || "".equals(key)) {request.source().query(QueryBuilders.matchAllQuery());} else {request.source().query(QueryBuilders.matchQuery("all", key));}//2.2分页int page = params.getPage();int size = params.getSize();request.source().from((page - 1) * size).size(size);//3.发送请求得到响应SearchResponse response = client.search(request, RequestOptions.DEFAULT);return handleResponse(response);} catch (IOException e) {throw new RuntimeException(e);}}private PageResult handleResponse(SearchResponse response) {//4.解析响应SearchHits searchHits = response.getHits();//4.1获取总条数long total = searchHits.getTotalHits().value;//4.2获取文档数组SearchHit[] hits = searchHits.getHits();//4.3遍历List<HotelDoc> hotels = new ArrayList<>();for (SearchHit hit : hits) {//获取文档sourceString json = hit.getSourceAsString();//反序列化HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);hotels.add(hotelDoc);}//4.4封装返回return new PageResult(total, hotels);}
}

4.2 酒店结果过滤

添加品牌、城市、星级、价格等过滤功能
步骤:

  1. 修改RequestParams类,添加brand、city、startName、minPrice、maxPrice等参数
@Data
public class RequestParams {private String key;private Integer page;private Integer size;private String sortBy;private String brand;private String startName;private String city;private Integer minPrice;private Integer maxPrice;
}
  1. 修改search方法的实现,在关键字搜索时,如果brand等参数存在,对其做过滤
    过滤条件包括:
  • ctiy精确包括
  • brand精确匹配
  • starName精确匹配
  • price范围过滤

注意事项:

  • 多个条件之间是AND关系,组合多条件用boolQuery
  • 参数存在才需要过滤,做好非空判断
@Service
public class HotelService extends ServiceImpl<HotelMapper, Hotel> implements IHotelService {@Autowiredprivate RestHighLevelClient client;@Overridepublic PageResult search(RequestParams params) {try {//1.准备RequestSearchRequest request = new SearchRequest("hotel");//2.准备DSL//2.1关键字搜索querybuildBasicQuery(params, request);//2.2分页int page = params.getPage();int size = params.getSize();request.source().from((page - 1) * size).size(size);//3.发送请求得到响应SearchResponse response = client.search(request, RequestOptions.DEFAULT);return handleResponse(response);} catch (IOException e) {throw new RuntimeException(e);}}private void buildBasicQuery(RequestParams params, SearchRequest request) {//构建BooleanQueryBoolQueryBuilder boolQuery = QueryBuilders.boolQuery();// must部分 关键字搜索String key = params.getKey();if (key == null || "".equals(key)) {boolQuery.must(QueryBuilders.matchAllQuery());} else {boolQuery.must(QueryBuilders.matchQuery("all", key));}//  filter条件过滤// 城市if (params.getCity() != null && !params.getCity().equals("")) {boolQuery.filter(QueryBuilders.termQuery("city", params.getCity()));}// 品牌if (params.getBrand() != null && !params.getBrand().equals("")) {boolQuery.filter(QueryBuilders.termQuery("brand", params.getBrand()));}// 星级if (params.getStartName() != null && !params.getStartName().equals("")) {boolQuery.filter(QueryBuilders.termQuery("startName", params.getStartName()));}//价格if (params.getMinPrice() != null && params.getMaxPrice() != null) {boolQuery.filter(QueryBuilders.rangeQuery("price").gte(params.getMinPrice()).lte(params.getMaxPrice()));}request.source().query(boolQuery);}private PageResult handleResponse(SearchResponse response) {//4.解析响应SearchHits searchHits = response.getHits();//4.1获取总条数long total = searchHits.getTotalHits().value;//4.2获取文档数组SearchHit[] hits = searchHits.getHits();//4.3遍历List<HotelDoc> hotels = new ArrayList<>();for (SearchHit hit : hits) {//获取文档sourceString json = hit.getSourceAsString();//反序列化HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);hotels.add(hotelDoc);}//4.4封装返回return new PageResult(total, hotels);}
}

4.3 我周边的酒店

前端页面点击定位后,会将你的位置发送给后台:

根据这个坐标,将酒店结果按照到这个点的距离升序排序。

距离排序:
距离排序与普通字段排序有所差异,API如下:

实现思路如下:

  • 修改RequestParams参数,接收location字段
@Data
public class RequestParams {private String key;private Integer page;private Integer size;private String sortBy;private String brand;private String startName;private String city;private Integer minPrice;private Integer maxPrice;private  String location;
}
  • 修改search方法业务逻辑,如果location有值,添加根据geo_distance排序的功能
@Service
public class HotelService extends ServiceImpl<HotelMapper, Hotel> implements IHotelService {@Autowiredprivate RestHighLevelClient client;@Overridepublic PageResult search(RequestParams params) {try {//1.准备RequestSearchRequest request = new SearchRequest("hotel");//2.准备DSL//2.1关键字搜索querybuildBasicQuery(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 (location != null && !location.equals("")) {request.source().sort(SortBuilders.geoDistanceSort("location", new GeoPoint(location)).order(SortOrder.ASC).unit(DistanceUnit.KILOMETERS));}//3.发送请求得到响应SearchResponse response = client.search(request, RequestOptions.DEFAULT);return handleResponse(response);} catch (IOException e) {throw new RuntimeException(e);}}private void buildBasicQuery(RequestParams params, SearchRequest request) {//构建BooleanQueryBoolQueryBuilder boolQuery = QueryBuilders.boolQuery();// must部分 关键字搜索String key = params.getKey();if (key == null || "".equals(key)) {boolQuery.must(QueryBuilders.matchAllQuery());} else {boolQuery.must(QueryBuilders.matchQuery("all", key));}//  filter条件过滤// 城市if (params.getCity() != null && !params.getCity().equals("")) {boolQuery.filter(QueryBuilders.termQuery("city", params.getCity()));}// 品牌if (params.getBrand() != null && !params.getBrand().equals("")) {boolQuery.filter(QueryBuilders.termQuery("brand", params.getBrand()));}// 星级if (params.getStartName() != null && !params.getStartName().equals("")) {boolQuery.filter(QueryBuilders.termQuery("startName", params.getStartName()));}//价格if (params.getMinPrice() != null && params.getMaxPrice() != null) {boolQuery.filter(QueryBuilders.rangeQuery("price").gte(params.getMinPrice()).lte(params.getMaxPrice()));}request.source().query(boolQuery);}private PageResult handleResponse(SearchResponse response) {//4.解析响应SearchHits searchHits = response.getHits();//4.1获取总条数long total = searchHits.getTotalHits().value;//4.2获取文档数组SearchHit[] hits = searchHits.getHits();//4.3遍历List<HotelDoc> hotels = new ArrayList<>();for (SearchHit hit : hits) {//获取文档sourceString json = hit.getSourceAsString();//反序列化HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);//获取排序值Object[] sortValues = hit.getSortValues();if (sortValues.length > 0) {Object sortValue = sortValues[0];hotelDoc.setDistance(sortValue);}hotels.add(hotelDoc);}//4.4封装返回return new PageResult(total, hotels);}
}

4.4 酒店竞价排名

让指定的酒店在搜索结果中排名置顶
给需要置顶的酒店文档添加一个标记,然后利用function score给带有标记的文档增加权重。

步骤:
1.给HotelDoc类添加isAD字段,Boolean类型

private boolean isAD;

2.挑选几个酒店,给它的文档数据添加isAD字段,值为true

# 添加isAD字段
POST /hotel/_update/2056126831
{"doc":{"isAD": true}
}

3.修改search方法,添加function score功能,给isAD值为true的酒店增加权重

组合查询-function score
Function score 查询可以控制文档的相关性算分,使用方式如下:

@Service
public class HotelService extends ServiceImpl<HotelMapper, Hotel> implements IHotelService {@Autowiredprivate RestHighLevelClient client;@Overridepublic PageResult search(RequestParams params) {try {//1.准备RequestSearchRequest request = new SearchRequest("hotel");//2.准备DSL//2.1关键字搜索querybuildBasicQuery(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 (location != null && !location.equals("")) {request.source().sort(SortBuilders.geoDistanceSort("location", new GeoPoint(location)).order(SortOrder.ASC).unit(DistanceUnit.KILOMETERS));}//3.发送请求得到响应SearchResponse response = client.search(request, RequestOptions.DEFAULT);return handleResponse(response);} catch (IOException e) {throw new RuntimeException(e);}}private void buildBasicQuery(RequestParams params, SearchRequest request) {//构建BooleanQueryBoolQueryBuilder boolQuery = QueryBuilders.boolQuery();// must部分 关键字搜索String key = params.getKey();if (key == null || "".equals(key)) {// 为空,查询所有boolQuery.must(QueryBuilders.matchAllQuery());} else {// 不为空,根据关键字查询boolQuery.must(QueryBuilders.matchQuery("all", key));}//  filter条件过滤// 城市if (params.getCity() != null && !params.getCity().equals("")) {boolQuery.filter(QueryBuilders.termQuery("city", params.getCity()));}// 品牌if (params.getBrand() != null && !params.getBrand().equals("")) {boolQuery.filter(QueryBuilders.termQuery("brand", params.getBrand()));}// 星级if (params.getStarName() != null && !params.getStarName().equals("")) {boolQuery.filter(QueryBuilders.termQuery("starName", params.getStarName()));}//价格if (params.getMinPrice() != null && params.getMaxPrice() != null) {boolQuery.filter(QueryBuilders.rangeQuery("price").gte(params.getMinPrice()).lte(params.getMaxPrice()));}//2.算分控制FunctionScoreQueryBuilder functionScoreQuery = QueryBuilders.functionScoreQuery(//原始查询,相关性算分的查询boolQuery,//function score的数组lnew FunctionScoreQueryBuilder.FilterFunctionBuilder[]{//其中的一个functionscore元素new FunctionScoreQueryBuilder.FilterFunctionBuilder(//过滤条件QueryBuilders.termQuery("isAD", true),//算分函数ScoreFunctionBuilders.weightFactorFunction(10))});request.source().query(functionScoreQuery);}private PageResult handleResponse(SearchResponse response) {//4.解析响应SearchHits searchHits = response.getHits();//4.1获取总条数long total = searchHits.getTotalHits().value;//4.2获取文档数组SearchHit[] hits = searchHits.getHits();//4.3遍历List<HotelDoc> hotels = new ArrayList<>();for (SearchHit hit : hits) {//获取文档sourceString json = hit.getSourceAsString();//反序列化HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);//获取排序值Object[] sortValues = hit.getSortValues();if (sortValues.length > 0) {Object sortValue = sortValues[0];hotelDoc.setDistance(sortValue);}hotels.add(hotelDoc);}//4.4封装返回return new PageResult(total, hotels);}
}

分布式搜索elasticsearch搜索功能【进阶】相关推荐

  1. 分布式搜索elasticsearch搜索功能【深入】

    elasticsearch搜索功能[深入] 分布式搜索elasticsearch搜索功能[深入] 1.数据聚合 1.1 聚合的种类 1.2 DSL实现聚合 1.2.1 Bucket聚合 1.2.2 M ...

  2. 黑马程序员--分布式搜索ElasticSearch学习笔记

    写在最前 黑马视频地址:https://www.bilibili.com/video/BV1LQ4y127n4/ 想获得最佳的阅读体验,请移步至我的个人博客 SpringCloud学习笔记 消息队列M ...

  3. 分布式搜索 Elasticsearch —— 节点实例化

    为什么80%的码农都做不了架构师?>>>    要连接到集群,首先要告诉集群:你是谁,你有什么特征.在 ES 中体现为实例化节点. ES 通过 org.elasticsearch.n ...

  4. 分布式搜索elasticsearch 索引文档的增删改查 入门

    分布式搜索elasticsearch 索引文档的增删改查 入门 1.RESTful接口使用方法 为了方便直观我们使用Head插件提供的接口进行演示,实际上内部调用的RESTful接口. RESTful ...

  5. java实现sug,Elasticsearch搜索Suggest功能优化

    搜索Suggest需要优化问题: 怎么优化Suggest词库,提升Suggest词准确率 怎么提高响应速度 suggest词库获取 冷启动可以从内容中提取热词数据来解决,或者人工设置 挖掘搜索日志: ...

  6. Elasticsearch 搜索的高级功能学习

    在文章 Elasticsearch 入门学习 中介绍了 Elasticsearch 的基础概念以及一些常用的 API.这篇文章是继续对 Elasticsearch 中一些高级的搜索功能的学习和总结: ...

  7. Elasticsearch搜索匹配功能解析(十一)

    针对不同的数据类型,ES提供了很多搜索匹配功能: 完全匹配的term搜索 按照范围匹配的range搜索 分词匹配的match搜索 前缀匹配的suggest搜索 查询所有文档 在关系型数据库中,当需要查 ...

  8. Java生鲜电商平台-电商中海量搜索ElasticSearch架构设计实战与源码解析

    Java生鲜电商平台-电商中海量搜索ElasticSearch架构设计实战与源码解析 生鲜电商搜索引擎的特点 众所周知,标准的搜索引擎主要分成三个大的部分,第一步是爬虫系统,第二步是数据分析,第三步才 ...

  9. Elasticsearch 搜索入门技术之一

    1,课程回顾 2,本章重点 什么是全文检索,常用全文检索框架的基本原理是什么 es是什么,主要使用场景 es分布式搜索引擎集群的搭建 3,具体内容 3.1 全文检索 3.1.1 数据分类 我们生活中的 ...

最新文章

  1. Java 8中stream相关用法
  2. OpenCV长方形squares探测器的实例(附完整代码)
  3. 腾讯游戏数据应用微服务实战
  4. flutter 透明度动画_Flutter中的动画填充+不透明度动画✨
  5. unity3d collider自动调整大小_自动网格组合建模工具Unity游戏素材资源
  6. OpenShift 4 之获取版本升级路径图
  7. 面向对象设计思想_重要_2
  8. 计算机设计类有哪些专业,2021新高考模式下报考,这4类专业有“潜规则”,考生报考需谨慎...
  9. 第八章、面向对象设计
  10. el图oracle,element-ui之el-image-viewer(图片查看器)
  11. XenCenter创建虚拟机
  12. [算法]详解关键路径算法
  13. 32-SIFI特征点提取(EmguCV学习)
  14. python 求最大值_Python 获取最大值函数
  15. 当我跑步时我在想什么读后感
  16. K8S pod 时区设置
  17. ARM 安装中文输入法
  18. 1.python真的是万恶之源么?(初识python)
  19. Quartz 实现画图片、写文字、画线、椭圆、矩形、棱形等。三
  20. python3爬小说_python3小说爬虫

热门文章

  1. html5学习笔记--leon
  2. 读 Steven J. Leon 之《线性代数》
  3. 台式机通过笔记本上网
  4. 聊聊eureka的delta配置
  5. 广告买量支付方式 cpa cpc cps cpt
  6. dnsmasq( DNS和DHCP)服务
  7. 爷青结!微软凌晨宣布“再见”!
  8. Apple是第一家价值1万亿美元的上市公司
  9. 莆田学院计算机在哪个校区,莆田学院有几个校区
  10. 如何成功打造你自己的“个人品牌”