ElasticSearch实战

1
2
3
4
5
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.6.1</version>
</dependency>

使用Java API来操作ES集群初始化连接,基于RestClient.builder方法来构建RestClientBuilder,使用RestHighLevelClient去连接ES集群,用HttpHost来添加ES的节点。

1
2
3
4
5
6
7
8
9
// 建立与ES的连接
// 1. 使用RestHighLevelClient构建客户端连接。
// 2. 基于RestClient.builder方法来构建RestClientBuilder
// 3. 用HttpHost来添加ES的节点
RestClientBuilder restClientBuilder = RestClient.builder(
new HttpHost("192.168.21.130", 9200, "http")
, new HttpHost("192.168.21.131", 9200, "http")
, new HttpHost("192.168.21.132", 9200, "http"));
RestHighLevelClient restHighLevelClient = new RestHighLevelClient(restClientBuilder);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
public void add(JobDetail jobDetail) throws IOException {
// 构建IndexRequest对象,用来描述ES发起请求的数据
IndexRequest indexRequest = new IndexRequest(JOB_IDX);
// 设置文档ID
indexRequest.id(String.valueOf(jobDetail.getId()));
// 使用FastJSON将实体类对象转换为JSON
String json = JSONObject.toJSONString(jobDetail);
// 使用IndexRequest.source方法设置文档数据,并设置请求的数据为JSON格式
indexRequest.source(json, XContentType.JSON);
// 使用ES RestHighLevelClient调用index方法发起请求,将一个文档添加到索引中
restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);
}
public JobDetail findById(long id) throws IOException {
GetRequest getRequest = new GetRequest(JOB_IDX, id + ""); // 构建GetRequest请求
// 使用RestHighLevelClient.get发送GetRequest请求,并获取到ES服务器的响应。
GetResponse getResponse = restHighLevelClient.get(getRequest, RequestOptions.DEFAULT);
String json = getResponse.getSourceAsString();// 将ES响应的数据转换为JSON字符串
// 并使用FastJSON将JSON字符串转换为JobDetail类对象
JobDetail jobDetail = JSONObject.parseObject(json, JobDetail.class);
jobDetail.setId(id);// 单独设置ID
return jobDetail;
}
public void update(JobDetail jobDetail) throws IOException {
// 判断对应ID的文档是否存在,构建GetRequest
GetRequest getRequest = new GetRequest(JOB_IDX, jobDetail.getId() + "");
// 执行client的exists方法,发起请求,判断是否存在
boolean exists = restHighLevelClient.exists(getRequest, RequestOptions.DEFAULT);
if(exists) {
// 构建UpdateRequest请求
UpdateRequest updateRequest = new UpdateRequest(JOB_IDX, jobDetail.getId() + "");
// 设置UpdateRequest的文档,并配置为JSON格式
updateRequest.doc(JSONObject.toJSONString(jobDetail), XContentType.JSON);
// 执行client发起update请求
restHighLevelClient.update(updateRequest, RequestOptions.DEFAULT);
}
}
public void deleteById(long id) throws IOException {
DeleteRequest deleteRequest = new DeleteRequest(JOB_IDX, id + "");// 构建delete请求
restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);// 使用RestHighLevelClient执行delete请求
}
public List<JobDetail> searchByKeywords(String keywords) throws IOException {
// 构建SearchRequest检索请求 专门用来进行全文检索、关键字检索的API
SearchRequest searchRequest = new SearchRequest(JOB_IDX);
// 创建一个SearchSourceBuilder专门用于构建查询条件
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
// 使用QueryBuilders.multiMatchQuery构建一个查询条件(搜索title、jd),并配置到SearchSourceBuilder
MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery(keywords, "title", "jd");
searchSourceBuilder.query(multiMatchQueryBuilder);// 将查询条件设置到查询请求构建器中
searchRequest.source(searchSourceBuilder);// 调用SearchRequest.source将查询条件设置到检索请求
// 执行RestHighLevelClient.search发起请求
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
SearchHit[] hitArray = searchResponse.getHits().getHits();
ArrayList<JobDetail> jobDetailArrayList = new ArrayList<>();
for (SearchHit documentFields : hitArray) {// 遍历结果
String json = documentFields.getSourceAsString();// 获取命中的结果
JobDetail jobDetail = JSONObject.parseObject(json, JobDetail.class);// 将JSON字符串转换为对象
jobDetail.setId(Long.parseLong(documentFields.getId()));// 使用SearchHit.getId设置文档ID
jobDetailArrayList.add(jobDetail);
}
return jobDetailArrayList;
}
public Map<String, Object> searchByPage(String keywords, int pageNum, int pageSize) throws IOException {
// 构建SearchRequest检索请求 专门用来进行全文检索、关键字检索的API
SearchRequest searchRequest = new SearchRequest(JOB_IDX);
// 创建一个SearchSourceBuilder专门用于构建查询条件
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
// 使用QueryBuilders.multiMatchQuery构建一个查询条件(搜索title、jd),并配置到SearchSourceBuilder
MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery(keywords, "title", "jd");
searchSourceBuilder.query(multiMatchQueryBuilder); // 将查询条件设置到查询请求构建器中
searchSourceBuilder.size(pageSize);// 每页显示多少条
searchSourceBuilder.from((pageNum - 1) * pageSize);// 设置从第几条开始查询
searchRequest.source(searchSourceBuilder);// 调用SearchRequest.source将查询条件设置到检索请求
// 执行RestHighLevelClient.search发起请求
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
SearchHit[] hitArray = searchResponse.getHits().getHits();
ArrayList<JobDetail> jobDetailArrayList = new ArrayList<>();
for (SearchHit documentFields : hitArray) {// 遍历结果
String json = documentFields.getSourceAsString();// 获取命中的结果
JobDetail jobDetail = JSONObject.parseObject(json, JobDetail.class);// 将JSON字符串转换为对象
jobDetail.setId(Long.parseLong(documentFields.getId()));// 使用SearchHit.getId设置文档ID
jobDetailArrayList.add(jobDetail);
}
// 将结果封装到Map结构中(带有分页信息)
long totalNum = searchResponse.getHits().getTotalHits().value;
Map<String, Object> resultMap = new HashMap<>();
resultMap.put("total", totalNum); // total -> 使用SearchHits.getTotalHits().value获取到所有的记录数
resultMap.put("content", jobDetailArrayList); content -> 当前分页中的数据
return resultMap;
}
public Map<String, Object> searchByScrollPage(String keywords, String scrollId, int pageSize) throws IOException {
SearchResponse searchResponse = null;
if(scrollId == null) {
// 构建SearchRequest检索请求 专门用来进行全文检索、关键字检索的API
SearchRequest searchRequest = new SearchRequest(JOB_IDX);
// 创建一个SearchSourceBuilder专门用于构建查询条件
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
// 使用QueryBuilders.multiMatchQuery构建一个查询条件(搜索title、jd),并配置到SearchSourceBuilder
MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery(keywords, "title", "jd");
searchSourceBuilder.query(multiMatchQueryBuilder);// 将查询条件设置到查询请求构建器中
HighlightBuilder highlightBuilder = new HighlightBuilder(); // 设置高亮
highlightBuilder.field("title");
highlightBuilder.field("jd");
highlightBuilder.preTags("<font color='red'>");
highlightBuilder.postTags("</font>");
searchSourceBuilder.highlighter(highlightBuilder); // 给请求设置高亮
searchSourceBuilder.size(pageSize); // 每页显示多少条
searchRequest.source(searchSourceBuilder); // 调用SearchRequest.source将查询条件设置到检索请求
searchRequest.scroll(TimeValue.timeValueMinutes(5)); // 设置scroll查询
// 执行RestHighLevelClient.search发起请求
searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
} else { // 第二次查询的时候,直接通过scroll id查询数据
SearchScrollRequest searchScrollRequest = new SearchScrollRequest(scrollId);
searchScrollRequest.scroll(TimeValue.timeValueMinutes(5));
// 使用RestHighLevelClient发送scroll请求
searchResponse = restHighLevelClient.scroll(searchScrollRequest, RequestOptions.DEFAULT);
}
SearchHit[] hitArray = searchResponse.getHits().getHits();
ArrayList<JobDetail> jobDetailArrayList = new ArrayList<>();
for (SearchHit documentFields : hitArray) { // 遍历结果,迭代ES响应的数据
String json = documentFields.getSourceAsString(); // 获取命中的结果
JobDetail jobDetail = JSONObject.parseObject(json, JobDetail.class); // 将JSON字符串转换为对象
jobDetail.setId(Long.parseLong(documentFields.getId())); // 使用SearchHit.getId设置文档ID
jobDetailArrayList.add(jobDetail);
// 设置高亮的一些文本到实体类中 封装了高亮
Map<String, HighlightField> highlightFieldMap = documentFields.getHighlightFields();
HighlightField titleHL = highlightFieldMap.get("title");
HighlightField jdHL = highlightFieldMap.get("jd");
if(titleHL != null) {
Text[] fragments = titleHL.getFragments(); // 获取指定字段的高亮片段
StringBuilder builder = new StringBuilder();
for(Text text : fragments) { // 将这些高亮片段拼接成一个完整的高亮字段
builder.append(text);
}
jobDetail.setTitle(builder.toString()); // 设置到实体类中
}
if(jdHL != null) {
Text[] fragments = jdHL.getFragments(); // 获取指定字段的高亮片段
StringBuilder builder = new StringBuilder();
for(Text text : fragments) {// 将这些高亮片段拼接成一个完整的高亮字段
builder.append(text);
}
jobDetail.setJd(builder.toString()); // 设置到实体类中
}
}
// 将结果封装到Map结构中,带有分页信息
long totalNum = searchResponse.getHits().getTotalHits().value;
Map<String, Object> hashMap = new HashMap<>();
hashMap.put("scroll_id", searchResponse.getScrollId());
hashMap.put("content", jobDetailArrayList); // content -> 当前分页中的数据
hashMap.put("total_num", totalNum); // total -> 使用SearchHits.getTotalHits().value获取到所有的记录数
return hashMap;
}
public void close() throws IOException {
restHighLevelClient.close();
}

