检索服务
封装查询条件
注意请求标识
/**
* 封装页面所有可能传递过来的查询条件
* 三种点击搜索的方式
* 1、点击搜索:keyword 【skuTitle】
* 2、点击分类:传catalog3Id【catalogld】
* 3、选择筛选条件
* 1、全文检索: keyword【skuTitle】
* 2、排序: saleCount_asc【销量】、hotScore_asc【综合排序:热度评分】、skuPrice_asc【价格】
* 3、过滤: hasStock、skuPrice区间、brandld、catalog3ld、attrs
* 4、聚合: attrs
* attrs=2_5寸 传参格式,所以直接for循环split("_")就可以得到attrId与attrValue
* attrs=1_白色:蓝色 然后值split(":")得到各项值attrValue
*/
@Data
public class SearchParam {
private String keyword;// 页面传递过来的全文匹配关键字
private Long catalog3Id;// 三级分类的id
/**
* 排序:sort=saleCount_asc sort=hotScore_asc sort=skuPrice_asc
*/
private String sort;
/**
* 过滤条件:
* hasStock=0/1【有货】
* skuPrice=0_500/500_/_500【价格区间】
* brandld=1
* attrs=1_白色:蓝色&attrs=2_2寸:5寸【属性可多选,值也可多选】
*/
private Integer hasStock = 1;// 是否只显示有货,默认显示有货,null == 1会NullPoint异常 0/1
private String skuPrice;// 按照价格区间查询
private List<Long> brandId;// 品牌id,可多选
private List<String> attrs;// 三级分类id+属性值
private Integer pageNum = 1;// 页码
private String _queryString;// 原生的所有查询条件(来自url的请求参数),用于构建面包屑
}
返回结果类
显示结果VO类
包括下面商品信息
面包屑导航
@Data
public class SearchResult {
private List<SkuEsModel> products;// es检索到的所有商品信息
/**
* 分页信息
*/
private Integer pageNum;// 当前页码
private Long total;// 总记录数
private Integer totalPages;// 总页码
private List<Integer> pageNavs;// 导航页码[1、2、3、4、5]
private List<BrandVo> brands;// 当前查询到的结果所有涉及到的品牌
private List<CatalogVo> catalogs;// 当前查询到的结果所有涉及到的分类
/**
* attrs=1_anzhuo&attrs=5_其他:1080P
*/
private List<AttrVo> attrs = new ArrayList<>();// 当前查询到的结果所有涉及到的属性【符合检索条件的,可检索的属性】
// ============================以上是要返回的数据====================================
// 面包屑导航数据
private List<NavVo> navs = new ArrayList<>();
// 封装筛选条件中的属性id集合【用于面包屑,选择属性后出现在面包屑中,下面的属性栏则隐藏】
// 该字段是提供前端用的
private List<Long> attrIds = new ArrayList<>();
/**
* 面包屑导航VO
*/
@Data
public static class NavVo {
private String navName;// 属性名
private String navValue;// 属性值
private String link;// 回退地址(删除该面包屑筛选条件回退地址)
}
@Data
public static class BrandVo {
private Long brandId;//
private String brandName;//
private String brandImg;//
}
@Data
public static class CatalogVo {
private Long catalogId;// 显示分类id
private String catalogName;// 显示分类name
}
@Data
public static class AttrVo {
private Long attrId;// 允许检索的 属性Id
private String attrName;// 允许检索的 属性名
private List<String> attrValue;// 属性值【多个】
}
}
ES查询的实例语句
{
"query": {
"bool": {
"must": [
{
"match": {
"skuTitle": "华为"
}
}
],
"filter": [
{
"term": {
"catalogId": "225"
}
},
{
"terms": {
"brandId": [
"9",
"5"
]
}
},
{
"nested": {
"path": "attrs",
"query": {
"bool": {
"must": [
{
"term": {
"attrs.attrId": {
"value": "15"
}
}
},
{
"terms": {
"attrs.attrValue": [
"高通(Qualcomm)"
]
}
}
]
}
}
}
},
{
"term": {
"hasStock": {
"value": "true"
}
}
},
{
"range": {
"skuPrice": {
"gte": 0,
"lte": 7000
}
}
}
]
}
},
"sort": [
{
"saleCount": {
"order": "desc"
}
}
],
"from": 0,
"size": 2,
"highlight": {
"fields": {
"skuTitle": {}
},
"pre_tags": "<b style='color:red'>",
"post_tags": "</b>"
},
"aggs": {
"brand_agg": {
"terms": {
"field": "brandId",
"size": 10
},
"aggs": {
"brand_name_agg": {
"terms": {
"field": "brandName",
"size": 10
}
},
"brand_img_agg": {
"terms": {
"field": "brandImg",
"size": 10
}
}
}
},
"catalog_agg": {
"terms": {
"field": "catalogId",
"size": 10
},
"aggs": {
"catalog_name_agg": {
"terms": {
"field": "catalogName",
"size": 10
}
}
}
},
"attr_agg": {
"nested": {
"path": "attrs"
},
"aggs": {
"attr_id_agg": {
"terms": {
"field": "attrs.attrId",
"size": 10
},
"aggs": {
"attr_name_agg": {
"terms": {
"field": "attrs.attrName",
"size": 10
}
},
"attr_value_agg": {
"terms": {
"field": "attrs.attrValue",
"size": 10
}
}
}
}
}
}
}
}
请求封装接口
@Override
public SearchResult search(SearchParam searchParam) {
// 动态构建查询需要的DSL语句
SearchResult searchResult = null;
// 准备检索请求
SearchRequest searchRequest = buildSearchRequest(searchParam);
// 执行检索请求
try {
SearchResponse response = restHighLevelClient.search(searchRequest, GulimallSearchConfig.COMMON_OPTIONS);
// 封装返回结果
searchResult = buildSearchResponse(response, searchParam);
} catch (IOException e) {
e.printStackTrace();
}
return searchResult;
}
/**
* 准备检索请求(参照dsl.json文件)
*
* @param searchParam
* @return
*/
private SearchRequest buildSearchRequest(SearchParam searchParam) {
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); // 构建DSL语句
// 构建 bool-query,根据商品的标题进行检索
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
if (!StringUtils.isEmpty(searchParam.getKeyword())) {
boolQueryBuilder.must(QueryBuilders.matchQuery("skuTitle", searchParam.getKeyword()));
}
// 构建 bool-filter-term,根据三级分类进行检索
if (!StringUtils.isEmpty(searchParam.getCatalog3Id())) {
boolQueryBuilder.filter(QueryBuilders.termQuery("catalogId", searchParam.getCatalog3Id()));
}
// 构建 bool-filter-term,根据品牌id进行检索
if (!StringUtils.isEmpty(searchParam.getBrandId())) {
boolQueryBuilder.filter(QueryBuilders.termQuery("brandId", searchParam.getBrandId()));
}
// bool-filter,根据属性值进行检索
if (!StringUtils.isEmpty(searchParam.getAttrs())) {
for (String attr : searchParam.getAttrs()) {
BoolQueryBuilder nestedBoolQuery = QueryBuilders.boolQuery();
// attr格式 attrs=1_白色:蓝色
String[] attrs = attr.split("_");
String attrId = attrs[0];
String[] attrValues = attrs[1].split(":");// 再将多个属性值分开
// TODO 此时如果属性只有一个,那么在ES中会封装为[高通],此时会查不到数据
nestedBoolQuery.must(QueryBuilders.termQuery("attrs.attrId", attrId));
if (attrValues.length == 1) {
nestedBoolQuery.must(QueryBuilders.termQuery("attrs.attrValue", attrValues[0]));
} else {
// 多个就放入数组
nestedBoolQuery.must(QueryBuilders.termQuery("attrs.attrValue", attrValues));
}
// 创建nested查询(注意每一个属性值,都需要一个nested进行查询)
NestedQueryBuilder nestedQuery = QueryBuilders.nestedQuery("attrs", nestedBoolQuery, ScoreMode.None); // 最后参数表示不参与评分
boolQueryBuilder.filter(nestedQuery);
}
}
// 构建 bool-filter-term,根据是否有库存进行检索
boolQueryBuilder.filter(QueryBuilders.termQuery("hasStock", searchParam.getHasStock() == 1));
// bool-filter,根据价格区间进行查询
// skuPrice=0_500/500_/_500【价格区间】
if (!StringUtils.isEmpty(searchParam.getSkuPrice())) {
RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("skuPrice");
String skuPrice = searchParam.getSkuPrice();
String[] s = skuPrice.split("_");
if (s.length == 2) { // 指定了两边区间 0_500
rangeQuery.gte(s[0]).lte(s[1]);
} else { // 指定了一边区间 500_/_500
if (skuPrice.endsWith("_")) {
rangeQuery.gte(s[0]);
}
if (skuPrice.startsWith("_")) {
rangeQuery.lte(s[0]);
}
}
boolQueryBuilder.filter(rangeQuery);
}
// 把以前所有的条件封装起来
searchSourceBuilder.query(boolQueryBuilder);
// 排序:sort=saleCount_asc
if (!StringUtils.isEmpty(searchParam.getSort())) {
String[] s = searchParam.getSort().split("_");
SortOrder sortOrder = s[1].equalsIgnoreCase("asc") ? SortOrder.ASC : SortOrder.DESC;
searchSourceBuilder.sort(s[0], sortOrder);
}
// 分页
// "from": 0,
// "size": 2,
searchSourceBuilder.from((searchParam.getPageNum() - 1) * EsConstant.PRODUCT_PAGESIZE);
searchSourceBuilder.size(EsConstant.PRODUCT_PAGESIZE);
// 高亮
// "highlight": {
// "fields": {
// "skuTitle": {}
// },
// "pre_tags": "<b style='color:red'>",
// "post_tags": "</b>"
// }
if (!StringUtils.isEmpty(searchParam.getKeyword())) {
HighlightBuilder highlightBuilder = new HighlightBuilder();
highlightBuilder.field("skuTitle");
highlightBuilder.preTags("<b style='color:red'>");
highlightBuilder.postTags("</b>");
searchSourceBuilder.highlighter(highlightBuilder);
}
// 聚合分析
// 品牌聚合
TermsAggregationBuilder brand_agg = AggregationBuilders.terms("brand_agg").field("brandId").size(50);
brand_agg.subAggregation(AggregationBuilders.terms("brand_name_agg").field("brandName").size(1));
brand_agg.subAggregation(AggregationBuilders.terms("brand_img_agg").field("brandImg").size(1));
searchSourceBuilder.aggregation(brand_agg);
// 分类聚合
TermsAggregationBuilder catalog_agg = AggregationBuilders.terms("catalog_agg").field("catalogId").size(20);
catalog_agg.subAggregation(AggregationBuilders.terms("catalog_name_agg").field("catalogName").size(1));
searchSourceBuilder.aggregation(catalog_agg);
// 属性聚合(nested属性)
NestedAggregationBuilder attr_agg = AggregationBuilders.nested("attr_agg", "attrs");
TermsAggregationBuilder attr_id_agg = AggregationBuilders.terms("attr_id_agg").field("attrs.attrId");
attr_id_agg.subAggregation(AggregationBuilders.terms("attr_name_agg").field("attrs.attrName").size(1));
attr_id_agg.subAggregation(AggregationBuilders.terms("attr_value_agg").field("attrs.attrValue").size(50));
attr_agg.subAggregation(attr_id_agg);
searchSourceBuilder.aggregation(attr_agg);
System.out.println(searchSourceBuilder.toString());
SearchRequest searchRequest = new SearchRequest(new String[]{EsConstant.PRODUCT_INDEX}, searchSourceBuilder);
return searchRequest;
}
ES返回JSON数据
{
"took" : 14,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 4,
"relation" : "eq"
},
"max_score" : null,
"hits" : [
{
"_index" : "gulimall_product",
"_type" : "_doc",
"_id" : "51",
"_score" : null,
"_source" : {
"attrs" : [
{
"attrId" : 15,
"attrName" : "CPU品牌",
"attrValue" : "高通(Qualcomm)"
},
{
"attrId" : 16,
"attrName" : "CPU型号",
"attrValue" : "鸿蒙"
}
],
"brandId" : 9,
"brandImg" : "https://gulimall-hello.oss-cn-beijing.aliyuncs.com/2019-11-18/de2426bd-a689-41d0-865a-d45d1afa7cde_huawei.png",
"brandName" : "华为",
"catalogId" : 225,
"catalogName" : "手机",
"hasStock" : true,
"hotScore" : 0,
"saleCount" : 0,
"skuId" : 51,
"skuImg" : "https://project-guli-education.oss-cn-chengdu.aliyuncs.com/2022-05-03/842c391d-6a37-441e-8aa4-ce5041b23ecb_23d9fbb256ea5d4a.jpg",
"skuPrice" : 6099.0,
"skuTitle" : "华为mate40pro 5G手机 夏日胡杨 8+128G全网通(4G版) 黑色 8+256GB 4G",
"spuId" : 29
},
"highlight" : {
"skuTitle" : [
"<b style='color:red'>华为</b>mate40pro 5G手机 夏日胡杨 8+128G全网通(4G版) 黑色 8+256GB 4G"
]
},
"sort" : [
0
]
},
{
"_index" : "gulimall_product",
"_type" : "_doc",
"_id" : "52",
"_score" : null,
"_source" : {
"attrs" : [
{
"attrId" : 15,
"attrName" : "CPU品牌",
"attrValue" : "高通(Qualcomm)"
},
{
"attrId" : 16,
"attrName" : "CPU型号",
"attrValue" : "鸿蒙"
}
],
"brandId" : 9,
"brandImg" : "https://gulimall-hello.oss-cn-beijing.aliyuncs.com/2019-11-18/de2426bd-a689-41d0-865a-d45d1afa7cde_huawei.png",
"brandName" : "华为",
"catalogId" : 225,
"catalogName" : "手机",
"hasStock" : true,
"hotScore" : 0,
"saleCount" : 0,
"skuId" : 52,
"skuImg" : "https://project-guli-education.oss-cn-chengdu.aliyuncs.com/2022-05-03/842c391d-6a37-441e-8aa4-ce5041b23ecb_23d9fbb256ea5d4a.jpg",
"skuPrice" : 6099.0,
"skuTitle" : "华为mate40pro 5G手机 夏日胡杨 8+128G全网通(4G版) 黑色 8+256GB 5G",
"spuId" : 29
},
"highlight" : {
"skuTitle" : [
"<b style='color:red'>华为</b>mate40pro 5G手机 夏日胡杨 8+128G全网通(4G版) 黑色 8+256GB 5G"
]
},
"sort" : [
0
]
}
]
},
"aggregations" : {
"catalog_agg" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : 225,
"doc_count" : 4,
"catalog_name_agg" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "手机",
"doc_count" : 4
}
]
}
}
]
},
"attr_agg" : {
"doc_count" : 8,
"attr_id_agg" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : 15,
"doc_count" : 4,
"attr_name_agg" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "CPU品牌",
"doc_count" : 4
}
]
},
"attr_value_agg" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "高通(Qualcomm)",
"doc_count" : 4
}
]
}
},
{
"key" : 16,
"doc_count" : 4,
"attr_name_agg" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "CPU型号",
"doc_count" : 4
}
]
},
"attr_value_agg" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "鸿蒙",
"doc_count" : 4
}
]
}
}
]
}
},
"brand_agg" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : 9,
"doc_count" : 4,
"brand_img_agg" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "https://gulimall-hello.oss-cn-beijing.aliyuncs.com/2019-11-18/de2426bd-a689-41d0-865a-d45d1afa7cde_huawei.png",
"doc_count" : 4
}
]
},
"brand_name_agg" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "华为",
"doc_count" : 4
}
]
}
}
]
}
}
}
返回结果封装方法
/**
* 封装返回结果(参照result.json文件)
*
* @param response
* @return
*/
private SearchResult buildSearchResponse(SearchResponse response, SearchParam searchParam) {
SearchResult searchResult = new SearchResult();
SearchHits hits = response.getHits();
// 封装商品信息
// 保存进去就是以SkuEsModel保存的
List<SkuEsModel> skuEsModelList = new ArrayList<>();
// hits的hits中的source保存的是商品信息
for (SearchHit hit : response.getHits()) {
// 获取到json数据,转换为对象
String sourceAsString = hit.getSourceAsString();
SkuEsModel skuEsModel = JSON.parseObject(sourceAsString, SkuEsModel.class);
// 设置高亮标题
if (!StringUtils.isEmpty(searchParam.getKeyword())) {
Map<String, HighlightField> highlightFieldMap = hit.getHighlightFields();
String skuTitle = highlightFieldMap.get("skuTitle").getFragments()[0].string();
skuEsModel.setSkuTitle(skuTitle);
}
skuEsModelList.add(skuEsModel);
}
searchResult.setProducts(skuEsModelList);
// 当前商品所涉及的品牌信息
ParsedLongTerms brand_agg = response.getAggregations().get("brand_agg");
List<SearchResult.BrandVo> brandVoList = new ArrayList<>();
List<? extends Terms.Bucket> brandList = brand_agg.getBuckets();
for (Terms.Bucket bucket : brandList) {
SearchResult.BrandVo brandVo = new SearchResult.BrandVo();
// 获取品牌的id
long brandId = bucket.getKeyAsNumber().longValue();
// 获取品牌的名字
String brandName = ((ParsedStringTerms) bucket.getAggregations().get("brand_name_agg")).getBuckets().get(0).getKeyAsString();
// 获取品牌的图片
String brandImage = ((ParsedStringTerms) bucket.getAggregations().get("brand_img_agg")).getBuckets().get(0).getKeyAsString();
brandVo.setBrandId(brandId);
brandVo.setBrandName(brandName);
brandVo.setBrandImg(brandImage);
brandVoList.add(brandVo);
}
searchResult.setBrands(brandVoList);
// 当前商品所涉及的属性信息
ParsedNested attr_agg = response.getAggregations().get("attr_agg");
List<SearchResult.AttrVo> attrVoList = new ArrayList<>();
ParsedLongTerms attrIdAgg = attr_agg.getAggregations().get("attr_id_agg");
for (Terms.Bucket bucket : attrIdAgg.getBuckets()) {
SearchResult.AttrVo attrVo = new SearchResult.AttrVo();
// 属性id
long attrId = bucket.getKeyAsNumber().longValue();
// 属性名
String attrName = ((ParsedStringTerms) bucket.getAggregations().get("attr_name_agg")).getBuckets().get(0).getKeyAsString();
// 属性值
List<String> attrValueList = ((ParsedStringTerms) bucket.getAggregations().get("attr_value_agg")).getBuckets().stream().map(item -> {
String attrValue = ((Terms.Bucket) item).getKeyAsString();
return attrValue;
}).collect(Collectors.toList());
attrVo.setAttrId(attrId);
attrVo.setAttrName(attrName);
attrVo.setAttrValue(attrValueList);
attrVoList.add(attrVo);
}
searchResult.setAttrs(attrVoList);
// 当前商品所涉及到的所有分类信息(从聚合中取出)
ParsedLongTerms catalog_agg = response.getAggregations().get("catalog_agg");
List<SearchResult.CatalogVo> catalogVoList = new ArrayList<>();
List<? extends Terms.Bucket> catalogList = catalog_agg.getBuckets();
for (Terms.Bucket bucket : catalogList) {
SearchResult.CatalogVo catalogVo = new SearchResult.CatalogVo();
// 得到分类id
String catalogId = bucket.getKeyAsString();
catalogVo.setCatalogId(Long.parseLong(catalogId));
// 得到分类名,子聚合
ParsedStringTerms catalog_name_agg = bucket.getAggregations().get("catalog_name_agg");
List<? extends Terms.Bucket> catalogNameList = catalog_name_agg.getBuckets();
String catalogName = catalogNameList.get(0).getKeyAsString();
catalogVo.setCatalogName(catalogName);
catalogVoList.add(catalogVo);
}
searchResult.setCatalogs(catalogVoList);
// 封装分页信息
long total = hits.getTotalHits().value;
searchResult.setPageNum(searchParam.getPageNum());
searchResult.setTotal(total);
// 总页码(总页数 / 每页数)有余数加一
int totalPage = (int) total % EsConstant.PRODUCT_PAGESIZE == 1 ? (((int) total / EsConstant.PRODUCT_PAGESIZE) + 1) : (int) total / EsConstant.PRODUCT_PAGESIZE;
searchResult.setTotalPages(totalPage);
// 导航页码[1、2、3、4、5]
List<Integer> pageNavList = new ArrayList<>();
for (int i = 1; i <= totalPage; i++) {
pageNavList.add(i);
}
searchResult.setPageNavs(pageNavList);
// 构建面包屑导航功能
if (searchParam.getAttrs() != null && searchParam.getAttrs().size() > 0) {
List<SearchResult.NavVo> navVoList = searchParam.getAttrs().stream().map(attr -> {
SearchResult.NavVo navVo = new SearchResult.NavVo();
// attr=2_5寸:6寸(此时只有属性id和属性值)
String[] split = attr.split("_");
// 此时需要调用product服务查询属性名
navVo.setNavValue(split[1]);
R r = productFeignClient.attrInfo(Long.parseLong(split[0]));
searchResult.getAttrIds().add(Long.parseLong(split[0]));
if (r.getCode() == 0) {
AttrResponseVo data = r.getData("attr", new TypeReference<AttrResponseVo>() {
});
// 设置属性名
navVo.setNavName(data.getAttrName());
} else {
navVo.setNavName(split[0]);
}
// 取消了这个面包屑以后,我们要跳转到那个地方.将请求地址的urL里面的当前置空拿到所有的查询条件,去掉当前等条件
// attrs=15_海思(Hisilicon)
String replace = replaceQueryString(searchParam, attr, "attrs");
navVo.setLink("http://search.gulimalls.com/list.html?" + replace);
return navVo;
}).collect(Collectors.toList());
searchResult.setNavs(navVoList);
}
if (!StringUtils.isEmpty(searchParam.getBrandId())) {
SearchResult.NavVo navVo = new SearchResult.NavVo();
navVo.setNavName("品牌");
// 远程查询所有品牌
R r = productFeignClient.getInfoByBrandIds(searchParam.getBrandId());
if (r.getCode() == 0) {
List<BrandVo> brandVo = r.getData("brand", new TypeReference<List<BrandVo>>() {
});
StringBuffer sb = new StringBuffer();
String replace = null;
for (BrandVo brand : brandVo) {
sb.append(brand.getBrandName() + ";");
replace = replaceQueryString(searchParam, brand.getBrandId().toString(), "brandId");
}
navVo.setNavValue(sb.toString());
navVo.setLink("http://search.gulimalls.com/list.html?" + replace);
}
searchResult.getNavs().add(navVo);
}
// TODO 分类的面包屑,不需要导航取消
return searchResult;
}
/**
* 替换地址字符串
*
* @param searchParam
* @param attr
* @return
*/
private String replaceQueryString(SearchParam searchParam, String attr, String key) {
// TODO 请求地址参数转义有问题
String encode = null;
try {
encode = URLEncoder.encode(attr, "UTF-8");
encode = encode.replace("+", "%20"); // 处理空格
encode = encode.replace("%28", "("); // 处理)
encode = encode.replace("%29", ")"); // 处理(
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
String replace = searchParam.get_queryString().replace("&" + key + "=" + encode, " ");
return replace;
}
Controller层
@Controller
public class SearchController {
@Autowired
private ElasticSearchService elasticSearchService;
@GetMapping("/list.html")
public String list(SearchParam searchParam, Model model, HttpServletRequest httpServletRequest) {
searchParam.set_queryString(httpServletRequest.getQueryString()); // 获取查询请求
SearchResult searchResult = elasticSearchService.search(searchParam);
model.addAttribute("result", searchResult);
return "list";
}
}