Springboot集成ElasticSearch7.4

1.各系统版本

1
2
3
4
ElasticSearch:7.4.0
SpringBoot:2.2.0.RELEASE
spring-boot-starter-data-elasticsearch:2.2.0.RELEASE
SpringCloud:Hoxton.SR1

#####特记:
如果要使用spring-data-elasticsearch目前只能使用ElasticSearch:6.7.2以下版本,所以现在只能用spring-boot-starter-data-elasticsearch连接ES7.4.0版本。

springboot对应的springcloud也是有版本对应。要不然会报Error creating bean with name 'traceFilterRegistration' defined in class path resource的错误,而这个导致这个错误的原因是springbootspingclould版本不匹配导致,parent采用了一个版本,内部又重新采用了另一个版本。
下面是版本对应:
官方网址:https://start.spring.io/actuator/info

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
"spring-cloud": {
"Finchley.M2": "Spring Boot >=2.0.0.M3 and <2.0.0.M5",
"Finchley.M3": "Spring Boot >=2.0.0.M5 and <=2.0.0.M5",
"Finchley.M4": "Spring Boot >=2.0.0.M6 and <=2.0.0.M6",
"Finchley.M5": "Spring Boot >=2.0.0.M7 and <=2.0.0.M7",
"Finchley.M6": "Spring Boot >=2.0.0.RC1 and <=2.0.0.RC1",
"Finchley.M7": "Spring Boot >=2.0.0.RC2 and <=2.0.0.RC2",
"Finchley.M9": "Spring Boot >=2.0.0.RELEASE and <=2.0.0.RELEASE",
"Finchley.RC1": "Spring Boot >=2.0.1.RELEASE and <2.0.2.RELEASE",
"Finchley.RC2": "Spring Boot >=2.0.2.RELEASE and <2.0.3.RELEASE",
"Finchley.SR4": "Spring Boot >=2.0.3.RELEASE and <2.0.999.BUILD-SNAPSHOT",
"Finchley.BUILD-SNAPSHOT": "Spring Boot >=2.0.999.BUILD-SNAPSHOT and <2.1.0.M3",
"Greenwich.M1": "Spring Boot >=2.1.0.M3 and <2.1.0.RELEASE",
"Greenwich.SR4": "Spring Boot >=2.1.0.RELEASE and <2.1.12.BUILD-SNAPSHOT",
"Greenwich.BUILD-SNAPSHOT": "Spring Boot >=2.1.12.BUILD-SNAPSHOT and <2.2.0.M4",
"Hoxton.SR1": "Spring Boot >=2.2.0.M4 and <2.2.3.BUILD-SNAPSHOT",
"Hoxton.BUILD-SNAPSHOT": "Spring Boot >=2.2.3.BUILD-SNAPSHOT"
}

2.项目结构

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
├─swj-parent
│ ├─swj-common
│ ├─swj-common-db
│ ├─swj-eureka
│ ├─swj-service
│ │ ├─swj-service-crawler
│ │ ├─swj-service-goods
│ │ └─swj-service-search
│ ├─src
│ │ ├─main
│ │ │ ├─java
│ │ │ │ └─com
│ │ │ │ └─swj
│ │ │ │ └─search
│ │ │ │ ├─controller
│ │ │ │ ├─mapper
│ │ │ │ │ └─ESManagerMapper.java
│ │ │ │ └─service
│ │ │ │ │ └─impl
│ │ │ │ │ ├─ESManagerServiceImpl.java
│ │ │ │ │ └─SearchServiceImpl.java
│ │ │ │ │ ├─ESManagerService.java
│ │ │ │ │ └─SearchService.java
│ └─swj-service-api
│ │ ├─swj-crawler-api
│ │ └─swj-service-search-api
│ │ ├─src
│ │ │ ├─main
│ │ │ │ ├─java
│ │ │ │ │ └─com
│ │ │ │ │ └─swj
│ │ │ │ │ └─search
│ │ │ │ │ ├─fegin
│ │ │ │ │ └─model
│ │ │ │ │ └─SkuInfo.java
│ │ │ │ └─resources
│─pom.xml

