请求DSL
GET guli_product/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"skuTitle": "华为"
}
}
],
"filter": [
{
"term": {
"catalogId": 225
}
},
{
"terms": {
"brandId": [
1,
2,
4
]
}
},
{
"nested": {
"path": "attrs",
"query": {
"bool": {
"must": [
{
"term": {
"attrs.attrId": {
"value": 1
}
}
},
{
"terms": {
"attrs.attrName": [
"电池容量",
"屏幕"
]
}
}
]
}
}
}
},
{
"range": {
"skuPrice": {
"gte": 1000,
"lte": 2000
}
}
}
]
}
},
"sort": [
{
"skuPrice": {
"order": "desc"
}
}
],
"from": 0,
"size": 20,
"highlight": {
"fields": {
"skuTitle": {}
},
"pre_tags": "<b style='color:red'>",
"post_tags": "</b>"
},
"aggs": {
"brandAggs": {
"terms": {
"field": "brandId",
"size": 10
},
"aggs": {
"brandNameAgg": {
"terms": {
"field": "brandName",
"size": 10
}
}
}
},
"catalogIdAggs": {
"terms": {
"field": "catalogId",
"size": 10
}
},
"attrAggs": {
"nested": {
"path": "attrs"
},
"aggs": {
"attrIdAggs": {
"terms": {
"field": "attrs.attrId",
"size": 10
},
"aggs": {
"attrNameAggs": {
"terms": {
"field": "attrs.attrName",
"size": 10
}
}
}
}
}
}
}
}
对应代码
package com.pl.guliamil.gulimailes.service.impl;
import com.alibaba.fastjson.JSON;
import com.pl.guliamil.gulimailes.config.ESConfig;
import com.pl.guliamil.gulimailes.constant.EsConstant;
import com.pl.guliamil.gulimailes.service.ProductSaveService;
import com.pl.guliamil.gulimailes.vo.EsSearchResult;
import com.pl.guliamil.gulimailes.vo.EsSkuVo;
import com.pl.guliamil.gulimailes.vo.SearchParamVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.NestedQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
@Slf4j
@Service("ProductSaveService")
public class ProductSaveServiceimpl implements ProductSaveService {
@Autowired
private RestHighLevelClient esClient;
/**
* 批量上架
*
* @param skuEsModels
* @return boolean
* @exception
* @author silenter
* @date 2020/11/27 6:14
*/
@Override
public boolean saveProductAsIndices(List<EsSkuVo> skuEsModels) throws IOException {
//构造批量请求
//A BulkRequest can be used to execute multiple index, update and/or delete operations using a single request.
BulkRequest bulkRequest = new BulkRequest();
skuEsModels.forEach(data->{
//TODO 构造索引请求 指定索引
IndexRequest indexRequest = new IndexRequest(EsConstant.PRODUCT_INDEX);
String jsonString = JSON.toJSONString(data);
//TODO 索引资源 指定值,和类型
indexRequest.source(jsonString, XContentType.JSON);
//TODO add
bulkRequest.add(indexRequest);
});
List<String> collect = null;
try {
BulkResponse bulkResponse = esClient.bulk(bulkRequest, ESConfig.COMMON_OPTIONS);
boolean hasFailures = bulkResponse.hasFailures();
collect = Arrays.asList(bulkResponse.getItems()).stream().map(item -> {
return item.getId();
}).collect(Collectors.toList());
return !hasFailures;
}catch (Exception e){
e.printStackTrace();
}
log.info("商品上架完成:{}",collect);
return false;
}
/**
* 查询
*
* @param searchParam
* @return com.pl.guliamil.gulimailes.vo.EsSearchResult
* @exception
* @author silenter
* @date 2021/1/11 18:29
*/
@Override
public EsSearchResult getSearchResult(SearchParamVo searchParam) {
EsSearchResult searchResult= null;
SearchRequest request = bulidSearchRequest(searchParam);
try {
SearchResponse searchResponse = esClient.search(request, ESConfig.COMMON_OPTIONS);
searchResult = bulidSearchResult(searchParam,searchResponse);
} catch (IOException e) {
e.printStackTrace();
}
return searchResult;
}
/**
* parse and build response
*
* @param searchParam
* @param searchResponse
* @return com.pl.guliamil.gulimailes.vo.EsSearchResult
* @exception
* @author silenter
* @date 2021/1/11 18:29
*/
private EsSearchResult bulidSearchResult(SearchParamVo searchParam, SearchResponse searchResponse) {
EsSearchResult result = new EsSearchResult();
//1.parse hits&&处理 高亮
SearchHits hits = searchResponse.getHits();
if (hits.getHits()!=null&&hits.getHits().length>0){
List<EsSkuVo> skuEsModels = new ArrayList<>();
for (SearchHit hit : hits) {
String sourceAsString = hit.getSourceAsString();
EsSkuVo skuEsModel = JSON.parseObject(sourceAsString, EsSkuVo.class);
//设置高亮属性
if (!StringUtils.isEmpty(searchParam.getKeyword())) {
//获取高亮字段 见n2
HighlightField skuTitle = hit.getHighlightFields().get("skuTitle");
String highLight = skuTitle.getFragments()[0].string();
skuEsModel.setSkuTitle(highLight);
}
skuEsModels.add(skuEsModel);
}
result.setProduct(skuEsModels);
}
//2. 封装分页信息
//2.1 当前页码
result.setPageNum(searchParam.getPageNum());
//2.2 总记录数
long total = hits.getTotalHits().value;
result.setTotal(total);
//2.3 总页码
Integer totalPages = (int)total % EsConstant.PRODUCT_PAGESIZE == 0 ?
(int)total / EsConstant.PRODUCT_PAGESIZE : (int)total / EsConstant.PRODUCT_PAGESIZE + 1;
result.setTotalPages(totalPages);
List<Integer> pageNavs = new ArrayList<>();
for (int i = 1; i <= totalPages; i++) {
pageNavs.add(i);
}
result.setPageNavs(pageNavs);
//3. parse aggs
Aggregations aggregations = searchResponse.getAggregations();
//3.1 查询结果涉及到的品牌
List<EsSearchResult.BrandVo> brandVos = new ArrayList<>();
//ParsedLongTerms用于接收terms聚合的结果,并且可以把key转化为Long类型的数据
ParsedLongTerms brandAgg = aggregations.get("brandAggs");
for (Terms.Bucket bucket : brandAgg.getBuckets()) {
//3.1 得到品牌id
Long brandId = bucket.getKeyAsNumber().longValue();
Aggregations subBrandAggs = bucket.getAggregations();
//3.2 得到品牌名字
Terms brandNameAgg=subBrandAggs.get("brandNameAgg");
String brandName = brandNameAgg.getBuckets().get(0).getKeyAsString();
EsSearchResult.BrandVo brandVo = new EsSearchResult.BrandVo(brandId, brandName);
brandVos.add(brandVo);
}
result.setBrands(brandVos);
//4. 查询涉及到的所有分类
List<EsSearchResult.CatalogVo> catalogVos = new ArrayList<>();
ParsedLongTerms catalogAgg = aggregations.get("catalogIdAggs");
for (Terms.Bucket bucket : catalogAgg.getBuckets()) {
//4.1 获取分类id
Long catalogId = bucket.getKeyAsNumber().longValue();
Aggregations subcatalogAggs = bucket.getAggregations();
//4.2 获取分类名
ParsedStringTerms catalogNameAgg=subcatalogAggs.get("catalogNameAgg");
String catalogName = catalogNameAgg.getBuckets().get(0).getKeyAsString();
EsSearchResult.CatalogVo catalogVo = new EsSearchResult.CatalogVo(catalogId, catalogName);
catalogVos.add(catalogVo);
}
result.setCatalogs(catalogVos);
//5 查询涉及到的所有属性
List<EsSearchResult.AttrVo> attrVos = new ArrayList<>();
//ParsedNested用于接收内置属性的聚合
ParsedNested parsedNested=aggregations.get("attrs");
ParsedLongTerms attrIdAgg=parsedNested.getAggregations().get("attrIdAgg");
for (Terms.Bucket bucket : attrIdAgg.getBuckets()) {
//5.1 查询属性id
Long attrId = bucket.getKeyAsNumber().longValue();
Aggregations subAttrAgg = bucket.getAggregations();
//5.2 查询属性名
ParsedStringTerms attrNameAgg=subAttrAgg.get("attrNameAgg");
String attrName = attrNameAgg.getBuckets().get(0).getKeyAsString();
//5.3 查询属性值
ParsedStringTerms attrValueAgg = subAttrAgg.get("attrValueAgg");
List<String> attrValues = new ArrayList<>();
for (Terms.Bucket attrValueAggBucket : attrValueAgg.getBuckets()) {
String attrValue = attrValueAggBucket.getKeyAsString();
attrValues.add(attrValue);
}
EsSearchResult.AttrVo attrVo = new EsSearchResult.AttrVo(attrId, attrName, attrValues);
attrVos.add(attrVo);
}
result.setAttrs(attrVos);
return result;
}
/**
* 构造请求参数
*
* @param searchParam
* @return org.elasticsearch.action.search.SearchRequest
* @exception
* @author silenter
* @date 2021/1/11 14:27
*/
private SearchRequest bulidSearchRequest(SearchParamVo searchParam) {
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
buildBoolQuery(searchParam, searchSourceBuilder);
addSort(searchParam, searchSourceBuilder);
addPageQuery(searchParam, searchSourceBuilder);
addHighlight(searchParam, searchSourceBuilder);
buildAggs(searchSourceBuilder);
log.debug("构建的DSL语句 {}",searchSourceBuilder.toString());
SearchRequest request = new SearchRequest(new String[]{EsConstant.PRODUCT_INDEX}, searchSourceBuilder);
return request;
}
/**
* 构建聚合函数
*
* @param searchSourceBuilder
* @return void
* @exception
* @author silenter
* @date 2021/1/11 16:15
*/
private void buildAggs(SearchSourceBuilder searchSourceBuilder) {
//5. 聚合
//5.1 按照brand聚合
//"brandAggs": {"terms": {"field": "brandId", "size": 10},"aggs": {"brandNameAgg": {"terms": {"field": "brandName", "size": 10 }}}}
TermsAggregationBuilder brandAgg = AggregationBuilders.terms("brandAggs").field("brandId").size(10);
TermsAggregationBuilder brandNameAgg = AggregationBuilders.terms("brandNameAgg").field("brandName").size(10);
brandAgg.subAggregation(brandNameAgg);
searchSourceBuilder.aggregation(brandAgg);
//5.2 按照catalog聚合
TermsAggregationBuilder catalogAgg = AggregationBuilders.terms("catalogIdAggs").field("catalogId").size(10);
TermsAggregationBuilder catalogNameAgg = AggregationBuilders.terms("catalogNameAgg").field("catalogName").size(10);
catalogAgg.subAggregation(catalogNameAgg);
searchSourceBuilder.aggregation(catalogAgg);
//5.3 按照attrs聚合
NestedAggregationBuilder nestedAggregationBuilder = new NestedAggregationBuilder("attrs", "attrs");
//按照attrId聚合
TermsAggregationBuilder attrIdAgg = AggregationBuilders.terms("attrIdAgg").field("attrs.attrId");
//按照attrId聚合之后再按照attrName和attrValue聚合
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);
}
/**
* 高亮查询
*
* @param searchParam
* @param searchSourceBuilder
* @return void
* @exception
* @author silenter
* @date 2021/1/11 15:33
*/
private void addHighlight(SearchParamVo searchParam, SearchSourceBuilder searchSourceBuilder) {
//4. 高亮highlight
if (!StringUtils.isEmpty(searchParam.getKeyword())) {
HighlightBuilder highlightBuilder = new HighlightBuilder();
highlightBuilder.field("skuTitle");
highlightBuilder.preTags("<b style='color:red'>");
highlightBuilder.postTags("</b>");
searchSourceBuilder.highlighter(highlightBuilder);
}
}
/**
* 添加分页查询
*
* @param searchParam
* @param searchSourceBuilder
* @return void
* @exception
* @author silenter
* @date 2021/1/11 15:30
*/
private void addPageQuery(SearchParamVo searchParam, SearchSourceBuilder searchSourceBuilder) {
//3. 分页
searchSourceBuilder.from((searchParam.getPageNum() - 1) * EsConstant.PRODUCT_PAGESIZE);
searchSourceBuilder.size(EsConstant.PRODUCT_PAGESIZE);
}
/**
* 排序
*
* @param searchParam
* @param searchSourceBuilder
* @return void
* @exception
* @author silenter
* @date 2021/1/11 15:23
*/
private void addSort(SearchParamVo searchParam, SearchSourceBuilder searchSourceBuilder) {
//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);
}
}
/**
* 构建bool query
*
* @param searchParam
* @param searchSourceBuilder
* @return void
* @exception
* @author silenter
* @date 2021/1/11 15:21
*/
private void buildBoolQuery(SearchParamVo searchParam, SearchSourceBuilder searchSourceBuilder) {
//1. 构建bool query start
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 {"term": {"catalogId": 225 }}
if (searchParam.getCatalog3Id()!=null){
boolQueryBuilder.filter(QueryBuilders.termQuery("catalogId", searchParam.getCatalog3Id()));
}
//1.2.2 brand {"terms": {"brandId": [1,2,4] }}
if (searchParam.getBrandId()!=null&&searchParam.getBrandId().size()>0) {
boolQueryBuilder.filter(QueryBuilders.termsQuery("brandId",searchParam.getBrandId()));
}
//1.2.3 构建 attrs-nested start
//attrs=1_电池:屏幕
List<String> 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.attrName", attrValues));
});
}
// nestedQuery(String path<嵌入查询的对象>, QueryBuilder query<查询条件>, ScoreMode scoreMode<指定查询子对象的得分,可以查平均分,最低分,最高分,也算一个过滤条件>) 见n1
NestedQueryBuilder nestedQueryBuilder = QueryBuilders.nestedQuery("attrs", queryBuilder, ScoreMode.None);
//1.2.3 构建 attrs-nested end
boolQueryBuilder.filter(nestedQueryBuilder);
//1.2.4 priceRange 价格区间 1000_2000
RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("skuPrice");
if (!StringUtils.isEmpty(searchParam.getSkuPrice())) {
String[] prices = searchParam.getSkuPrice().split("_");
if (prices.length == 1) {
//_1000 >1000
if (searchParam.getSkuPrice().startsWith("_")) {
rangeQueryBuilder.lte(Integer.parseInt(prices[0]));
}else {
rangeQueryBuilder.gte(Integer.parseInt(prices[0]));
}
} else if (prices.length == 2) {
//1000_2000
if (!prices[0].isEmpty()) {
rangeQueryBuilder.gte(Integer.parseInt(prices[0]));
}
rangeQueryBuilder.lte(Integer.parseInt(prices[1]));
}
boolQueryBuilder.filter(rangeQueryBuilder);
}
//1. bool query构建完成
searchSourceBuilder.query(boolQueryBuilder);
//1. 构建bool query end
}
}
请求
![](https://img.haomeiwen.com/i18721752/3406819d761152d2.png)
image.png
网友评论