Elasticsearch的基本语法我们已经学完,足以应对大多数搜索业务需求了。接下来大家就可以基于学习的知识实现商品搜索的业务了。在昨天的作业中要求大家拆分一个独立的微服务:
search-service
,在这个微服务中实现搜索数据的导入、商品数据库数据与elasticsearch索引库数据的同步。接下来的搜索功能也要在search-service
服务中实现。
拆分成单独的服务search-service在上一个作业已经实现了
这次作业主要是过滤条件聚合和竞价排序
工具直接用swagger调试就行,记得在网关的时候把/Search/**路径放行,不用登录校验这样就方便很多。
1.过滤条件聚合
这张图片就是发过来的参数,首先分析一个每个参数在文档中设置的类型,整体流程就是,先根据条件查询,再排序,最后分页取出
1.1 条件查询
"mappings" : {
"properties" : {
"brand" : {
"type" : "keyword"
},
"category" : {
"type" : "keyword"
},
"commentCount" : {
"type" : "integer",
"index" : false
},
"id" : {
"type" : "keyword"
},
"image" : {
"type" : "keyword",
"index" : false
},
"isAD" : {
"type" : "boolean"
},
"name" : {
"type" : "text",
"analyzer" : "ik_max_word"
},
"price" : {
"type" : "integer"
},
"sold" : {
"type" : "integer"
},
"stock" : {
"type" : "integer"
},
"updateTime" : {
"type" : "date"
}
}
因此可以得出:
text ——》 matchQuery
keyword ——》 termQuery
price ——》 rangeQuery
注意:在添加条件之前,记得先进行判空
1.2 排序
整体思路,前端有个参数传过来是——sortBy,表示按照什么排序,我这里设置了默认值为price
if (query.getIsAsc()){
if (query.getSortBy() == null){
query.setSortBy("price");
}
request.source().sort(query.getSortBy(), SortOrder.ASC);
}
1.3 分页
分页是根据前端传过来的pageSize,pageNo决定的
request.source().query(bool)
.from((query.getPageNo() - 1) * query.getPageSize())
.size(query.getPageSize());
1.4 完整代码
@Override
public Page<ItemDOC> queryItemByES(ItemPageQuery query) {
client = new RestHighLevelClient(
RestClient.builder(new HttpHost("127.0.0.1", 9200, "http")));
SearchRequest request = new SearchRequest("item");
BoolQueryBuilder bool = new BoolQueryBuilder();
if (query.getKey() != null) {
bool.filter(QueryBuilders.matchQuery("name", query.getKey()));
}
if (query.getCategory() != null) {
bool.filter(termQuery("category", query.getCategory()));
}
if (query.getBrand() != null){
bool.filter(termQuery("brand", query.getBrand()));
}
if (query.getMinPrice() != null || query.getMaxPrice() != null) {
RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("price");
if (query.getMinPrice() != null) {
rangeQuery.gte(query.getMinPrice()); // 大于等于最小价格
}
if (query.getMaxPrice() != null) {
rangeQuery.lte(query.getMaxPrice()); // 小于等于最大价格
}
bool.filter(rangeQuery);
}
//TODO 设置竞价排名
Page<ItemDOC> page = new Page<>();
try {
if (query.getIsAsc()){
if (query.getSortBy() == null){
query.setSortBy("price");
}
request.source().sort(query.getSortBy(), SortOrder.ASC);
}
request.source().query(bool)
.from((query.getPageNo() - 1) * query.getPageSize())
.size(query.getPageSize());
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
List<ItemDOC> itemDOCS = handleResponse(response);
SearchHits searchHits = response.getHits();
long total = searchHits.getTotalHits().value;
page.setCurrent(query.getPageNo());
page.setSize(query.getPageSize());
page.setTotal(total);
page.setRecords(itemDOCS);
client.close();
}catch (Exception e){
e.printStackTrace();
}
return page;
}
2.竞价排名
要让广告商品(isAD = true
)排在前面,同时保持原有相关性得分,可以用 function_score 查询,通过 filter + weight 的方式给广告商品额外加分。
2.1 实现代码:代码位置上面标注了
//1.过滤出加权函数
FunctionScoreQueryBuilder.FilterFunctionBuilder[] functions = {
new FunctionScoreQueryBuilder.FilterFunctionBuilder(
termQuery("isAD", true), //如果是广告就进行加权
weightFactorFunction(100.0f) //设置权重
)
};
// 2. 用带有 FilterFunctionBuilder[] 的构造器
FunctionScoreQueryBuilder fsqb = new FunctionScoreQueryBuilder(
bool, // 你原来的 bool 查询
functions // 函数数组
)
.scoreMode(FunctionScoreQuery.ScoreMode.SUM) //多个 function 得分相加
.boostMode(CombineFunction.SUM);// 与原始的得分进行累加
// 3. 替换原来的 query
request.source().query(fsqb);
参数说明:
- weightFactorFunction(100f):广告商品权重值
- ScoreMode.SUM:多权重值求和模式
- CombineFunction.SUM:新分数=原始分+权重分