3.具体文件(这里只给出关键文件)

3.1 parent pom文件

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
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.swj</groupId>
<artifactId>swj-parent</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>swj-eureka</module>
<module>swj-service</module>
<module>swj-common</module>
<module>swj-common-db</module>
<module>swj-service-api</module>
</modules>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.0.RELEASE</version>
</parent>

<properties>
<skipTests>true</skipTests>
</properties>

<!--依赖包-->
<dependencies>
<!--测试包-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>

3.2 SkuInfo.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
package com.swj.search.model;

import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;

import javax.persistence.Id;
import java.io.Serializable;
import java.util.Date;
import java.util.Map;

@Document(indexName = "skuinfo", type = "docs")
public class SkuInfo implements Serializable {
//商品id,同时也是商品编号
@Id
@Field(index = true, store = true, type = FieldType.Keyword)
private Long id;

//SKU名称
@Field(index = true, store = true, type = FieldType.Text, analyzer = "ik_smart")
private String name;

//商品价格,单位为:元
@Field(index = true, store = true, type = FieldType.Double)
private Long price;

//库存数量
@Field(index = true, store = true, type = FieldType.Integer)
private Integer num;

//商品图片
@Field(index = false, store = true, type = FieldType.Text)
private String image;

//商品状态,1-正常,2-下架,3-删除
@Field(index = true, store = true, type = FieldType.Keyword)
private String status;

//创建时间
private Date createTime;

//更新时间
private Date updateTime;

//是否默认
@Field(index = true, store = true, type = FieldType.Keyword)
private String isDefault;

//SPUID
@Field(index = true, store = true, type = FieldType.Long)
private Long spuId;

//类目ID
@Field(index = true, store = true, type = FieldType.Long)
private Long categoryId;

//类目名称
@Field(index = true, store = true, type = FieldType.Keyword)
private String categoryName;

//品牌名称
@Field(index = true, store = true, type = FieldType.Keyword)
private String brandName;

//规格
private String spec;

//规格参数
private Map<String, Object> specMap;

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Long getPrice() {
return price;
}

public void setPrice(Long price) {
this.price = price;
}

public Integer getNum() {
return num;
}

public void setNum(Integer num) {
this.num = num;
}

public String getImage() {
return image;
}

public void setImage(String image) {
this.image = image;
}

public String getStatus() {
return status;
}

public void setStatus(String status) {
this.status = status;
}

public Date getCreateTime() {
return createTime;
}

public void setCreateTime(Date createTime) {
this.createTime = createTime;
}

public Date getUpdateTime() {
return updateTime;
}

public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}

public String getIsDefault() {
return isDefault;
}

public void setIsDefault(String isDefault) {
this.isDefault = isDefault;
}

public Long getSpuId() {
return spuId;
}

public void setSpuId(Long spuId) {
this.spuId = spuId;
}

public Long getCategoryId() {
return categoryId;
}

public void setCategoryId(Long categoryId) {
this.categoryId = categoryId;
}

public String getCategoryName() {
return categoryName;
}

public void setCategoryName(String categoryName) {
this.categoryName = categoryName;
}

public String getBrandName() {
return brandName;
}

public void setBrandName(String brandName) {
this.brandName = brandName;
}

public String getSpec() {
return spec;
}

public void setSpec(String spec) {
this.spec = spec;
}

public Map<String, Object> getSpecMap() {
return specMap;
}

public void setSpecMap(Map<String, Object> specMap) {
this.specMap = specMap;
}
}

3.3 ESManagerMapper.java文件

1
2
3
4
5
6
7
package com.swj.search.mapper;

import com.swj.search.model.SkuInfo;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;

public interface ESManagerMapper extends ElasticsearchRepository<SkuInfo,Long> {
}

3.4 ESManagerServiceImpl.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
package com.swj.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.swj.crawler.fegin.SKUFegin;
import com.swj.crawler.model.TbSku;
import com.swj.search.mapper.ESManagerMapper;
import com.swj.search.model.SkuInfo;
import com.swj.search.service.ESManagerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;

