前言:准备示例数据如下
索引jmall_product的映射是
{
"jmall_product" : {
"mappings" : {
"properties" : {
"attrs" : {
"type" : "nested",
"properties" : {
"attrId" : {
"type" : "long"
},
"attrName" : {
"type" : "keyword"
},
"attrValue" : {
"type" : "keyword"
}
}
},
"brandId" : {
"type" : "long"
},
"brandImg" : {
"type" : "keyword"
},
"brandName" : {
"type" : "keyword"
},
"catalogId" : {
"type" : "long"
},
"catalogName" : {
"type" : "keyword"
},
"catelogId" : {
"type" : "long"
},
"hasStock" : {
"type" : "boolean"
},
"hosStock" : {
"type" : "boolean"
},
"hotScore" : {
"type" : "long"
},
"saleCount" : {
"type" : "long"
},
"skuId" : {
"type" : "long"
},
"skuImg" : {
"type" : "keyword"
},
"skuPrice" : {
"type" : "keyword"
},
"skuTitle" : {
"type" : "text",
"analyzer" : "ik_smart"
},
"spuId" : {
"type" : "long"
}
}
}
}
}
一:查询标题skuTitle含有Apple的数据
GET /jmall_product/_search
{
"query": {
"bool": {
"must": [
{"match": {
"skuTitle": "Apple"
}}
]
}
}
}
二:根据catalogId=225继续查询
分析:由于filter不参与评分,所以我们使用filter来过滤
GET /jmall_product/_search
{
"query": {
"bool": {
"must": [
{"match": {
"skuTitle": "Apple"
}}
],"filter":[
{
"term": {
"catalogId": "225"
}
}
]
}
}
}
三:继续根据skuPrice价格区间检索
GET /jmall_product/_search
{
"query": {
"bool": {
"must": [
{"match": {
"skuTitle": "Apple"
}}
],"filter":[
{
"term": {
"catalogId": "225"
}
},{
"term": {
"brandId": "12"
}
},{
"range": {
"skuPrice": {
"gte": 5500,
"lte": 6800
}
}
}
]
}
}
}
四:根据attrs检索
根据mapping得知attrs的type是nested,这里不能像catalogId那样直接使用term,具体应该怎么写呢,我们参考官网www.elastic.co/guide/en/el… 对nested有详细的介绍
GET /jmall_product/_search
{
"query": {
"bool": {
"must": [
{"match": {
"skuTitle": "Apple"
}}
],"filter":[
{
"term": {
"catalogId": "225"
}
},{
"term": {
"brandId": "12"
}
},{
"range": {
"skuPrice": {
"gte": 5500,
"lte": 6800
}
}
},{
"nested": {
"path": "attrs",
"query": {
"term": {
"attrs.attrId": {
"value": "3"
}
}
}
}
}
]
}
}
}
五:根据skuPrice降序排序
GET /jmall_product/_search
{
"query": {
"bool": {
"must": [
{"match": {
"skuTitle": "Apple"
}}
],"filter":[
{
"term": {
"catalogId": "225"
}
},{
"term": {
"brandId": "12"
}
},{
"range": {
"skuPrice": {
"gte": 5500,
"lte": 6800
}
}
},{
"nested": {
"path": "attrs",
"query": {
"term": {
"attrs.attrId": {
"value": "3"
}
}
}
}
}
]
}
},"sort": [
{
"skuPrice": {
"order": "desc"
}
}
]
六:分页
es的分页主要靠from和size来实现
GET /jmall_product/_search
{
"query": {
"bool": {
"must": [
{"match": {
"skuTitle": "Apple"
}}
],"filter":[
{
"term": {
"catalogId": "225"
}
},{
"term": {
"brandId": "12"
}
},{
"range": {
"skuPrice": {
"gte": 5500,
"lte": 6800
}
}
},{
"nested": {
"path": "attrs",
"query": {
"term": {
"attrs.attrId": {
"value": "3"
}
}
}
}
}
]
}
},"sort": [
{
"skuPrice": {
"order": "desc"
}
}
],
"from": 0,
"size": 5
}
七:检索字段高亮显示
es的高亮实现通过highlight,可以在返回结果中拼接css代码
"highlight": {
"fields": {
"skuTitle": {}
},
"pre_tags": "<b style='color:red'>",
"post_tags": "</b>"
}
八:对检索结果聚合
"aggs": {
"brandIdAgg": {
"terms": {
"field": "brandId",
"size": 10
},
"aggs": {
"brandNameAgg": {
"terms": {
"field": "brandName",
"size": 10
}
},
"brandImgAgg": {
"terms": {
"field": "brandImg",
"size": 10
}
}
}
},
"catalogIdAgg": {
"terms": {
"field": "catalogId",
"size": 10
},
"aggs": {
"catalogNameAgg": {
"terms": {
"field": "catalogName",
"size": 10
}
}
}
},
"attrsAgg": {
"nested": {
"path": "attrs"
},
"aggs": {
"attrIdAgg": {
"terms": {
"field": "attrs.attrId",
"size": 10
},
"aggs": {
"attrNameAgg": {
"terms": {
"field": "attrs.attrName",
"size": 10
}
}
}
}
}
}
}
九:完整代码
GET /jmall_product/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"skuTitle": "Apple"
}
}
],
"filter": [
{
"term": {
"catalogId": "225"
}
},
{
"terms": {
"brandId": [
"12"
]
}
},
{
"range": {
"skuPrice": {
"gte": 5000,
"lte": 6900
}
}
},
{
"nested": {
"path": "attrs",
"query": {
"bool": {
"must": [
{
"term": {
"attrs.attrId": {
"value": "12"
}
}
}
]
}
}
}
}
]
}
},
"sort": [
{
"skuPrice": {
"order": "desc"
}
}
],
"from": 0,
"size": 5,
"highlight": {
"fields": {
"skuTitle": {}
},
"pre_tags": "<b style='color:red'>",
"post_tags": "</b>"
},
"aggs": {
"brandIdAgg": {
"terms": {
"field": "brandId",
"size": 10
},
"aggs": {
"brandNameAgg": {
"terms": {
"field": "brandName",
"size": 10
}
},
"brandImgAgg": {
"terms": {
"field": "brandImg",
"size": 10
}
}
}
},
"catalogIdAgg": {
"terms": {
"field": "catalogId",
"size": 10
},
"aggs": {
"catalogNameAgg": {
"terms": {
"field": "catalogName",
"size": 10
}
}
}
},
"attrsAgg": {
"nested": {
"path": "attrs"
},
"aggs": {
"attrIdAgg": {
"terms": {
"field": "attrs.attrId",
"size": 10
},
"aggs": {
"attrNameAgg": {
"terms": {
"field": "attrs.attrName",
"size": 10
}
}
}
}
}
}
}
}
执行结果
十:结合java实现
10.1模糊匹配,过滤(按照属性,分类,品牌,价格区间,库存)
SearchSourceBuilder searchSourceBuilder=new SearchSourceBuilder();
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
SearchRequest searchRequest=new SearchRequest();
if(!StringUtils.isEmpty(searchParam.getKeyword())){
boolQueryBuilder.must(QueryBuilders.matchQuery("skuTitle",searchParam.getKeyword()));
//放在最后拼装searchSourceBuilder.query(boolQueryBuilder);
//searchRequest.source(searchSourceBuilder);
}
if(searchParam.getCatalog3Id()!=null){
boolQueryBuilder.filter(QueryBuilders.termQuery("catalog3Id",searchParam.getCatalog3Id()));
}
//由于brandId传过来的是list,所以还要判断size是否>0
if(searchParam.getBrandId()!=null&&searchParam.getBrandId().size()>0){
boolQueryBuilder.filter(QueryBuilders.termsQuery("brandId",searchParam.getBrandId()));
}
//判断是否有库存
if(searchParam.getHasStock()!=null){
boolQueryBuilder.filter(QueryBuilders.termQuery("hasStock",searchParam.getHasStock()==1));
}
//判断价格区间,和前端约定价格skuPrice默认格式为:1_500或_500或500_
// 三种情况
if(searchParam.getSkuPrice()!=null){
String[] s = searchParam.getSkuPrice().split("_");
if(s.length==1){
if(searchParam.getSkuPrice().startsWith("_")){
boolQueryBuilder.filter(QueryBuilders.rangeQuery("skuPrice").lt(s[0]));
}
boolQueryBuilder.filter(QueryBuilders.rangeQuery("skuPrice").gt(s[0]));
}
boolQueryBuilder.filter(QueryBuilders.rangeQuery("skuPrice").gt(s[0]).lt(s[1]));
}
//attr属性检索,注意该属性是nested
//
/*
{
"nested": {
"path": "attrs",
"query": {
"bool": {
"must": [
{
"term": {
"attrs.attrId": {
"value": "12"
}} } ]}}} }
*/
//和前端约定传输格式为:attrs=1_5寸:8寸&attrs=2_16G:8G
if(searchParam.getAttrs()!=null&&searchParam.getAttrs().size()>0){
//使用for循环遍历
for (String s:searchParam.getAttrs()) {
BoolQueryBuilder boolQueryBuilder1 = QueryBuilders.boolQuery();
String[] s1 = s.split("_");
String[] attrValues = s1[1].split(":");
boolQueryBuilder1.must(QueryBuilders.termQuery("attrs.attrId",s1[0]));
boolQueryBuilder1.must(QueryBuilders.termsQuery("attrs.value",attrValues));
boolQueryBuilder.filter(QueryBuilders.nestedQuery("attrs",boolQueryBuilder1, ScoreMode.None));
}
}
//封装所有检索
searchSourceBuilder.query(boolQueryBuilder);
10.2排序,分页,高亮
/**
* 排序,分页,高亮
* sort,from,size,highLight
* "sort": [
* {
* "skuPrice": {
* "order": "desc"
* }
* }
* ],
* "from": 0,
* "size": 5,
* "highlight": {
* "fields": {
* "skuTitle": {}
* },
* "pre_tags": "<b style='color:red'>",
* "post_tags": "</b>"
* }
*/
//排序条件:sort=price/salecount/hotscore_desc/asc
String sort = searchParam.getSort();
if(!StringUtils.isEmpty(sort)){
String[] s = sort.split("_");
searchSourceBuilder.sort(s[0],s[1].equalsIgnoreCase("ASC")?SortOrder.ASC:SortOrder.DESC);
}
//分页条件
//from=(pageNum-1)*size
//TODO 这个size我觉的应该也是需要前端传的,目前先后端写死,待后续优化
Integer pageNum = searchParam.getPageNum();
searchSourceBuilder.from((pageNum-1)* EsConstant.PRODUCT_PAGESIZE).size(EsConstant.PRODUCT_PAGESIZE);
//高亮
if(!StringUtils.isEmpty(searchParam.getKeyword())){
searchSourceBuilder.highlighter().field("skuTitle").preTags("<b style='color:red'>").postTags("</b>");
}
10.3聚合aggs实现
/*
关于聚合agg部分
*/
//一:品牌聚合
TermsAggregationBuilder brandIdAgg = AggregationBuilders.terms("brandIdAgg");
brandIdAgg.field("brandId").size(50);
//品牌子聚合-brandNameAgg
TermsAggregationBuilder brandNameAgg = AggregationBuilders.terms("brandNameAgg");
brandNameAgg.field("brandName").size(1);
brandIdAgg.subAggregation(brandNameAgg);
//品牌子聚合-brandNameAgg
TermsAggregationBuilder brandImgAgg = AggregationBuilders.terms("brandImgAgg");
brandImgAgg.field("brandImg").size(1);
brandIdAgg.subAggregation(brandImgAgg);
searchSourceBuilder.aggregation(brandIdAgg);
//二:分类聚合
TermsAggregationBuilder catalogIdAgg = AggregationBuilders.terms("catalogIdAgg");
catalogIdAgg.field("catalogId").size(50);
TermsAggregationBuilder catalogNameAgg = AggregationBuilders.terms("catalogNameAgg");
catalogNameAgg.field("catalogName").size(1);
catalogIdAgg.subAggregation(catalogNameAgg);
searchSourceBuilder.aggregation(catalogIdAgg);
//三:属性聚合
NestedAggregationBuilder nestedAttrsAgg = AggregationBuilders.nested("attrsAgg", "attrs");
TermsAggregationBuilder attrIdAgg = AggregationBuilders.terms("attrIdAgg");
attrIdAgg.field("attrs.attrId").size(50);
TermsAggregationBuilder attrNameAgg = AggregationBuilders.terms("attrNameAgg");
attrNameAgg.field("attrs.attrName").size(1);
attrIdAgg.subAggregation(attrNameAgg);
TermsAggregationBuilder attrValueAgg = AggregationBuilders.terms("attrValueAgg");
attrValueAgg.field("attrs.attrValue").size(50);
attrIdAgg.subAggregation(attrValueAgg);
nestedAttrsAgg.subAggregation(attrIdAgg);
searchSourceBuilder.aggregation(nestedAttrsAgg);
searchRequest = new SearchRequest(new String[]{EsConstant.PRODUCT_INDEX},searchSourceBuilder);
// SearchRequest source = searchRequest.source(searchSourceBuilder);
return searchRequest;