一、显示页面
1.1、thymeleaf.yml1.2、修改资源路径 二、搜索商品
2.1、参数封装2.3、检索结果封装2.4、修改映射2.5、数据迁移2.6、搜索商品接口
2.6.1、构建检索参数2.6.2、接收检索响应2.6.3、检索不到数据 三、渲染检索页面
3.1、基本数据渲染3.2、筛选条件渲染3.3、搜索按钮渲染3.4、分页数据渲染3.5、页面排序渲染3.6、 面包屑导航3.7、 条件筛选联动3.8、[异步和线程池](https://blog.csdn.net/m0_46914264/article/details/122872613)
一、显示页面index静态资源复制到:fireflymall-elasticsearch/src/main/resources/static
index页面复制到:fireflymall-elasticsearch/src/main/resources/templates
关闭thymeleaf缓存,方便开发实时看到更新
1.1、thymeleaf.ymlthymeleaf.yml
spring:
thymeleaf:
cache: false
suffix: .html #后缀
prefix: classpath:/templates/ #前缀
引入thymeleaf的命名空间
xmlns:th="http://www.thymeleaf.org"1.2、修改资源路径
ctrl+r
src=" src="/search/
href=" href="/search/二、搜索商品 2.1、参数封装
封装页面所有可能传递的数据
全文检索:skuTitle-》keyword 排序:saleCount(销量)、hotScore(热度分)、skuPrice(价格) 过滤:hasStock、skuPrice区间、brandId、catalog3Id、attrs 聚合:attrs
SearchParamVo
package com.firefly.fireflymall.elasticsearch.vo;
import lombok.Data;
import java.util.List;
@Data
public class SearchParamVo {
//全文检索
private String keyword;
private Long catalog3Id;
private String sort;
private Integer hasStock = 1;
private String skuPrice;
private Integer skuPrice1;
private Integer skuPrice2;
private List brandId;
private List attrs;
//分页
private Integer pageNum = 1;
private String _queryString;//原生查询条件
}
2.3、检索结果封装
查询得到商品、总记录数、总页码 品牌list用于在品牌栏显示,分类list用于在分类栏显示 其他栏每栏用AttrVo表示 不仅要根据关键字从es中检索到商品 还要通过聚合生成品牌等信息,方便分类栏显示
SearchResult
package com.firefly.fireflymall.elasticsearch.vo;
import com.firefly.common.to.es.SkuEsModel;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Data
public class SearchResult {
private List products;
private Integer pageNum;//当前页
private Long total;//总记录数
private Long totalPage;//总页码
private List pageNavs;
private List brands;//品牌信息
private List attrs;//属性信息
private List catalogs;//分类信息
private List navs = new ArrayList<>();
private List attrIds = new ArrayList<>();
@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 AttrVo{
private Long attrId;
private String attrName;
private List attrValue;
}
@Data
public static class CatalogVo {
private Long catalogId;
private String catalogName;
}
}
2.4、修改映射
此处先写出如何检索指定的商品,如检索"华为"关键字 嵌入式的属性 highlight:设置该值后,返回的时候就包装过了 查出结果后,附属栏也要对应变化 嵌入式的聚合时候也要注意
PUT /firefly_product
{
"mappings": {
"properties": {
"skuId": {
"type": "long"
},
"spuId": {
"type": "long"
},
"skuTitle": {
"type": "text",
"analyzer": "ik_smart"
},
"skuPrice": {
"type": "keyword"
},
"skuImg": {
"type": "keyword"
},
"saleCount": {
"type": "long"
},
"hosStock": {
"type": "boolean"
},
"hotScore": {
"type": "long"
},
"brandId": {
"type": "long"
},
"catalogId": {
"type": "long"
},
"brandName": {
"type": "keyword"
},
"brandImg": {
"type": "keyword"
},
"catalogName": {
"type": "keyword"
},
"attrs": {
"type": "nested",
"properties": {
"attrId": {
"type": "long"
},
"attrName": {
"type": "keyword"
},
"attrValue": {
"type": "keyword"
}
}
}
}
}
}
2.5、数据迁移
POST _reindex
{
"source": {
"index": "product"
},
"dest": {
"index": "mall_product"
}
}
根据一些信息检索出符合条件的文档
GET product/_search
{
"query": {
"bool": {
"must": [ {"match": { "skuTitle": "华为" }} ],
"filter": [
{ "term": { "catalogId": "225" } },
{ "terms": {"brandId": [ "2"] } },
{ "term": { "hasStock": "false"} },
{
"range": {
"skuPrice": {
"gte": 1000,
"lte": 7000
}
}
},
{
"nested": {
"path": "attrs",
"query": {
"bool": {
"must": [
{
"term": { "attrs.attrId": { "value": "6"} }
}
]
}
}
}
}
]
}
},
"sort": [ {"skuPrice": {"order": "desc" } } ],
"from": 0,
"size": 5,
"highlight": {
"fields": {"skuTitle": {}},
"pre_tags": "",
"post_tags": ""
},
"aggs": {
"brandAgg": {
"terms": {
"field": "brandId",
"size": 10
},
"aggs": {
"brandNameAgg": {
"terms": {
"field": "brandName",
"size": 10
}
},
"brandImgAgg": {
"terms": {
"field": "brandImg",
"size": 10
}
}
}
},
"catalogAgg":{
"terms": {
"field": "catalogId",
"size": 10
},
"aggs": {
"catalogNameAgg": {
"terms": {
"field": "catalogName",
"size": 10
}
}
}
},
"attrs":{
"nested": {"path": "attrs" },
"aggs": {
"attrIdAgg": {
"terms": {
"field": "attrs.attrId",
"size": 10
},
"aggs": {
"attrNameAgg": {
"terms": {
"field": "attrs.attrName",
"size": 10
}
}
}
}
}
}
}
}
返回结果
{
"took" : 37,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 0,
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ]
},
"aggregations" : {
"brandAgg" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [ ]
},
"catalogAgg" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [ ]
},
"attrs" : {
"doc_count" : 0,
"attrIdAgg" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [ ]
}
}
}
}
2.6、搜索商品接口
SearchController
package com.firefly.fireflymall.elasticsearch.controller;
import com.firefly.fireflymall.elasticsearch.service.MallSearchService;
import com.firefly.fireflymall.elasticsearch.vo.SearchParamVo;
import com.firefly.fireflymall.elasticsearch.vo.SearchResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping
public class SearchController {
@Autowired
private MallSearchService mallSearchService;
@GetMapping("/list.html")
public String listPage(SearchParamVo searchParam, Model model) {
SearchResult searchResult = (SearchResult) mallSearchService.search(searchParam);
model.addAttribute("result", searchResult);
return "list";
}
}
MallSearchServiceImpl
@Override
public SearchResult search(SearchParamVo searchParam) {
SearchResult request = null;
//1.执行检索请求
SearchRequest searchResult = bulidSearchRequest(searchParam);
try {
//2.执行检索请求
SearchResponse response = client.search(searchResult, GuliESConfig.COMMON_OPTIONS);
//3.封装检索结果
request = buildSearchResult(response, searchParam);
} catch (IOException e) {
e.printStackTrace();
}
System.out.println(request);
return request;
}
2.6.1、构建检索参数
private SearchRequest bulidSearchRequest(SearchParamVo searchParam) {
// 用于构建DSL语句
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
//1. 构建bool query
BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
//1.1 bool must
if (!StringUtils.isEmpty(searchParam.getKeyword())) {
boolQueryBuilder.must(QueryBuilders.matchQuery("skuTitle", searchParam.getKeyword()));
}
//1.2 bool filter
//1.2.1 catalog
if (searchParam.getCatalog3Id()!=null){
boolQueryBuilder.filter(QueryBuilders.termQuery("catalogId", searchParam.getCatalog3Id()));
}
//1.2.2 brand
if (searchParam.getBrandId()!=null&&searchParam.getBrandId().size()>0) {
boolQueryBuilder.filter(QueryBuilders.termsQuery("brandId",searchParam.getBrandId()));
}
//1.2.3 hasStock
if (searchParam.getHasStock() != null) {
boolQueryBuilder.filter(QueryBuilders.termQuery("hasStock", searchParam.getHasStock() == 1));
}
//1.2.4 priceRange
RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("skuPrice");
if (!StringUtils.isEmpty(searchParam.getSkuPrice())) {
String[] prices = searchParam.getSkuPrice().split("_");
if (prices.length == 1) {
if (searchParam.getSkuPrice().startsWith("_")) {
rangeQueryBuilder.lte(Integer.parseInt(prices[0]));
}else {
rangeQueryBuilder.gte(Integer.parseInt(prices[0]));
}
} else if (prices.length == 2) {
//_6000会截取成["","6000"]
if (!prices[0].isEmpty()) {
rangeQueryBuilder.gte(Integer.parseInt(prices[0]));
}
rangeQueryBuilder.lte(Integer.parseInt(prices[1]));
}
boolQueryBuilder.filter(rangeQueryBuilder);
}
//1.2.5 attrs-nested
//attrs=1_5寸:8寸&2_16G:8G
List attrs = searchParam.getAttrs();
BoolQueryBuilder queryBuilder = new BoolQueryBuilder();
if (attrs!=null&&attrs.size() > 0) {
attrs.forEach(attr->{
String[] attrSplit = attr.split("_");
queryBuilder.must(QueryBuilders.termQuery("attrs.attrId", attrSplit[0]));
String[] attrValues = attrSplit[1].split(":");
queryBuilder.must(QueryBuilders.termsQuery("attrs.attrValue", attrValues));
});
}
NestedQueryBuilder nestedQueryBuilder = QueryBuilders.nestedQuery("attrs", queryBuilder, ScoreMode.None);
boolQueryBuilder.filter(nestedQueryBuilder);
//1.X bool query构建完成
searchSourceBuilder.query(boolQueryBuilder);
//2. sort eg:sort=saleCount_desc/asc
if (!StringUtils.isEmpty(searchParam.getSort())) {
String[] sortSplit = searchParam.getSort().split("_");
searchSourceBuilder.sort(sortSplit[0], sortSplit[1].equalsIgnoreCase("asc") ? SortOrder.ASC : SortOrder.DESC);
}
//3. 分页 // 是检测结果分页
searchSourceBuilder.from((searchParam.getPageNum() - 1) * PRODUCT_PAGE_SIZE);
searchSourceBuilder.size(PRODUCT_PAGE_SIZE);
//4. 高亮highlight
if (!StringUtils.isEmpty(searchParam.getKeyword())) {
HighlightBuilder highlightBuilder = new HighlightBuilder();
highlightBuilder.field("skuTitle");
highlightBuilder.preTags("");
highlightBuilder.postTags("");
searchSourceBuilder.highlighter(highlightBuilder);
}
//5. 聚合
//5.1 按照brand聚合
TermsAggregationBuilder brandAgg = AggregationBuilders.terms("brandAgg").field("brandId");
TermsAggregationBuilder brandNameAgg = AggregationBuilders.terms("brandNameAgg").field("brandName");
TermsAggregationBuilder brandImgAgg = AggregationBuilders.terms("brandImgAgg").field("brandImg");
brandAgg.subAggregation(brandNameAgg);
brandAgg.subAggregation(brandImgAgg);
searchSourceBuilder.aggregation(brandAgg);
//5.2 按照catalog聚合
TermsAggregationBuilder catalogAgg = AggregationBuilders.terms("catalogAgg").field("catalogId");
// 子聚合
TermsAggregationBuilder catalogNameAgg = AggregationBuilders.terms("catalogNameAgg").field("catalogName");
catalogAgg.subAggregation(catalogNameAgg);
searchSourceBuilder.aggregation(catalogAgg);
//5.3 按照attrs聚合
NestedAggregationBuilder nestedAggregationBuilder = new NestedAggregationBuilder("attrs", "attrs");
//按照attrId聚合 //按照attrId聚合之后再按照attrName和attrValue聚合
TermsAggregationBuilder attrIdAgg = AggregationBuilders.terms("attrIdAgg" ).field("attrs.attrId");
TermsAggregationBuilder attrNameAgg = AggregationBuilders.terms("attrNameAgg" ).field("attrs.attrName");
TermsAggregationBuilder attrValueAgg = AggregationBuilders.terms("attrValueAgg").field("attrs.attrValue");
attrIdAgg.subAggregation(attrNameAgg);
attrIdAgg.subAggregation(attrValueAgg);
nestedAggregationBuilder.subAggregation(attrIdAgg);
searchSourceBuilder.aggregation(nestedAggregationBuilder);
log.debug("构建的DSL语句 {}",searchSourceBuilder.toString());
SearchRequest request = new SearchRequest(new String[]{PRODUCT_INDEX}, searchSourceBuilder);
return request;
}
{
"from": 0,
"size": 2,
"query": {
"bool": {
"must": [
{
"match": {
"skuTitle": {
"query": "小米",
"operator": "OR",
"prefix_length": 0,
"max_expansions": 50,
"fuzzy_transpositions": true,
"lenient": false,
"zero_terms_query": "NONE",
"auto_generate_synonyms_phrase_query": true,
"boost": 1
}
}
}
],
"filter": [
{
"term": {
"catalogId": {
"value": 165,
"boost": 1
}
}
},
{
"terms": {
"brandId": [
8
],
"boost": 1
}
},
{
"term": {
"hasStock": {
"value": true,
"boost": 1
}
}
},
{
"range": {
"skuPrice": {
"from": 1,
"to": 10000,
"include_lower": true,
"include_upper": true,
"boost": 1
}
}
},
{
"nested": {
"query": {
"bool": {
"must": [
{
"term": {
"attrs.attrId": {
"value": "85",
"boost": 1
}
}
},
{
"terms": {
"attrs.attrValue": [
"8G",
"4G"
],
"boost": 1
}
}
],
"adjust_pure_negative": true,
"boost": 1
}
},
"path": "attrs",
"ignore_unmapped": false,
"score_mode": "none",
"boost": 1
}
}
],
"adjust_pure_negative": true,
"boost": 1
}
},
"sort": [
{
"saleCount": {
"order": "desc"
}
}
],
"aggregations": {
"brandAgg": {
"terms": {
"field": "brandId",
"size": 10,
"min_doc_count": 1,
"shard_min_doc_count": 0,
"show_term_doc_count_error": false,
"order": [
{
"_count": "desc"
},
{
"_key": "asc"
}
]
},
"aggregations": {
"brandNameAgg": {
"terms": {
"field": "brandName",
"size": 10,
"min_doc_count": 1,
"shard_min_doc_count": 0,
"show_term_doc_count_error": false,
"order": [
{
"_count": "desc"
},
{
"_key": "asc"
}
]
}
},
"brandImgAgg": {
"terms": {
"field": "brandImg",
"size": 10,
"min_doc_count": 1,
"shard_min_doc_count": 0,
"show_term_doc_count_error": false,
"order": [
{
"_count": "desc"
},
{
"_key": "asc"
}
]
}
}
}
},
"catalogAgg": {
"terms": {
"field": "catalogId",
"size": 10,
"min_doc_count": 1,
"shard_min_doc_count": 0,
"show_term_doc_count_error": false,
"order": [
{
"_count": "desc"
},
{
"_key": "asc"
}
]
},
"aggregations": {
"catalogNameAgg": {
"terms": {
"field": "catalogName",
"size": 10,
"min_doc_count": 1,
"shard_min_doc_count": 0,
"show_term_doc_count_error": false,
"order": [
{
"_count": "desc"
},
{
"_key": "asc"
}
]
}
}
}
},
"attrs": {
"nested": {
"path": "attrs"
},
"aggregations": {
"attrIdAgg": {
"terms": {
"field": "attrs.attrId",
"size": 10,
"min_doc_count": 1,
"shard_min_doc_count": 0,
"show_term_doc_count_error": false,
"order": [
{
"_count": "desc"
},
{
"_key": "asc"
}
]
},
"aggregations": {
"attrNameAgg": {
"terms": {
"field": "attrs.attrName",
"size": 10,
"min_doc_count": 1,
"shard_min_doc_count": 0,
"show_term_doc_count_error": false,
"order": [
{
"_count": "desc"
},
{
"_key": "asc"
}
]
}
},
"attrValueAgg": {
"terms": {
"field": "attrs.attrValue",
"size": 10,
"min_doc_count": 1,
"shard_min_doc_count": 0,
"show_term_doc_count_error": false,
"order": [
{
"_count": "desc"
},
{
"_key": "asc"
}
]
}
}
}
}
}
}
},
"highlight": {
"pre_tags": [
""
],
"post_tags": [
""
],
"fields": {
"skuTitle": {}
}
}
}
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 6,
"relation" : "eq"
},
"max_score" : 1.0,
"hits" : [
{
"_index" : "fire_product",
"_type" : "_doc",
"_id" : "50",
"_score" : 1.0,
"_source" : {
"attrs" : [
{
"attrId" : 85,
"attrName" : "内存",
"attrValue" : "8G;4G"
}
],
"brandId" : 8,
"brandImg" : "https://fireflymall.oss-cn-beijing.aliyuncs.com/2022-02-03b206b057-f351-4fdc-8eeb-0bf9de0c6277_1582979072845574-lp.jpg",
"brandName" : "小米",
"catalogId" : 165,
"catalogName" : "电子书",
"hasStock" : false,
"hotScore" : 0,
"saleCount" : 0,
"skuId" : 50,
"skuImg" : "https://fireflymall.oss-cn-beijing.aliyuncs.com/2022-02-0489f04a93-9af3-49a2-8973-efc7bb593427_1582979072845574-lp.jpg",
"skuPrice" : 0.0,
"skuTitle" : "1 森林绿",
"spuId" : 70
}
},
{
"_index" : "fire_product",
"_type" : "_doc",
"_id" : "51",
"_score" : 1.0,
"_source" : {
"attrs" : [
{
"attrId" : 85,
"attrName" : "内存",
"attrValue" : "8G;4G"
}
],
"brandId" : 8,
"brandImg" : "https://fireflymall.oss-cn-beijing.aliyuncs.com/2022-02-03b206b057-f351-4fdc-8eeb-0bf9de0c6277_1582979072845574-lp.jpg",
"brandName" : "小米",
"catalogId" : 165,
"catalogName" : "电子书",
"hasStock" : false,
"hotScore" : 0,
"saleCount" : 0,
"skuId" : 51,
"skuImg" : "https://fireflymall.oss-cn-beijing.aliyuncs.com/2022-02-0489f04a93-9af3-49a2-8973-efc7bb593427_1582979072845574-lp.jpg",
"skuPrice" : 0.0,
"skuTitle" : "1 玫瑰金",
"spuId" : 70
}
},
{
"_index" : "fire_product",
"_type" : "_doc",
"_id" : "46",
"_score" : 1.0,
"_source" : {
"attrs" : [
{
"attrId" : 85,
"attrName" : "内存",
"attrValue" : "4G;8G"
}
],
"brandId" : 8,
"brandImg" : "https://fireflymall.oss-cn-beijing.aliyuncs.com/2022-02-03b206b057-f351-4fdc-8eeb-0bf9de0c6277_1582979072845574-lp.jpg",
"brandName" : "小米",
"catalogId" : 165,
"catalogName" : "电子书",
"hasStock" : false,
"hotScore" : 0,
"saleCount" : 0,
"skuId" : 46,
"skuImg" : "",
"skuPrice" : 9990.0,
"skuTitle" : "手机 森林绿",
"spuId" : 64
}
},
{
"_index" : "fire_product",
"_type" : "_doc",
"_id" : "47",
"_score" : 1.0,
"_source" : {
"attrs" : [
{
"attrId" : 85,
"attrName" : "内存",
"attrValue" : "4G;8G"
}
],
"brandId" : 8,
"brandImg" : "https://fireflymall.oss-cn-beijing.aliyuncs.com/2022-02-03b206b057-f351-4fdc-8eeb-0bf9de0c6277_1582979072845574-lp.jpg",
"brandName" : "小米",
"catalogId" : 165,
"catalogName" : "电子书",
"hasStock" : false,
"hotScore" : 0,
"saleCount" : 0,
"skuId" : 47,
"skuImg" : "",
"skuPrice" : 9990.0,
"skuTitle" : "手机 玫瑰金",
"spuId" : 64
}
},
{
"_index" : "fire_product",
"_type" : "_doc",
"_id" : "48",
"_score" : 1.0,
"_source" : {
"attrs" : [
{
"attrId" : 85,
"attrName" : "内存",
"attrValue" : "4G"
}
],
"brandId" : 8,
"brandImg" : "https://fireflymall.oss-cn-beijing.aliyuncs.com/2022-02-03b206b057-f351-4fdc-8eeb-0bf9de0c6277_1582979072845574-lp.jpg",
"brandName" : "小米",
"catalogId" : 165,
"catalogName" : "电子书",
"hasStock" : false,
"hotScore" : 0,
"saleCount" : 0,
"skuId" : 48,
"skuImg" : "",
"skuPrice" : 0.0,
"skuTitle" : "12 森林绿",
"spuId" : 65
}
},
{
"_index" : "fire_product",
"_type" : "_doc",
"_id" : "49",
"_score" : 1.0,
"_source" : {
"attrs" : [
{
"attrId" : 85,
"attrName" : "内存",
"attrValue" : "4G"
}
],
"brandId" : 8,
"brandImg" : "https://fireflymall.oss-cn-beijing.aliyuncs.com/2022-02-03b206b057-f351-4fdc-8eeb-0bf9de0c6277_1582979072845574-lp.jpg",
"brandName" : "小米",
"catalogId" : 165,
"catalogName" : "电子书",
"hasStock" : false,
"hotScore" : 0,
"saleCount" : 0,
"skuId" : 49,
"skuImg" : "",
"skuPrice" : 0.0,
"skuTitle" : "12 玫瑰金",
"spuId" : 65
}
}
]
}
}
2.6.2、接收检索响应
buildSearchResult
//3.1封装检索结果
private SearchResult buildSearchResult(SearchResponse response, SearchParamVo searchParam) {
SearchResult searchResult = new SearchResult();
//1、返回查询到的商品
SearchHits hits = response.getHits();
if (hits.getHits() != null && hits.getHits().length > 0) {
List skuEsModels = new ArrayList<>();
for (SearchHit hit : hits) {
String sourceAsString = hit.getSourceAsString();
SkuEsModel skuEsModel = JSON.parseObject(sourceAsString, SkuEsModel.class);
//1.1 判断有无关键字检索 Yes-设置高亮属性 No-不设置高亮属性
if (StringUtils.isNotEmpty(searchParam.getKeyword())) {
HighlightField highlightField = hit.getHighlightFields().get("skuTitle");
String skuTitle = highlightField.getFragments()[0].string();
skuEsModel.setSkuTitle(skuTitle);
}
//1.2 添加到list集合中
skuEsModels.add(skuEsModel);
}
//1.3 设置查询到的所有商品
searchResult.setProducts(skuEsModels);
}
//2、当前所有商品涉及到的所有属性信息
//2.1 先获取attrs聚合
ParsedNested attrs = response.getAggregations().get("attrs");
//2.2 获取attrIdAgg聚合
ParsedLongTerms attrIdAgg = attrs.getAggregations().get("attrIdAgg");
List extends Terms.Bucket> attrIdAggBuckets = attrIdAgg.getBuckets();
if (attrIdAggBuckets != null && attrIdAggBuckets.size() > 0) {
//2.2 创建属性信息list集合
List attrVos = new ArrayList<>();
for (Terms.Bucket bucket : attrIdAggBuckets) {
//2.3 获取:属性ID 设置属性名 设置属性值
Long attrId = bucket.getKeyAsNumber().longValue();
ParsedStringTerms attrNameAgg = bucket.getAggregations().get("attrNameAgg");
String attrName = attrNameAgg.getBuckets().get(0).getKeyAsString();
ParsedStringTerms attrValueAgg = bucket.getAggregations().get("attrValueAgg");
List attrValues = attrValueAgg.getBuckets().stream().map(item -> {
String keyAsString = item.getKeyAsString();
return keyAsString;
}).collect(Collectors.toList());
//2.4 设置:属性ID 设置属性名 设置属性值
SearchResult.AttrVo attrVo = new SearchResult.AttrVo(attrId, attrName, attrValues);
attrVos.add(attrVo);
}
searchResult.setAttrs(attrVos);
}
//3、当前所有商品涉及到的所有分类信息
//3.1 获取catalogAgg聚合
ParsedLongTerms catalogAgg = response.getAggregations().get("catalogAgg");
List extends Terms.Bucket> catalogAggBuckets = catalogAgg.getBuckets();
if (catalogAggBuckets != null && catalogAggBuckets.size() > 0) {
//3.2 创建分类信息list集合
ArrayList catalogVos = new ArrayList<>();
for (Terms.Bucket bucket : catalogAggBuckets) {
//3.3 获取:分类ID 分类名
Long catalogId = bucket.getKeyAsNumber().longValue();
ParsedStringTerms catalogNameAgg = bucket.getAggregations().get("catalogNameAgg");
String catalogName = catalogNameAgg.getBuckets().get(0).getKeyAsString();
//3.4 设置:分类ID 分类名
SearchResult.CatalogVo catalogVo = new SearchResult.CatalogVo(catalogId, catalogName);
catalogVos.add(catalogVo);
}
searchResult.setCatalogs(catalogVos);
}
//4、当前所有商品涉及到的所有品牌信息
//4.1 brandAgg获取聚合
ParsedLongTerms brandAgg = response.getAggregations().get("brandAgg");
List extends Terms.Bucket> brandAggBuckets = brandAgg.getBuckets();
if (brandAggBuckets != null && brandAggBuckets.size() > 0) {
//4.2 创建品牌信息list集合
ArrayList brandVos = new ArrayList<>();
for (Terms.Bucket bucket : brandAggBuckets) {
//4.3 获取:品牌ID 品牌图片 品牌名
long brandId = bucket.getKeyAsNumber().longValue();
ParsedStringTerms brandImgAgg = bucket.getAggregations().get("brandImgAgg");
String brandImg = brandImgAgg.getBuckets().get(0).getKeyAsString();
ParsedStringTerms brandNameAgg = bucket.getAggregations().get("brandNameAgg");
String brandName = brandNameAgg.getBuckets().get(0).getKeyAsString();
//4.4 设置:品牌ID 品牌图片 品牌名
SearchResult.BrandVo brandVo = new SearchResult.BrandVo(brandId, brandName, brandImg);
brandVos.add(brandVo);
}
searchResult.setBrands(brandVos);
}
//5、分页信息
//5.1、设置当前页码
searchResult.setPageNum(searchParam.getPageNum());
//5.2、总记录数
long total = hits.getTotalHits().value;
searchResult.setTotal(total);
//5.3 设置总页码
Long totalPage = (Long) ((total + PRODUCT_PAGE_SIZE - 1) / PRODUCT_PAGE_SIZE);
searchResult.setTotalPage(totalPage);
return searchResult;
}
SearchResult(products=[SkuEsModel(skuId=52, spuId=71, skuTitle=小米手机 黑色, skuPrice=900.0, skuImg=https://fireflymall.oss-cn-beijing.aliyuncs.com/2022-02-091e36d0d6-d95a-40a3-87a3-d097cca48385_1582979072845574-lp.jpg, saleCount=1000, hasStock=true, hotScore=0, brandId=9, catalogId=225, brandName=小米, brandImg=https://fireflymall.oss-cn-beijing.aliyuncs.com/2022-02-1009e04471-7efd-4f1a-810f-c53675b6923d_598042c9n6e4e79e5.jpg, catalogName=手机, attrs=[SkuEsModel.Attr(attrId=87, attrName=鸿蒙, attrValue=[V1, V2])]), SkuEsModel(skuId=53, spuId=71, skuTitle=小米手机 白色, skuPrice=600.0, skuImg=https://fireflymall.oss-cn-beijing.aliyuncs.com/2022-02-091e36d0d6-d95a-40a3-87a3-d097cca48385_1582979072845574-lp.jpg, saleCount=1000, hasStock=true, hotScore=0, brandId=9, catalogId=225, brandName=小米, brandImg=https://fireflymall.oss-cn-beijing.aliyuncs.com/2022-02-1009e04471-7efd-4f1a-810f-c53675b6923d_598042c9n6e4e79e5.jpg, catalogName=手机, attrs=[SkuEsModel.Attr(attrId=87, attrName=鸿蒙, attrValue=[V1, V2])]), SkuEsModel(skuId=54, spuId=74, skuTitle=1 白色, skuPrice=1.0, skuImg=https://fireflymall.oss-cn-beijing.aliyuncs.com/2022-02-09c9595b7c-09b7-4162-885b-d9a7f97309fc_1582979072845574-lp.jpg, saleCount=2000, hasStock=false, hotScore=0, brandId=9, catalogId=225, brandName=小米, brandImg=https://fireflymall.oss-cn-beijing.aliyuncs.com/2022-02-1009e04471-7efd-4f1a-810f-c53675b6923d_598042c9n6e4e79e5.jpg, catalogName=手机, attrs=[SkuEsModel.Attr(attrId=87, attrName=鸿蒙, attrValue=[V2])])], pageNum=1, total=3, totalPage=1, pageNavs=null, brands=[SearchResult.BrandVo(brandId=9, brandName=小米, brandImg=https://fireflymall.oss-cn-beijing.aliyuncs.com/2022-02-1009e04471-7efd-4f1a-810f-c53675b6923d_598042c9n6e4e79e5.jpg)], attrs=[SearchResult.AttrVo(attrId=87, attrName=鸿蒙, attrValue=[V2, V1])], catalogs=[SearchResult.CatalogVo(catalogId=225, catalogName=手机)], navs=[], attrIds=[])2.6.3、检索不到数据
注意:查询不到数据可能是因为 DSL语句是:
{
"terms": {
"attrs.attrValue": [
"V1","V2"
],
"boost": 1
}
}
而es保存的数据是:"attrValue" : "V1;V2",导致匹配不到
修改:SkuEsModel
.....
@Data
public static class Attr {
private Long attrId;
private String attrName;
private List attrValue;
}
修改:SpuInfoServiceImpl
List三、渲染检索页面 3.1、基本数据渲染attrsList = baseAttrs.stream().filter(item -> { return idSet.contains(item.getAttrId()); }).map(item -> { SkuEsModel.Attr attrs1 = new SkuEsModel.Attr(); BeanUtils.copyProperties(item, attrs1); String attrValue = item.getAttrValue(); String[] split = attrValue.split(";"); List attrValues = new ArrayList<>(); for (String i : split) { attrValues.add(i); attrs1.setAttrValue(attrValues); } return attrs1; }).collect(Collectors.toList());
"rig_tab">
th:each="product : ${result.getProducts()}">
"ico">
"/search/#">关注
th:href="|http://item.fire.flymall.com/${product.skuId}.html|">
"dim" th:src="${product.skuImg}"/>
"/static/search/#" th:title='${product.skuTitle}'>
th:src="${product.skuImg}"/>
¥5199.00
"/search/#" th:utext="${product.skuTitle}">
Apple iPhone 7 Plus (A1661) 32G 黑色 移动联通电信4G手机
已有11万+热门评价
"/search/#">二手有售
"/search/#" title="火萤商城Apple产品专营店">火萤商城Apple产品...
'#' title="联系供应商进行咨询">
"/search/img/xcxc.png">
"tab_FO">
"FO_one">
自营
火萤商城自营,品质保证
满赠
该商品参加满赠活动
关键字检索高亮显示
http://localhost:13000//search.html?catalog3Id=225&keyword=%E6%89%8B%E6%9C%BA
th:utext="${product.skuTitle}"
3.2、筛选条件渲染
将结果的品牌、分类、商品属性进行遍历显示,并且点击某个属性值时 可以通过拼接url进行跳转。
"JD_selector">
"title" >
手机商品筛选
"st-ext">共 10135个商品
"JD_nav_logo">
"JD_nav_wrap">
"sl_key">
品牌:
"sl_value">
"sl_value_logo">
searchProducts
function searchProducts(name, value) {
//原來的页面
location.href = replaceParamVal(location.href,name,value,true)
};
replaceParamVal
function replaceParamVal(url, param, replaceval, forceAdd) {
var oUrl = url.toString();
var nUrl;
if (oUrl.indexOf(param) != -1) {
if (forceAdd && oUrl.indexOf(param + "=" + replaceval) == -1) {
if (oUrl.indexOf("?") != -1) {
nUrl = oUrl + "&" + param + "=" + replaceval;
} else {
nUrl = oUrl + "?" + param + "=" + replaceval;
}
} else {
var re = eval('/(' + param + '=)([^&]*)/gi');
nUrl = oUrl.replace(re, param + '=' + replaceval);
}
} else {
if (oUrl.indexOf("?") != -1) {
nUrl = oUrl + "&" + param + "=" + replaceval;
} else {
nUrl = oUrl + "?" + param + "=" + replaceval;
}
}
return nUrl;
};
3.3、搜索按钮渲染
"header_form">
"javascript:searchByKeyword();">搜索
searchByKeyword
function searchByKeyword() {
var keyWord = $("#keyword_input").val();
location.href = replaceParamVal(location.href, "keyword", keyWord, false);
}
3.4、分页数据渲染
"filter_page">
"page_wrap">
"page_a" href="#" th:if="${result.pageNum>1}"
th:attr="pn=${result.getPageNum()-1}">
< 上一页
"#" class="page_a"
th:each="page :${#numbers.sequence(1,result.totalPage)} "
th:if="${result.getTotalPage()>0}"
th:text="${page}"
th:style="${page==result.pageNum?'border: 0;color:#ee2222;background: #fff':''}"
th:attr="pn=${page}"
>1
"#" th:if="${result.pageNum
下一页 >
共169页 到第
页
"javascript:searchByPage();">确定
function searchByPage(){
var page = $("#page_input").val();
location.href = replaceParamVal(location.href, "pageNum", page, false);
}
3.5、页面排序渲染
页面排序功能需要保证,点击某个按钮时,样式会变红,并且其他的样式保持最初的样子;点击某个排序时首先按升序显示,再次点击再变为降序,并且还会显示上升或下降箭头。
页面排序跳转的思路是通过点击某个按钮时会向其class属性添加/去除desc,并根据属性值进行url拼接。
"JD_con_right">
"filter">
"filter_top">
"filter_top_left" th:with="p = ${param.sort}, priceRange = ${param.skuPrice}">
"hotScore"
th:class="${(!#strings.isEmpty(p) && #strings.startsWith(p,'hotScore') && #strings.endsWith(p,'desc')) ? 'sort_a desc' : 'sort_a'}"
th:attr="style=${(#strings.isEmpty(p) || #strings.startsWith(p,'hotScore')) ?
'color: #fff; border-color: #e4393c; background: #e4393c;':'color: #333; border-color: #ccc; background: #fff;' }">
综合排序[[${(!#strings.isEmpty(p) && #strings.startsWith(p,'hotScore') &&
#strings.endsWith(p,'desc')) ?'↓':'↑' }]]
"saleCount"
th:class="${(!#strings.isEmpty(p) && #strings.startsWith(p,'saleCount') && #strings.endsWith(p,'desc')) ? 'sort_a desc' : 'sort_a'}"
th:attr="style=${(!#strings.isEmpty(p) && #strings.startsWith(p,'saleCount')) ?
'color: #fff; border-color: #e4393c; background: #e4393c;':'color: #333; border-color: #ccc; background: #fff;' }">
销量[[${(!#strings.isEmpty(p) && #strings.startsWith(p,'saleCount') &&
#strings.endsWith(p,'desc'))?'↓':'↑' }]]
"skuPrice"
th:class="${(!#strings.isEmpty(p) && #strings.startsWith(p,'skuPrice') && #strings.endsWith(p,'desc')) ? 'sort_a desc' : 'sort_a'}"
th:attr="style=${(!#strings.isEmpty(p) && #strings.startsWith(p,'skuPrice')) ?
'color: #fff; border-color: #e4393c; background: #e4393c;':'color: #333; border-color: #ccc; background: #fff;' }">
价格[[${(!#strings.isEmpty(p) && #strings.startsWith(p,'skuPrice') &&
#strings.endsWith(p,'desc'))?'↓':'↑' }]]
"hotScore" class="fs-tit" src="/search/#">评论分
"hotScore" class="fs-tit" src="/search/#">上架时间
-
"filter_top_right">
共 10135个商品
1/169
"filter_bottom">
"filter_bottom_left">
"fs-cell">收货地
"dizhi">
"dizhi_show">
北京朝阳区三环以内
"dizhi_con">
"/search/#"> 火萤商城配送 "/search/#"> 京尊达 "/search/#"> 货到付款 "/search/#"> 仅显示有货 "/search/#"> 可配送全球 "rig_tab"> th:each="product : ${result.getProducts()}"> "ico"> "/search/#">关注
th:href="|http://item.fire.flymall.com/${product.skuId}.html|"> "dim" th:src="${product.skuImg}"/>
"/static/search/#" th:title='${product.skuTitle}'> th:src="${product.skuImg}"/>
¥5199.00
"/search/#" th:utext="${product.skuTitle}"> SkuTitle
已有11万+热门评价 "/search/#">二手有售
"/search/#" title="火萤商城Apple产品专营店">火萤商城Apple产品... '#' title="联系供应商进行咨询"> "/search/img/xcxc.png">
"tab_FO"> "FO_one">自营 火萤商城自营,品质保证
满赠 该商品参加满赠活动
"filter_page"> "page_wrap"> "page_a" href="#" th:if="${result.pageNum>1}" th:attr="pn=${result.getPageNum()-1}"> < 上一页 "#" class="page_a" th:each="page :${#numbers.sequence(1,result.totalPage)} " th:if="${result.getTotalPage()>0}" th:text="${page}" th:style="${page==result.pageNum?'border: 0;color:#ee2222;background: #fff':''}" th:attr="pn=${page}" >1 "#" th:if="${result.pageNum$(".sort_a").click(function () {
//添加、剔除desc
$(this).toggleClass("desc");
//获取sort属性值并进行url跳转
let sort = $(this).attr("sort");
sort = $(this).hasClass("desc") ? sort + "_desc" : sort + "_asc";
location.href = replaceParamVal(location.href, "sort", sort, false);
return false;
});
$("#skuPriceSearchBtn").click(function () {
var skuPriceFrom = $("#skuPriceFrom").val();
var skuPriceTo = $("#skuPriceTo").val();
location.href = replaceParamVal(location.href, "skuPrice", skuPriceFrom + "_" + skuPriceTo, false);
})
$(".sort_a").click(function () {
//添加、剔除desc
$(this).toggleClass("desc");
//获取sort属性值并进行url跳转
let sort = $(this).attr("sort");
sort = $(this).hasClass("desc") ? sort + "_desc" : sort + "_asc";
location.href = replaceParamVal(location.href, "sort", sort, false);
return false;
});
3.6、 面包屑导航
MallSearchServiceImpl
// 6. 构建面包屑导航 Listattrs1 = searchParam.getAttrs(); if (attrs1 != null && attrs1.size() > 0) { List navVos = attrs1.stream().map(attr -> { String[] split = attr.split("_"); SearchResult.NavVo navVo = new SearchResult.NavVo(); //6.1 设置属性值 navVo.setNavValue(split[1]); //6.2 查询并设置属性名 try { R r = productFeignService.info(Long.parseLong(split[0])); if (r.getCode() == 0) { AttrResponseVo attrResponseVo = JSON.parseObject(JSON.toJSONString(r.get("attr")), new TypeReference () { }); navVo.setNavName(attrResponseVo.getAttrName()); } } catch (Exception e) { log.error(REMOTEL_QUERY_PROPERTY_FAIL, e); } //6.3 设置面包屑跳转链接(当点击该链接时剔除点击属性) String queryString = searchParam.get_queryString(); String encodeAattr = null; try { encodeAattr = URLEncoder.encode(attr, "UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } String replace = queryString.replace("&attrs=" + encodeAattr, "").replace("attrs=" + encodeAattr + "&", "").replace("attrs=" + encodeAattr, ""); navVo.setlink("http://search.fire.flymall.com/list.html" + (replace.isEmpty() ? "" : "?" + replace)); return navVo; }).collect(Collectors.toList()); searchResult.setNavs(navVos); }
AttrResponseVo
package com.firefly.fireflymall.elasticsearch.vo;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
@Data
public class AttrResponseVo {
@TableId
private Long attrId;
private String attrName;
private Integer searchType;
private String icon;
private String valueSelect;
private Integer attrType;
private Long enable;
private Long catelogId;
private Integer showDesc;
private Integer valueType;
private Long attrGroupId;
private String catelogName;
private String attrGroupName;
private Long[] catelogPath;
}
渲染面包屑页面
"JD_ipone">
"JD_ipone_bar">
"JD_ipone_one a">
th:href="${nav.link}" th:each="nav:${result.navs}">: x
"/search/image/right-@1x.png" alt="">
3.7、 条件筛选联动
就是将品牌和分类也封装进面包屑数据中,并且在页面进行th:if的判断,当url有该属性的查询条件时就不进行显示了。
3.8、异步和线程池上一章:day07-商城业务
下一章:day09-商城详情