@Service
public class ESManagerServiceImpl implements ESManagerService {

@Autowired private ElasticsearchTemplate elasticsearchTemplate;

@Autowired private SKUFegin skuFegin;

@Autowired private ESManagerMapper esManagerMapper;

// 创建索引库结构
@Override
public void createMappingAndIndex() {
// 创建索引
elasticsearchTemplate.createIndex(SkuInfo.class);
// 创建映射
elasticsearchTemplate.putMapping(SkuInfo.class);
}

@Override
public void importAll() {
// 查询sku集合
List<TbSku> tbSkus = skuFegin.selectAll();
String jsonTbSkus = JSON.toJSONString(tbSkus);
List<SkuInfo> skuInfos = JSON.parseArray(jsonTbSkus, SkuInfo.class);
for (SkuInfo skuInfo : skuInfos) {
Map spaceMap = JSON.parseObject(skuInfo.getSpec(), Map.class);
skuInfo.setSpecMap(spaceMap);
}
// 导入索引库
esManagerMapper.saveAll(skuInfos);
}

@Override
public void importDataBySpuId(String spuId) {}

@Override
public void delDataBySpuId(String spuId) {}
}

3.4 SearchServiceImpl.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
package com.swj.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.swj.search.model.SkuInfo;
import com.swj.search.service.SearchService;
import org.apache.commons.lang.StringUtils;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.SearchResultMapper;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class SearchServiceImpl implements SearchService {
@Autowired private ElasticsearchTemplate elasticsearchTemplate;

@Override
public Map search(Map<String, String> searchMap) {
Map<String, Object> resultMap = new HashMap<>();
if (searchMap != null) {
// 构建查询条件
NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();

// 按照关键字查询
if (StringUtils.isNotEmpty(searchMap.get("keywords"))) {
boolQuery.must(
QueryBuilders.matchQuery("name", searchMap.get("keywords")).operator(Operator.AND));
}

// 按照品牌进行过滤查询
if (StringUtils.isNotEmpty(searchMap.get("brand"))) {
boolQuery.filter(QueryBuilders.termQuery("brandName", searchMap.get("brand")));
}

// 范围查询
if (StringUtils.isNotEmpty(searchMap.get("price"))) {
String[] prices = searchMap.get("price").split("-");
if (prices.length == 2) {
boolQuery.filter(QueryBuilders.rangeQuery("price").lte(prices[0]));
}
boolQuery.filter(QueryBuilders.rangeQuery("price").gte(prices[1]));
}

nativeSearchQueryBuilder.withQuery(boolQuery);

// 开启查询
/** 第一个参数: 条件构建对象 第二个参数: 查询操作实体类 第三个参数: 查询结果操作对象 */
// 封装查询结果
AggregatedPage<SkuInfo> aggregatedPage =
elasticsearchTemplate.queryForPage(
nativeSearchQueryBuilder.build(),
SkuInfo.class,
new SearchResultMapper() {
@Override
public <T> AggregatedPage<T> mapResults(
SearchResponse searchResponse, Class<T> aClass, Pageable pageable) {

List list = new ArrayList();
// 获取查询结果集
SearchHits searchHits = searchResponse.getHits();
for (SearchHit searchHit : searchHits) {
if (searchHit != null) {
SkuInfo skuInfo = JSON.parseObject(searchHit.toString(), SkuInfo.class);
Map<String, HighlightField> highlightFields = searchHit.getHighlightFields();
if (highlightFields != null && highlightFields.size() > 0) {
// 替换数据
skuInfo.setName(highlightFields.get("name").getFragments()[0].toString());
}

list.add((T) skuInfo);
}
}

return new AggregatedPageImpl<T>(
list, pageable, searchHits.getTotalHits(), searchResponse.getAggregations());
}

@Nullable
@Override
public <T> T mapSearchHit(SearchHit searchHit, Class<T> aClass) {
return null;
}
});

resultMap.put("rows", aggregatedPage.getContent());
}

return null;
}
}