案例1
DSLGET /hotel/_search { "query":{ "match_all": {} }, "sort":[ { "score":"desc" }, { "price":"asc" } ] }
案例2
DSL# 31.034661 121.612282 GET /hotel/_search { "query":{ "match_all": {} }, "sort":[ { "_geo_distance": { "location": { "lat": 31.034661, "lon": 121.612282 }, "order": "asc", "unit": "km" } }] }
示例
DSLGET /hotel/_search { "query":{ "match_all": {} }, "sort":[ { "price":"asc" } ], "from": 0, "size": 20 }
深度分页问题
解决方案
DSLGET /hotel/_search { "query":{ "match": { "all": "如家" } }, "highlight": { "fields": { "name": { "require_field_match": "false" } } } }
快速入门
java @Test
void testMatchAll() throws IOException {
// 准备reuqest
SearchRequest request = new SearchRequest("hotel");
// 准备DSL
request.source().query(QueryBuilders.matchAllQuery());
// 发送请求
SearchResponse response = client.search(request,RequestOptions.DEFAULT);
System.out.println(response);
}
解析结果
java @Test
void testMatchAll() throws IOException {
// 准备reuqest
SearchRequest request = new SearchRequest("hotel");
// 准备DSL
request.source().query(QueryBuilders.matchAllQuery());
// 发送请求
SearchResponse response = client.search(request,RequestOptions.DEFAULT);
SearchHits searchHits = response.getHits();
long total = searchHits.getTotalHits().value;
SearchHit[] hits = searchHits.getHits();
for (SearchHit hit : hits) {
String json = hit.getSourceAsString();
System.out.println(json);
}
}
match查询
java @Test
void testMatch() throws IOException {
// 准备reuqest
SearchRequest request = new SearchRequest("hotel");
// 准备DSL
request.source().query(QueryBuilders.matchQuery("all","如家"));
// 发送请求
SearchResponse response = client.search(request,RequestOptions.DEFAULT);
SearchHits searchHits = response.getHits();
long total = searchHits.getTotalHits().value;
SearchHit[] hits = searchHits.getHits();
for (SearchHit hit : hits) {
String json = hit.getSourceAsString();
System.out.println(json);
}
}
精确查询
复合查询
java @Test
void testBoolQuery() throws IOException {
// 准备reuqest
SearchRequest request = new SearchRequest("hotel");
// 创建boolQueryBuilder对象
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
// 添加term
boolQueryBuilder.must(QueryBuilders.termQuery("city","上海"));
// 添加range
boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").lte(250));
request.source().query(boolQueryBuilder);
// 发送请求
SearchResponse response = client.search(request,RequestOptions.DEFAULT);
SearchHits searchHits = response.getHits();
long total = searchHits.getTotalHits().value;
SearchHit[] hits = searchHits.getHits();
for (SearchHit hit : hits) {
String json = hit.getSourceAsString();
System.out.println(json);
}
}
java @Test
void testPageAndSort() throws IOException {
// 准备reuqest
int page = 2, size = 5;
SearchRequest request = new SearchRequest("hotel");
request.source().query(QueryBuilders.matchAllQuery());
request.source().sort("price", SortOrder.ASC);
request.source().from((page-1)*size).size(size);
// 发送请求
SearchResponse response = client.search(request,RequestOptions.DEFAULT);
SearchHits searchHits = response.getHits();
long total = searchHits.getTotalHits().value;
System.out.println("共"+total+"条数据");
SearchHit[] hits = searchHits.getHits();
for (SearchHit hit : hits) {
String json = hit.getSourceAsString();
HotelDoc hotelDoc = JSON.parseObject(json,HotelDoc.class);
System.out.println(hotelDoc.toString());
}
}
RestClient处理高亮
java @Test
void testHeightLight() throws IOException {
// 准备reuqest
int page = 2, size = 5;
SearchRequest request = new SearchRequest("hotel");
request.source().query(QueryBuilders.matchQuery("all","如家"));
request.source().highlighter(new HighlightBuilder().field("name").requireFieldMatch(false));
// 发送请求
SearchResponse response = client.search(request,RequestOptions.DEFAULT);
SearchHits searchHits = response.getHits();
long total = searchHits.getTotalHits().value;
System.out.println("共"+total+"条数据");
SearchHit[] hits = searchHits.getHits();
for (SearchHit hit : hits) {
String 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("hotelDoc = "+hotelDoc.toString());
}
}
地理距离排序
添加权重
案例关键代码
javapackage cn.itcast.hotel.service.impl;
import cn.itcast.hotel.mapper.HotelMapper;
import cn.itcast.hotel.pojo.Hotel;
import cn.itcast.hotel.pojo.HotelDoc;
import cn.itcast.hotel.pojo.PageResult;
import cn.itcast.hotel.pojo.RequestParams;
import cn.itcast.hotel.service.IHotelService;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import javax.swing.text.Highlighter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@Service
public class HotelService extends ServiceImpl<HotelMapper, Hotel> implements IHotelService {
// 注入Elasticsearch的高级客户端
@Autowired
private RestHighLevelClient client;
/**
* 酒店搜索方法
* @param requestParams 搜索请求参数
* @return 分页结果
*/
@Override
public PageResult search(RequestParams requestParams) {
try {
// 1. 准备SearchRequest,指定索引名为"hotel"
SearchRequest request = new SearchRequest("hotel");
// 2. 构建基本查询条件
buildBasicQuery(requestParams, request);
// 3. 设置分页参数
int page = requestParams.getPage();
int size = requestParams.getSize();
request.source().from((page-1)*size).size(size);
// 4. 地理位置排序(如果提供了位置参数)
String location = requestParams.getLocation();
if(location != null && !"".equals(location)){
request.source().sort(SortBuilders.geoDistanceSort("location", new GeoPoint(location))
.order(SortOrder.ASC) // 按距离升序排序
.unit(DistanceUnit.KILOMETERS)); // 距离单位为公里
}
// 5. 执行搜索请求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 6. 处理搜索结果并返回
return handleResponse(response);
} catch (IOException e) {
throw new RuntimeException("搜索酒店失败", e);
}
}
/**
* 构建基本查询条件
* @param requestParams 请求参数
* @param request SearchRequest对象
*/
private void buildBasicQuery(RequestParams requestParams, SearchRequest request) {
// 1. 创建布尔查询构建器
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
// 2. 关键字查询
String key = requestParams.getKey();
if (key == null || key.equals("")){
// 如果关键字为空,则匹配所有文档
boolQueryBuilder.must(QueryBuilders.matchAllQuery());
} else {
// 否则在"all"字段中搜索关键字
boolQueryBuilder.must(QueryBuilders.matchQuery("all", key));
}
// 3. 城市过滤条件
if (requestParams.getCity() != null && !requestParams.getCity().equals("")){
boolQueryBuilder.filter(QueryBuilders.termQuery("city", requestParams.getCity()));
}
// 4. 品牌过滤条件
if (requestParams.getBrand() != null && !requestParams.getBrand().equals("")){
boolQueryBuilder.filter(QueryBuilders.termQuery("brand", requestParams.getBrand()));
}
// 5. 星级过滤条件
if (requestParams.getStarName() != null && !requestParams.getStarName().equals("")){
boolQueryBuilder.filter(QueryBuilders.termQuery("starName", requestParams.getStarName()));
}
// 6. 价格范围过滤
if (requestParams.getMaxPrice() != null && requestParams.getMinPrice() != null){
boolQueryBuilder.filter(QueryBuilders.rangeQuery("price")
.gte(requestParams.getMinPrice()) // 大于等于最小值
.lte(requestParams.getMaxPrice())); // 小于等于最大值
}
// 7. 构建函数评分查询(广告置顶功能)
FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery(
boolQueryBuilder,
new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
new FunctionScoreQueryBuilder.FilterFunctionBuilder(
QueryBuilders.termQuery("isAd", true), // 广告标识字段
ScoreFunctionBuilders.weightFactorFunction(10) // 权重因子为10
)
}
);
// 8. 将查询设置到请求中
request.source().query(functionScoreQueryBuilder);
}
/**
* 处理搜索响应结果
* @param response Elasticsearch的搜索响应
* @return 分页结果对象
*/
public PageResult handleResponse(SearchResponse response) {
PageResult pageResult = new PageResult();
// 1. 获取命中结果
SearchHits searchHits = response.getHits();
// 2. 设置总命中数
long total = searchHits.getTotalHits().value;
pageResult.setTotal(total);
// 3. 处理每条命中记录
SearchHit[] hits = searchHits.getHits();
List<HotelDoc> hotelDocList = new ArrayList<>();
for (SearchHit hit : hits) {
// 3.1 将JSON格式的源数据转换为HotelDoc对象
String json = hit.getSourceAsString();
HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);
// 3.2 处理距离排序值(如果有)
Object[] sortValues = hit.getSortValues();
if (sortValues != null && sortValues.length > 0) {
Object sortValue = sortValues[0];
hotelDoc.setDistance(sortValue);
}
// 3.3 处理高亮字段(如果有)
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);
}
}
// 3.4 将处理后的酒店文档添加到列表
hotelDocList.add(hotelDoc);
}
// 4. 设置结果列表并返回
pageResult.setHotels(hotelDocList);
return pageResult;
}
}
本文作者:钱小杰
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!