京东商城搜索效果实现

ES索引库表结构分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
PUT product_db  // 创建索引库
{"mappings":{"properties":{"id":{"type":"long"},"name":{"type":"text","analyzer":"ik_max_word"},"keywords":{"type":"text","analyzer":"ik_max_word"},"subTitle":{"type":"text","analyzer":"ik_max_word"},"salecount":{"type":"long"},"putawayDate":{"type":"date"},"price":{"type":"double"},"promotionPrice":{"type":"keyword"},"originalPrice":{"type":"keyword"},"pic":{"type":"keyword"},"sale":{"type":"long"},"hasStock":{"type":"boolean"},"brandId":{"type":"long"},"brandName":{"type":"keyword"},"brandImg":{"type":"keyword"},"categoryId":{"type":"long"},"categoryName":{"type":"keyword"},"attrs":{"type":"nested","properties":{"attrId":{"type":"long"},"attrName":{"type":"keyword"},"attrValue":{"type":"keyword"}}}}}}

// 索引数据准备
PUT /product_db/_doc/1
{"id":"26","name":"小米 11 手机","keywords":"小米手机","subTitle":"AI智慧全面屏 6GB +64GB 亮黑色 全网通版 移动联通电信4G手机 双卡双待 双卡双待","price":"3999","promotionPrice":"2999","originalPrice":"5999","pic":"http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180615/xiaomi.jpg","sale":999,"hasStock":true,"salecount":999,"putawayDate":"2021-04-01","brandId":6,"brandName":"小米","brandImg":"http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20190129/1e34aef2a409119018a4c6258e39ecfb_222_222.png","categoryId":19,"categoryName":"手机通讯","attrs":[{"attrId":1,"attrName":"cpu","attrValue":"2核"},{"attrId":2,"attrName":"颜色","attrValue":"黑色"}]}
PUT /product_db/_doc/2
{"id":"27","name":"小米 10 手机","keywords":"小米手机","subTitle":"AI智慧全面屏 4GB +64GB 亮白色 全网通版 移动联通电信4G手机 双卡双待 双卡双待","price":"2999","promotionPrice":"1999","originalPrice":"3999","pic":"http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180615/xiaomi.jpg","sale":999,"hasStock":false,"salecount":99,"putawayDate":"2021-04-02","brandId":6,"brandName":"小米","brandImg":"http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20190129/1e34aef2a409119018a4c6258e39ecfb_222_222.png","categoryId":19,"categoryName":"手机通讯","attrs":[{"attrId":1,"attrName":"cpu","attrValue":"4核"},{"attrId":2,"attrName":"颜色","attrValue":"白色"}]}
PUT /product_db/_doc/3
{"id":"28","name":"小米 手机","keywords":"小米手机","subTitle":"AI智慧全面屏 4GB +64GB 亮蓝色 全网通版 移动联通电信4G手机 双卡双待 双卡双待","price":"2999","promotionPrice":"1999","originalPrice":"3999","pic":"http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180615/xiaomi.jpg","sale":999,"hasStock":true,"salecount":199,"putawayDate":"2021-04-03","brandId":6,"brandName":"小米","brandImg":"http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20190129/1e34aef2a409119018a4c6258e39ecfb_222_222.png","categoryId":19,"categoryName":"手机通讯","attrs":[{"attrId":1,"attrName":"cpu","attrValue":"2核"},{"attrId":2,"attrName":"颜色","attrValue":"蓝色"}]}
PUT /product_db/_doc/4
{"id":"29","name":"Apple iPhone 8 Plus 64GB 金色特别版 移动联通电信4G手机","keywords":"苹果手机","subTitle":"苹果手机 Apple产品年中狂欢节,好物尽享,美在智慧!速来 >> 勾选[保障服务][原厂保2年],获得AppleCare+全方位服务计划,原厂延保售后无忧。","price":"5999","promotionPrice":"4999","originalPrice":"7999","pic":"http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180615/5acc5248N6a5f81cd.jpg","sale":999,"hasStock":true,"salecount":1199,"putawayDate":"2021-04-04","brandId":51,"brandName":"苹果","brandImg":"http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180607/timg.jpg","categoryId":19,"categoryName":"手机通讯","attrs":[{"attrId":1,"attrName":"cpu","attrValue":"4核"},{"attrId":2,"attrName":"颜色","attrValue":"金色"}]}
PUT /product_db/_doc/5
{"id":"30","name":"HLA海澜之家简约动物印花短袖T恤","keywords":"海澜之家衣服","subTitle":"HLA海澜之家短袖T恤","price":"199","promotionPrice":"99","originalPrice":"299","pic":"http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180615/5ad83a4fN6ff67ecd.jpg!cc_350x449.jpg","sale":999,"hasStock":true,"salecount":19,"putawayDate":"2021-04-05","brandId":50,"brandName":"海澜之家","brandImg":"http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20190129/99d3279f1029d32b929343b09d3c72de_222_222.jpg","categoryId":8,"categoryName":"T恤","attrs":[{"attrId":3,"attrName":"尺寸","attrValue":"M"},{"attrId":4,"attrName":"颜色","attrValue":"黑色"}]}
PUT /product_db/_doc/6
{"id":"31","name":"HLA海澜之家蓝灰花纹圆领针织布短袖T恤","keywords":"海澜之家衣服","subTitle":"HLA海澜之家短袖T恤","price":"299","promotionPrice":"199","originalPrice":"299","pic":"http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180615/5ac98b64N70acd82f.jpg!cc_350x449.jpg","sale":999,"hasStock":true,"salecount":399,"putawayDate":"2021-04-06","brandId":50,"brandName":"海澜之家","brandImg":"http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20190129/99d3279f1029d32b929343b09d3c72de_222_222.jpg","categoryId":8,"categoryName":"T恤","attrs":[{"attrId":3,"attrName":"尺寸","attrValue":"X"},{"attrId":4,"attrName":"颜色","attrValue":"蓝灰"}]}
PUT /product_db/_doc/7
{"id":"32","name":"HLA海澜之家短袖T恤男基础款","keywords":"海澜之家衣服","subTitle":"HLA海澜之家短袖T恤","price":"269","promotionPrice":"169","originalPrice":"399","pic":"http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180615/5a51eb88Na4797877.jpg","sale":999,"hasStock":true,"salecount":399,"putawayDate":"2021-04-07","brandId":50,"brandName":"海澜之家","brandImg":"http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20190129/99d3279f1029d32b929343b09d3c72de_222_222.jpg","categoryId":8,"categoryName":"T恤","attrs":[{"attrId":3,"attrName":"尺寸","attrValue":"L"},{"attrId":4,"attrName":"颜色","attrValue":"蓝色"}]}
PUT /product_db/_doc/8
{"id":"33","name":"小米(MI)小米电视4A ","keywords":"小米电视机家用电器","subTitle":"小米(MI)小米电视4A 55英寸 L55M5-AZ/L55M5-AD 2GB+8GB HDR 4K超高清 人工智能网络液晶平板电视","price":"2269","promotionPrice":"2169","originalPrice":"2399","pic":"http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180615/5b02804dN66004d73.jpg","sale":999,"hasStock":true,"salecount":132,"putawayDate":"2021-04-09","brandId":6,"brandName":"小米","brandImg":"http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20190129/1e34aef2a409119018a4c6258e39ecfb_222_222.png","categoryId":35,"categoryName":"手机数码","attrs":[{"attrId":5,"attrName":"屏幕尺寸","attrValue":"52"},{"attrId":6,"attrName":"机身颜色","attrValue":"黑色"}]}
PUT /product_db/_doc/9
{"id":"34","name":"小米(MI)小米电视4A 65英寸","keywords":"小米电视机家用电器","subTitle":"小米(MI)小米电视4A 65英寸 L55M5-AZ/L55M5-AD 2GB+8GB HDR 4K超高清 人工智能网络液晶平板电视","price":"3269","promotionPrice":"3169","originalPrice":"3399","pic":"http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180615/5b028530N51eee7d4.jpg","sale":999,"hasStock":true,"salecount":999,"putawayDate":"2021-04-10","brandId":6,"brandName":"小米","brandImg":"http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20190129/1e34aef2a409119018a4c6258e39ecfb_222_222.png","categoryId":35,"categoryName":"手机数码","attrs":[{"attrId":5,"attrName":"屏幕尺寸","attrValue":"65"},{"attrId":6,"attrName":"机身颜色","attrValue":"金色"}]}
PUT /product_db/_doc/10
{"id":"35","name":"耐克NIKE 男子 休闲鞋 ROSHE RUN 运动鞋 511881-010黑色41码","keywords":"耐克运动鞋 鞋子","subTitle":"耐克NIKE 男子 休闲鞋 ROSHE RUN 运动鞋 511881-010黑色41码","price":"569","promotionPrice":"369","originalPrice":"899","pic":"http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180615/5b235bb9Nf606460b.jpg","sale":999,"hasStock":true,"salecount":399,"putawayDate":"2021-04-11","brandId":58,"brandName":"NIKE","brandImg":"http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180615/timg (51).jpg","categoryId":29,"categoryName":"男鞋","attrs":[{"attrId":7,"attrName":"尺码","attrValue":"42"},{"attrId":8,"attrName":"颜色","attrValue":"黑色"}]}
PUT /product_db/_doc/11
{"id":"36","name":"耐克NIKE 男子 气垫 休闲鞋 AIR MAX 90 ESSENTIAL 运动鞋 AJ1285-101白色41码","keywords":"耐克运动鞋 鞋子","subTitle":"AIR MAX 90 ESSENTIAL 运动鞋 AJ1285-101白色","price":"769","promotionPrice":"469","originalPrice":"999","pic":"http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180615/5b19403eN9f0b3cb8.jpg","sale":999,"hasStock":true,"salecount":499,"putawayDate":"2021-04-13","brandId":58,"brandName":"NIKE","brandImg":"http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180615/timg (51).jpg","categoryId":29,"categoryName":"男鞋","attrs":[{"attrId":7,"attrName":"尺码","attrValue":"44"},{"attrId":8,"attrName":"颜色","attrValue":"白色"}]}
PUT /product_db/_doc/12
{"id":"37","name":"(华为)HUAWEI MateBook X Pro 2019款 13.9英寸3K触控全面屏 轻薄笔记本","keywords":"轻薄笔记本华为 笔记本电脑","subTitle":"轻薄华为笔记本 电脑","price":"4769","promotionPrice":"4469","originalPrice":"4999","pic":"http://tuling-mall.oss-cn-shenzhen.aliyuncs.com/tulingmall/images/20200317/800_800_1555752016264mp.png","sale":999,"hasStock":true,"salecount":699,"putawayDate":"2021-04-14","brandId":3,"brandName":"华为","brandImg":"http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20190129/17f2dd9756d9d333bee8e60ce8c03e4c_222_222.jpg","categoryId":19,"categoryName":"手机通讯","attrs":[{"attrId":9,"attrName":"容量","attrValue":"16G"},{"attrId":10,"attrName":"网络","attrValue":"4G"}]}
PUT /product_db/_doc/13
{"id":"38","name":"华为nova6se 手机 绮境森林 全网通(8G+128G)","keywords":"轻薄笔记本华为 手机","subTitle":"华为nova6se 手机","price":"6769","promotionPrice":"6469","originalPrice":"6999","pic":"http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180607/5ac1bf58Ndefaac16.jpg","sale":999,"hasStock":true,"salecount":899,"putawayDate":"2021-04-15","brandId":3,"brandName":"华为","brandImg":"http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20190129/17f2dd9756d9d333bee8e60ce8c03e4c_222_222.jpg","categoryId":19,"categoryName":"手机通讯","attrs":[{"attrId":9,"attrName":"容量","attrValue":"64G"},{"attrId":10,"attrName":"网络","attrValue":"5G"}]}
PUT /product_db/_doc/14
{"id":"39","name":"iPhone7/6s/8钢化膜苹果8Plus全屏复盖抗蓝光防窥防偷看手机膜","keywords":"手机膜","subTitle":"iPhone7/6s/8钢化膜苹果8Plus全屏复盖抗蓝光防窥防偷看手机膜","price":"29","promotionPrice":"39","originalPrice":"49","pic":"http://tuling-mall.oss-cn-shenzhen.aliyuncs.com/tulingmall/images/20200311/6df99dab78bb2014.jpg","sale":999,"hasStock":true,"salecount":799,"putawayDate":"2021-04-16","brandId":51,"brandName":"苹果","brandImg":"http://tuling-mall.oss-cn-shenzhen.aliyuncs.com/tulingmall/images/20200311/2b84746650fc122d67749a876c453619.png","categoryId":30,"categoryName":"手机配件","attrs":[{"attrId":11,"attrName":"手机膜-材料","attrValue":"钢化"},{"attrId":12,"attrName":"手机膜-颜色","attrValue":"白色"}]}
PUT /product_db/_doc/15
{"id":"40","name":"七匹狼短袖T恤男纯棉舒适春夏修身运动休闲短袖三条装 圆领3条装","keywords":"七匹狼服装 衣服","subTitle":"七匹狼短袖T恤男纯棉舒适春夏修身运动休闲短袖三条装 圆领3条装","price":"129","promotionPrice":"139","originalPrice":"149","pic":"http://tuling-mall.oss-cn-shenzhen.aliyuncs.com/tulingmall/images/20200311/19e846e727dff337.jpg","sale":999,"hasStock":true,"salecount":199,"putawayDate":"2021-04-20","brandId":49,"brandName":"七匹狼","brandImg":"http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20190129/18d8bc3eb13533fab466d702a0d3fd1f40345bcd.jpg","categoryId":8,"categoryName":"T恤","attrs":[{"attrId":3,"attrName":"尺寸","attrValue":"M"},{"attrId":4,"attrName":"颜色","attrValue":"白色"}]}
PUT /product_db/_doc/16
{"id":"41","name":"华为P40 Pro手机","keywords":"华为手机","subTitle":"华为P40 Pro手机","price":"2129","promotionPrice":"2139","originalPrice":"2149","pic":"http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180607/5ac1bf58Ndefaac16.jpg","sale":999,"hasStock":true,"salecount":199,"putawayDate":"2021-05-03","brandId":3,"brandName":"华为","brandImg":"http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20190129/17f2dd9756d9d333bee8e60ce8c03e4c_222_222.jpg","categoryId":19,"categoryName":"手机通讯","attrs":[{"attrId":9,"attrName":"容量","attrValue":"128G"},{"attrId":10,"attrName":"网络","attrValue":"5G"}]}
PUT /product_db/_doc/17
{"id":"42","name":"朵唯智能手机 4G全网通 老人学生双卡双待手机","keywords":"朵唯手机","subTitle":"朵唯手机后置双摄,国产虎贲芯片!优化散热结构!浅薄机身!朵唯4月特惠!","price":"3129","promotionPrice":"3139","originalPrice":"3249","pic":"http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180615/xiaomi.jpg","sale":999,"hasStock":true,"salecount":1199,"putawayDate":"2021-06-01","brandId":59,"brandName":"朵唯","brandImg":"http://tuling-mall.oss-cn-shenzhen.aliyuncs.com/tulingmall/images/20200311/2b84746650fc122d67749a876c453619.png","categoryId":19,"categoryName":"手机通讯","attrs":[{"attrId":9,"attrName":"容量","attrValue":"32G"},{"attrId":10,"attrName":"网络","attrValue":"4G"}]}

