赞
踩
# 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.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=false GET /hotel/_search { "query": { "match": { "all": "如家" } }, "highlight": { "fields": { "name": { "require_field_match": "false", "pre_tags": "<em>", "post_tags": "</em>" } } } }
@SpringBootTest class HotelSearchTest { private RestHighLevelClient client; @BeforeEach void setUp() { client = new RestHighLevelClient(RestClient.builder( HttpHost.create("http://192.168.1.101:9200") )); } @AfterEach void tearDown() throws IOException { client.close(); } /** * 解析json文档 */ private void handleResponse(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.获取source String json = hit.getSourceAsString(); // 4.5.反序列化,非高亮的 HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class); // 4.6.处理高亮结果 // 1)获取高亮map Map<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); } } /** * 查询所有文档 */ @Test void testMatchAll() throws IOException { // 1.准备request SearchRequest request = new SearchRequest("hotel"); // 2.准备请求参数 request.source().query(QueryBuilders.matchAllQuery()); // 3.发送请求,得到响应 SearchResponse response = client.search(request, RequestOptions.DEFAULT); // 4.结果解析 handleResponse(response); } /** * 全文检索查询:match、multi_match */ @Test void testMatch() throws IOException { // 1.准备request SearchRequest request = new SearchRequest("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的文档 */ @Test void testBool() throws IOException { // 1.准备request SearchRequest request = new SearchRequest("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); } /** * 排序和分页 */ @Test void testSortAndPage() throws IOException { int page = 2,size = 5; // 1.准备request SearchRequest request = new SearchRequest("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); } /** * 结果高亮 */ @Test void testHighlight() throws IOException { // 1.准备request SearchRequest request = new SearchRequest("hotel"); // 2.准备请求参数 // 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); } }
PageResult.java
响应结果类:由搜索框得到的查询结果类
/** * 响应结果类:由搜索框得到的查询结果类 */ @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; } }
RequestParams.java
请求参数类:搜索框中有哪些参数
/** * 请求参数类:搜索框中有哪些参数 */ @Data public class RequestParams { private String key; // 搜索关键字 private Integer page;// 当前页码 private Integer size;// 每页大小 private String sortBy;// 排序字段 private String brand;// 品牌 private String city;// 城市 private String starName;// 星级 private Integer minPrice;// 最低价格 private Integer maxPrice;// 最高价格 private String location;// 位置 }
@RestController
@RequestMapping("hotel")
public class HotelController {
@Autowired
private IHotelService hotelService;
@PostMapping("list")
public PageResult search(@RequestBody RequestParams params) {
return hotelService.search(params);
}
}
public interface IHotelService extends IService<Hotel> {
PageResult search(RequestParams params);
}
@Slf4j @Service public class HotelService extends ServiceImpl<HotelMapper, Hotel> implements IHotelService { @Autowired private RestHighLevelClient restHighLevelClient; @Override public PageResult search(RequestParams params) { try { // 1.准备Request SearchRequest request = new SearchRequest("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", new GeoPoint(location)) .order(SortOrder.ASC) .unit(DistanceUnit.KILOMETERS) ); } // 3.发送请求 SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT); // 4.解析响应 return handleResponse(response); } catch (IOException e) { throw new RuntimeException("搜索数据失败", e); } } private void buildBasicQuery(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, // 原始查询,boolQuery new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{ // function数组 new FunctionScoreQueryBuilder.FilterFunctionBuilder( QueryBuilders.termQuery("isAD", true), // 过滤条件 ScoreFunctionBuilders.weightFactorFunction(10) // 算分函数 ) } ); /** * 4.设置查询条件 */ request.source().query(functionScoreQuery); } private PageResult handleResponse(SearchResponse response) { SearchHits searchHits = response.getHits(); // 4.1.总条数 long total = searchHits.getTotalHits().value; // 4.2.获取文档数组 SearchHit[] hits = searchHits.getHits(); // 4.3.遍历 List<HotelDoc> hotels = new ArrayList<>(hits.length); for (SearchHit hit : hits) { // 4.4.获取source String json = hit.getSourceAsString(); // 4.5.反序列化,非高亮的 HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class); // 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)把高亮结果放到HotelDoc中 hotelDoc.setName(hName); } } // 4.8.排序信息 Object[] sortValues = hit.getSortValues(); // 获取排序结果 if (sortValues.length > 0) { /** * 由于该程序是根据距离[酒店距你选择位置的距离]进行排序,所以排序结果为距离 */ hotelDoc.setDistance(sortValues[0]); } // 4.9.放入集合 hotels.add(hotelDoc); } return new PageResult(total, hotels); } }
@MapperScan("cn.itcast.hotel.mapper")
@SpringBootApplication
public class HotelDemoApplication {
public static void main(String[] args) {
SpringApplication.run(HotelDemoApplication.class, args);
}
@Bean
public RestHighLevelClient restHighLevelClient(){
return new RestHighLevelClient(RestClient.builder(
HttpHost.create("http://192.168.1.101:9200")
)); // 服务器IP+端口9200
}
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。