栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 前沿技术 > 大数据 > 大数据系统

day08-【检索服务】

day08-【检索服务】

检索服务

一、显示页面

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.yml

thymeleaf.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 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 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 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 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());
三、渲染检索页面 3.1、基本数据渲染
"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">
                    

  • th:href="${'javascript:searchProducts("brandId",'+brand.brandId+')'}"> th:src="${brand.brandImg}" alt=""/> th:text="${brand.brandName}"> 华为(HUAWEI) "sl_ext"> "/search/#"> 更多 "/search/#"> 多选 + + "JD_pre"> "sl_key"> 分类: "sl_value">

  • th:href="${'javascript:searchProducts("catalogId",'+catalog.catalogId+')'}" th:text="${catalog.catalogName}">属性值 "sl_ext"> "/search/#"> 更多 "/search/#"> 多选 + + "JD_pre" th:each="attr:${result.getAttrs()}"> "sl_key"> 属性名: "sl_value">

  • th:href="${'javascript:searchProducts("attrs","'+attr.attrId+'_'+val+'")'}" th:text="${val}">属性值 "JD_show"> "/search/#"> 更多选项( CPU核数、网络、机身颜色 等)
  • 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/image/down-@1x.png" alt="">
  • 朝阳 "/search/image/down-@1x.png" alt="">
  • 三环以内 "/search/image/down-@1x.png" alt=""> "container"> "content1" style="z-index: 1;"> "/search/#">北京 "/search/#">上海 "/search/#">天津 "/search/#">重庆 "/search/#">河北 "/search/#">山西 "/search/#">河南 "/search/#">辽宁 "/search/#">吉林 "/search/#">黑龙江 "/search/#">内蒙古 "/search/#">江苏 "/search/#">山东 "/search/#">安徽 "/search/#">浙江 "/search/#">福建 "/search/#">湖北 "/search/#">湖南 "/search/#">广东 "/search/#">广西 "/search/#">江西 "/search/#">四川 "/search/#">海南 "/search/#">贵州 "/search/#">云南 "/search/#">西藏 "/search/#">陕西 "/search/#">甘肃 "/search/#">青海 "/search/#">宁夏 "/search/#">新疆 "/search/#">港澳 "/search/#">台湾 "/search/#">钓鱼岛 "/search/#">海外 "content2"> "/search/#">朝阳区 "/search/#">海淀区 "/search/#">西城区 "/search/#">东城区 "/search/#">大兴区 "/search/#">丰台区 "/search/#">昌平区 "/search/#">顺义区 "content3"> "/search/#">三环以内 "/search/#">管庄 "/search/#">北苑 "/search/#">定福庄 "/search/#">三环到四环之间 "/search/#">四环到五环之间 "/search/#">五环到六环之间 "filter_bottom_right">

    "/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 下一页 > 169页  到第 "javascript:searchByPage();">确定
  • $(".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. 构建面包屑导航
    List attrs1 = 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-商城详情

    转载请注明:文章转载自 www.mshxw.com
    我们一直用心在做
    关于我们 文章归档 网站地图 联系我们

    版权所有 (c)2021-2022 MSHXW.COM

    ICP备案号:晋ICP备2021003244-6号