检索DSL语句构建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
POST /product_db/_doc/_search
{
"from": 0,
"size": 8,
"query": {
"bool": {
"must": [{"match": {"name": {"query": "手机"}}}],
"filter": [
{"term": {"hasStock": {"value": true}}},
{"range": {"price": {"from": "1","to": "5000"}}}
]
}
},
"sort": [{"salecount": {"order": "asc"}}],
"aggregations": {
"brand_agg": {
"terms": {"field": "brandId","size": 50},
"aggregations": {
"brand_name_agg": {"terms": {"field": "brandName"}},
"brand_img_agg": {"terms": {"field": "brandImg"}}
}
},
"category_agg": {
"terms": {"field": "categoryId","size": 50,"min_doc_count": 1},
"aggregations": {
"category_name_agg": {"terms": {"field": "categoryName"}}
}
},
"attr_agg": {
"nested": {"path": "attrs"},
"aggregations": {
"attr_id_agg": {
"terms": {"field": "attrs.attrId"},
"aggregations": {
"attr_name_agg": {"terms": {"field": "attrs.attrName"}},
"attr_value_agg": {"terms": {"field": "attrs.attrValue"}}
}
}
}
}
},
"highlight": {
"pre_tags": ["<b style='color:red'>"],
"post_tags": ["</b>"],
"fields": {"name": {}}
}
}

