title: 樂優(yōu)商城學習筆記十七-搜索過濾(二)
date: 2019-04-20 14:37:56
tags:
- 樂優(yōu)商城
- java
- springboot
categories:
- 樂優(yōu)商城
3.生成規(guī)格參數(shù)過濾
3.1.謀而后動
有四個問題需要先思考清楚:
- 什么時候顯示規(guī)格參數(shù)過濾宦芦?
- 如何知道哪些規(guī)格需要過濾弧轧?
- 要過濾的參數(shù)问畅,其可選值是如何獲取的谍失?
- 規(guī)格過濾的可選值夭咬,其數(shù)據(jù)格式怎樣的?
什么情況下顯示有關(guān)規(guī)格參數(shù)的過濾僧须?
如果用戶尚未選擇商品分類图仓,或者聚合得到的分類數(shù)大于1,那么就沒必要進行規(guī)格參數(shù)的聚合揭厚。因為不同分類的商品却特,其規(guī)格是不同的。
因此筛圆,我們在后臺需要對聚合得到的商品分類數(shù)量進行判斷裂明,如果等于1,我們才繼續(xù)進行規(guī)格參數(shù)的聚合太援。
如何知道哪些規(guī)格需要過濾漾岳?
我們不能把數(shù)據(jù)庫中的所有規(guī)格參數(shù)都拿來過濾。因為并不是所有的規(guī)格參數(shù)都可以用來過濾粉寞,參數(shù)的值是不確定的尼荆。
值的慶幸的是,我們在設計規(guī)格參數(shù)時唧垦,已經(jīng)標記了某些規(guī)格可搜索捅儒,某些不可搜索。
因此,一旦商品分類確定巧还,我們就可以根據(jù)商品分類查詢到其對應的規(guī)格鞭莽,從而知道哪些規(guī)格要進行搜索。
要過濾的參數(shù)麸祷,其可選值是如何獲取的澎怒?
雖然數(shù)據(jù)庫中有所有的規(guī)格參數(shù),但是不能把一切數(shù)據(jù)都用來供用戶選擇阶牍。
與商品分類和品牌一樣喷面,應該是從用戶搜索得到的結(jié)果中聚合,得到與結(jié)果品牌的規(guī)格參數(shù)可選值走孽。
規(guī)格過濾的可選值惧辈,其數(shù)據(jù)格式怎樣的?
我們直接看頁面效果:
我們之前存儲時已經(jīng)將數(shù)據(jù)分段磕瓷,恰好符合這里的需求
3.3.實戰(zhàn)
接下來盒齿,我們就用代碼實現(xiàn)剛才的思路。
總結(jié)一下困食,應該是以下幾步:
- 1)用戶搜索得到商品边翁,并聚合出商品分類
- 2)判斷分類數(shù)量是否等于1,如果是則進行規(guī)格參數(shù)聚合
- 3)先根據(jù)分類硕盹,查找可以用來搜索的規(guī)格
- 4)對規(guī)格參數(shù)進行聚合
- 5)將規(guī)格參數(shù)聚合結(jié)果整理后返回
3.3.1.擴展返回結(jié)果
返回結(jié)果中需要增加新數(shù)據(jù)符匾,用來保存規(guī)格參數(shù)過濾條件。這里與前面的品牌和分類過濾的json結(jié)構(gòu)類似:
[
{
"k":"規(guī)格參數(shù)名",
"options":["規(guī)格參數(shù)值","規(guī)格參數(shù)值"]
}
]
因此莱睁,在java中我們用List<Map<String,Object>>來表示。
/**
* @Author smallmartial
* @Date 2019/4/19
* @Email smallmarital@qq.com
*/
@Data
public class SearchResult extends PageResult<Goods> {
private List<Category> categories;//分類過濾條件
private List<Brand> brands;//品牌過濾條件
private List<Map<String,Object>> specs; // 規(guī)格參數(shù)過濾條件
public SearchResult(){}
public SearchResult(Long total, Integer totalPage, List<Goods> item, List<Category> categories, List<Brand> brands, List<Map<String, Object>> specs) {
super(total, totalPage, item);
this.categories = categories;
this.brands = brands;
this.specs = specs;
}
}
3.3.2.判斷是否需要聚合
首先芒澜,在聚合得到商品分類后仰剿,判斷分類的個數(shù),如果是1個則進行規(guī)格聚合:
if (categories !=null && categories.size() == 1){
specs = buildSpecificationAgg(categories.get(0).getId(),basicQuery);
}
我們將聚合的代碼抽取到了一個buildSpecificationAgg
方法中痴晦。
3.3.3.獲取需要聚合的規(guī)格參數(shù)
然后南吮,我們需要根據(jù)商品分類,查詢所有可用于搜索的規(guī)格參數(shù):
List<SpecParam> params = specificationClient.querySpecSpecParam(null, cid, true, null);
要注意的是誊酌,這里我們需要根據(jù)id查詢規(guī)格部凑,而規(guī)格參數(shù)接口需要從商品微服務提供
商品微服務:ly-item-interface中提供接口:
@RequestMapping("spec")
public interface SpecificationApi {
@GetMapping("/params")
List<SpecParam> querySpecParam(SpecParam specParam);
}
搜索服務中調(diào)用:
@FeignClient("item-service")
public interface SpecificationClient extends SpecificationApi {
}
3.3.4.聚合規(guī)格參數(shù)
因為規(guī)格參數(shù)保存時不做分詞,因此其名稱會自動帶上一個.keyword后綴:
for (SpecParam param : params) {
String name = param.getName();
queryBuilder.addAggregation(AggregationBuilders.terms(name)
.field("specs."+name+".keyword"));
}
3.3.5.解析聚合結(jié)果
//解析結(jié)果
Aggregations aggs = result.getAggregations();
for (SpecParam param : params) {
//規(guī)格參數(shù)名
Map<String,Object> map = new HashMap();
//準備map
String name = param.getName();
map.put("k",name);
StringTerms terms = (StringTerms) aggs.get(name);
map.put("options",terms.getBuckets().stream().map(b -> b.getKeyAsString()).collect(Collectors.toList()));
specs.add(map);
}
3.3.6.最終的代碼
/**
* 聚合規(guī)格參數(shù)查詢
* @param cid
* @param basicQuery
* @return
*/
private List<Map<String,Object>> buildSpecificationAgg(Long cid, QueryBuilder basicQuery) {
List<Map<String, Object>> specs = new ArrayList<>();
//查詢所需要的結(jié)果
List<SpecParam> params = specificationClient.querySpecSpecParam(null, cid, true, null);
//聚合
NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
//帶上查詢條件
queryBuilder.withQuery(basicQuery);
for (SpecParam param : params) {
String name = param.getName();
queryBuilder.addAggregation(AggregationBuilders.terms(name)
.field("specs."+name+".keyword"));
}
//獲取結(jié)果
AggregatedPage<Goods> result = template.queryForPage(queryBuilder.build(), Goods.class);
// 查詢
//解析結(jié)果
Aggregations aggs = result.getAggregations();
for (SpecParam param : params) {
//規(guī)格參數(shù)名
Map<String,Object> map = new HashMap();
//準備map
String name = param.getName();
map.put("k",name);
StringTerms terms = (StringTerms) aggs.get(name);
map.put("options",terms.getBuckets().stream().map(b -> b.getKeyAsString()).collect(Collectors.toList()));
specs.add(map);
}
return specs;
}
結(jié)果
3.4.2.展示或收起過濾條件
是不是感覺顯示的太多了碧浊,我們可以通過按鈕點擊來展開和隱藏部分內(nèi)容:
我們在data中定義變量涂邀,記錄展開或隱藏的狀態(tài):
然后在按鈕綁定點擊事件,以改變show的取值:
在展示規(guī)格時箱锐,對show進行判斷:
OK比勉!
4.過濾條件的篩選
當我們點擊頁面的過濾項,要做哪些事情?
- 把過濾條件保存在search對象中(watch監(jiān)控到search變化后就會發(fā)送到后臺)
- 在頁面頂部展示已選擇的過濾項
- 把商品分類展示到頂部面包屑
4.1.保存過濾項
4.1.1.定義屬性
我們把已選擇的過濾項保存在search中:
要注意浩聋,在created構(gòu)造函數(shù)中會對search進行初始化观蜗,所以要在構(gòu)造函數(shù)中對filter進行初始化:
search.filter是一個對象,結(jié)構(gòu):
{
"過濾項名":"過濾項值"
}
4.1.2.綁定點擊事件
給所有的過濾項綁定點擊事件:
要注意衣洁,點擊事件傳2個參數(shù):
- k:過濾項的key
- option:當前過濾項對象
在點擊事件中墓捻,保存過濾項到selectedFilter
:
selectFilter(k, o){
const obj = {};
Object.assign(obj, this.search);
if(k === 'cid3' || k === 'brandId'){
o = o.id;
}
obj.filter[k] = o;
this.search = obj;
}
另外,這里search對象中嵌套了filter對象坊夫,請求參數(shù)格式化時需要進行特殊處理砖第,修改common.js中的一段代碼:
我們刷新頁面,點擊后通過瀏覽器功能查看search.filter
的屬性變化:
4.2.1.拓展請求對象
我們需要在請求類:SearchRequest
中添加屬性践樱,接收過濾屬性厂画。過濾屬性都是鍵值對格式,但是key不確定,所以用一個map來接收即可。
4.2.2.添加過濾條件
目前霍狰,我們的基本查詢是這樣的:
private QueryBuilder buildBasicQuery(SearchRequest request) {
BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
// 基本查詢條件
queryBuilder.must(QueryBuilders.matchQuery("all", request.getKey()).operator(Operator.AND));
// 過濾條件構(gòu)建器
BoolQueryBuilder filterQueryBuilder = QueryBuilders.boolQuery();
// 整理過濾條件
Map<String, String> filter = request.getFilter();
for (Map.Entry<String, String> entry : filter.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
// 商品分類和品牌要特殊處理
if (key != "cid3" && key != "brandId") {
key = "specs." + key + ".keyword";
}
// 字符串類型佳励,進行term查詢
filterQueryBuilder.must(QueryBuilders.termQuery(key, value));
}
// 添加過濾條件
queryBuilder.filter(filterQueryBuilder);
return queryBuilder;
}
總結(jié)
頁面過濾部分功能未能實現(xiàn),點擊品牌分類無法查詢纪蜒。