Java代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
@ResponseBody
@RequestMapping(value = "/searchList")
public CommonResult<ESResponseResult> listPage(ESRequestParam param, HttpServletRequest request) {
// 根据传递来的页面的查询参数,去es中检索商品
ESResponseResult searchResult = tulingMallSearchService.search(param);
return CommonResult.success(searchResult);
}
@Override
public ESResponseResult search(ESRequestParam param) {
try {
// 构建检索对象-封装请求相关参数信息
SearchRequest searchRequest = startBuildRequestParam(param);
// 进行检索操作
SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
// 分析响应数据,封装成指定的格式
ESResponseResult responseResult = startBuildResponseResult(response, param);
return responseResult;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 封装请求参数信息,关键字查询、根据属性、分类、品牌、价格区间、是否有库存等进行过滤、分页、高亮、以及聚合统计品牌分类属性
*/
private SearchRequest startBuildRequestParam(ESRequestParam param) {
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
// 关键字查询、根据属性、分类、品牌、价格区间、是否有库存等进行过滤、分页、高亮、以及聚合统计品牌分类属性
BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
if (!StringUtils.isEmpty(param.getKeyword())) {
//单字段查询 boolQueryBuilder.must(QueryBuilders.matchQuery("name", param.getKeyword()));
//多字段查询
boolQueryBuilder.must(QueryBuilders.multiMatchQuery(param.getKeyword(),"name","keywords","subTitle"));
}
// 根据类目ID进行过滤
if (null != param.getCategoryId()) {
boolQueryBuilder.filter(QueryBuilders.termQuery("categoryId", param.getCategoryId()));
}
// 根据品牌ID进行过滤
if (null != param.getBrandId() && param.getBrandId().size() > 0) {
boolQueryBuilder.filter(QueryBuilders.termsQuery("brandId", param.getBrandId()));
}
// 根据属性进行相关过滤
if (param.getAttrs() != null && param.getAttrs().size() > 0) {
param.getAttrs().forEach(item -> {
//attrs=1_白色&2_4核
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
//attrs=1_64G
String[] s = item.split("_");
String attrId = s[0];
String[] attrValues = s[1].split(":");//这个属性检索用的值
boolQuery.must(QueryBuilders.termQuery("attrs.attrId", attrId));
boolQuery.must(QueryBuilders.termsQuery("attrs.attrValue", attrValues));

NestedQueryBuilder nestedQueryBuilder = QueryBuilders.nestedQuery("attrs", boolQuery, ScoreMode.None);
boolQueryBuilder.filter(nestedQueryBuilder);
});
}
// 是否有库存
if (null != param.getHasStock()) {
boolQueryBuilder.filter(QueryBuilders.termQuery("hasStock", param.getHasStock() == 1));
}
// 根据价格过滤
if (!StringUtils.isEmpty(param.getPrice())) {
// 价格的输入形式为:10-100(起始价格和最终价格)或-100(不指定起始价格)或10-(不限制最终价格)
RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("price");
String[] price = param.getPrice().split("_");
if (price.length == 2) {
rangeQueryBuilder.gte(price[0]).lte(price[1]);
} else if (price.length == 1) {
if (param.getPrice().startsWith("_")) {
rangeQueryBuilder.lte(price[1]);
}
if (param.getPrice().endsWith("_")) {
rangeQueryBuilder.gte(price[0]);
}
}
boolQueryBuilder.filter(rangeQueryBuilder);
}
// 封装所有查询条件
searchSourceBuilder.query(boolQueryBuilder);
//实现排序、高亮、分页操作,排序,页面传入的参数值形式 sort=price_asc/desc
if (!StringUtils.isEmpty(param.getSort())) {
String sort = param.getSort();
String[] sortFileds = sort.split("_");
System.out.println("sortFileds:"+sortFileds.length);
if(!StringUtils.isEmpty(sortFileds[0])){
SortOrder sortOrder = "asc".equalsIgnoreCase(sortFileds[1]) ? SortOrder.ASC : SortOrder.DESC;
searchSourceBuilder.sort(sortFileds[0], sortOrder);
}
}
// 分页查询
searchSourceBuilder.from((param.getPageNum() - 1) * SearchConstant.PAGE_SIZE);
searchSourceBuilder.size(SearchConstant.PAGE_SIZE);
// 高亮显示
if (!StringUtils.isEmpty(param.getKeyword())) {
HighlightBuilder highlightBuilder = new HighlightBuilder();
highlightBuilder.field("name");
highlightBuilder.preTags("<b style='color:red'>");
highlightBuilder.postTags("</b>");
searchSourceBuilder.highlighter(highlightBuilder);
}
// 对品牌、分类信息、属性信息进行聚合分析,按照品牌进行聚合
TermsAggregationBuilder brand_agg = AggregationBuilders.terms("brand_agg");
brand_agg.field("brandId").size(50);
// 品牌的子聚合-品牌名聚合
brand_agg.subAggregation(AggregationBuilders.terms("brand_name_agg").field("brandName").size(1));
// 品牌的子聚合-品牌图片聚合
brand_agg.subAggregation(AggregationBuilders.terms("brand_img_agg").field("brandImg").size(1));
searchSourceBuilder.aggregation(brand_agg);
// 按照分类信息进行聚合
TermsAggregationBuilder category_agg = AggregationBuilders.terms("category_agg");
category_agg.field("categoryId").size(50); category_agg.subAggregation(AggregationBuilders.terms("category_name_agg").field("categoryName").size(1));
searchSourceBuilder.aggregation(category_agg);
// 按照属性信息进行聚合
NestedAggregationBuilder attr_agg = AggregationBuilders.nested("attr_agg", "attrs");
// 按照属性ID进行聚合
TermsAggregationBuilder attr_id_agg = AggregationBuilders.terms("attr_id_agg").field("attrs.attrId");
attr_agg.subAggregation(attr_id_agg);
// 在每个属性ID下,按照属性名进行聚合
attr_id_agg.subAggregation(AggregationBuilders.terms("attr_name_agg").field("attrs.attrName").size(1));
// 在每个属性ID下,按照属性值进行聚合
attr_id_agg.subAggregation(AggregationBuilders.terms("attr_value_agg").field("attrs.attrValue").size(50));
searchSourceBuilder.aggregation(attr_agg);
System.out.println("构建的DSL语句 {}:"+ searchSourceBuilder.toString());
SearchRequest searchRequest = new SearchRequest(new String[]{SearchConstant.INDEX_NAME}, searchSourceBuilder);
return searchRequest;
}
/**
* 封装查询到的结果信息,关键字查询、根据属性、分类、品牌、价格区间、是否有库存等进行过滤、分页、高亮、以及聚合统计品牌分类属性
*/
private ESResponseResult startBuildResponseResult(SearchResponse response, ESRequestParam param) {
ESResponseResult result = new ESResponseResult();
// 获取查询到的商品信息
SearchHits hits = response.getHits();
List<EsProduct> esModels = new ArrayList<>();
// 遍历所有商品信息
if (hits.getHits() != null && hits.getHits().length > 0) {
for (SearchHit hit : hits.getHits()) {
String sourceAsString = hit.getSourceAsString();
EsProduct esModel = JSON.parseObject(sourceAsString, EsProduct.class);
// 判断是否按关键字检索,若是就显示高亮,否则不显示
if (!StringUtils.isEmpty(param.getKeyword())) {
// 拿到高亮信息显示标题
HighlightField name = hit.getHighlightFields().get("name");
// 判断name中是否含有查询的关键字(因为是多字段查询,因此可能不包含指定的关键字,假设不包含则显示原始name字段的信息)
String nameValue = name!=null ? name.getFragments()[0].string() : esModel.getName();
esModel.setName(nameValue);
}
esModels.add(esModel);
}
}
result.setProducts(esModels);
// 当前商品涉及到的所有品牌信息,小米手机和小米电脑都属于小米品牌,过滤重复品牌信息
Set<ESResponseResult.BrandVo> brandVos = new LinkedHashSet<>();
// 获取到品牌的聚合
ParsedLongTerms brandAgg = response.getAggregations().get("brand_agg");
for (Terms.Bucket bucket : brandAgg.getBuckets()) {
ESResponseResult.BrandVo brandVo = new ESResponseResult.BrandVo();
// 获取品牌的id
long brandId = bucket.getKeyAsNumber().longValue();
brandVo.setBrandId(brandId);
// 获取品牌的名字
ParsedStringTerms brandNameAgg = bucket.getAggregations().get("brand_name_agg");
String brandName = brandNameAgg.getBuckets().get(0).getKeyAsString();
brandVo.setBrandName(brandName);
// 获取品牌的LOGO
ParsedStringTerms brandImgAgg = bucket.getAggregations().get("brand_img_agg");
String brandImg = brandImgAgg.getBuckets().get(0).getKeyAsString();
brandVo.setBrandImg(brandImg);
System.out.println("brandId:"+brandId+"brandName:"+brandName+"brandImg");
brandVos.add(brandVo);
}
System.out.println("brandVos.size:"+brandVos.size());
result.setBrands(brandVos);
// 当前商品相关的所有类目信息,获取到分类的聚合
List<ESResponseResult.categoryVo> categoryVos = new ArrayList<>();
ParsedLongTerms categoryAgg = response.getAggregations().get("category_agg");
for (Terms.Bucket bucket : categoryAgg.getBuckets()) {
ESResponseResult.categoryVo categoryVo = new ESResponseResult.categoryVo();
// 获取分类id
String keyAsString = bucket.getKeyAsString();
categoryVo.setCategoryId(Long.parseLong(keyAsString));
// 获取分类名
ParsedStringTerms categoryNameAgg = bucket.getAggregations().get("category_name_agg");
String categoryName = categoryNameAgg.getBuckets().get(0).getKeyAsString();
categoryVo.setCategoryName(categoryName);
categoryVos.add(categoryVo);
}
result.setCategorys(categoryVos);
// 获取商品相关的所有属性信息
List<ESResponseResult.AttrVo> attrVos = new ArrayList<>();
// 获取属性信息的聚合
ParsedNested attrsAgg = response.getAggregations().get("attr_agg");
ParsedLongTerms attrIdAgg = attrsAgg.getAggregations().get("attr_id_agg");
for (Terms.Bucket bucket : attrIdAgg.getBuckets()) {
ESResponseResult.AttrVo attrVo = new ESResponseResult.AttrVo();
// 获取属性ID值
long attrId = bucket.getKeyAsNumber().longValue();
attrVo.setAttrId(attrId);
// 获取属性的名字
ParsedStringTerms attrNameAgg = bucket.getAggregations().get("attr_name_agg");
String attrName = attrNameAgg.getBuckets().get(0).getKeyAsString();
attrVo.setAttrName(attrName);
// 获取属性的值
ParsedStringTerms attrValueAgg = bucket.getAggregations().get("attr_value_agg");
List<String> attrValues = attrValueAgg.getBuckets().stream().map(item -> item.getKeyAsString()).collect(Collectors.toList());
attrVo.setAttrValue(attrValues);
attrVos.add(attrVo);
}
result.setAttrs(attrVos);

// 进行分页操作
result.setPageNum(param.getPageNum());
// 获取总记录数
long total = hits.getTotalHits().value;
result.setTotal(total);
// 计算总页码
int totalPages = (int) total % SearchConstant.PAGE_SIZE == 0 ?
(int) total / SearchConstant.PAGE_SIZE : ((int) total / SearchConstant.PAGE_SIZE + 1);
result.setTotalPages(totalPages);
List<Integer> pageNavs = new ArrayList<>();
for (int i = 1; i <= totalPages; i++) {
pageNavs.add(i);
}
result.setPageNavs(pageNavs);
return result;
}