Spring Data ElasticSearch 快速起步
pom:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
1
2
3
4
2
3
4
pojo类:
@Document(indexName = "pojo", type = "docs")
public class POJO implements Serializable {
@Id
@Field(index = true, store = true, type = FieldType.Keyword)
private Long id;
//是否索引,是否存储,类型,分词器
@Field(index = true, store = true, type = FieldType.Text, analyzer = "ik_smart")
private String name;
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
mapper类:
public interface SearchMapper extends ElasticsearchRepository<POJO, Long> {}
1
配置文件:
spring:
data: elasticsearch:
cluster-name: elasticsearch
cluster-nodes: 192.168.200.128:9300
1
2
3
4
2
3
4
注入对象:
//简单的CURD
SearchMapper
//更底层
ElasticsearchTemplate
1
2
3
4
2
3
4
# 查询示例
@Service
public class EsSearchServiceImpl implements EsSearchService {
@Autowired
private ElasticsearchTemplate elasticsearchTemplate;
@Override
public Map search(Map<String, String> searchMap) {
Map searchResult = new HashMap();//搜索结果的对象
if(searchMap==null || searchMap.size()==0){
return searchResult;
}
//构建组合查询搜索条件对象BooleanQueryBuilder
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
//判断搜索条件是否为空
if(StringUtils.isNotEmpty(searchMap.get("keywords"))){
//模糊搜索
boolQueryBuilder.must(QueryBuilders.matchQuery("name",searchMap.get("keywords") ).operator(Operator.AND)); // must-and , should - or, mustnot - not
}
//精确搜索
if(StringUtils.isNotEmpty(searchMap.get("brand"))){
boolQueryBuilder.filter(QueryBuilders.termQuery("brandName", searchMap.get("brand")));
}
//范围搜索
if(StringUtils.isNotEmpty(searchMap.get("price"))){
String price = searchMap.get("price"); //价格区间的值格式如: 1000-3000
String[] split = price.split("-");
if(split.length==2){
String lowPrice = split[0];//获取价格区间最小价格
String highPrice = split[1];//获取价格区间最大价格
boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(lowPrice).lte(highPrice));
}
}
//构建顶级搜索条件对象
NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
//添加布尔查询对象
nativeSearchQueryBuilder.withQuery(boolQueryBuilder);
//根据品牌名称进行聚合
String brandGroup = "brandGroup";
TermsAggregationBuilder brandGroupBuilder = AggregationBuilders.terms(brandGroup).field("brandName");
nativeSearchQueryBuilder.addAggregation(brandGroupBuilder);
//根据价格等进行排序
if(StringUtils.isNotEmpty(searchMap.get("sortField")) && StringUtils.isNotEmpty(searchMap.get("sortRule"))){
String sortField = searchMap.get("sortField");
String sortRule = searchMap.get("sortRule");
if("DESC".equalsIgnoreCase(sortRule)){
nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort(sortField).order(SortOrder.DESC));
} else {
nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort(sortField).order(SortOrder.ASC));
}
}
//分页设置
int pageNum = 1;
int pageSize = 20;
if(StringUtils.isNotEmpty(searchMap.get("pageNum"))){
pageNum = Integer.valueOf(searchMap.get("pageNum"));
}
if(StringUtils.isNotEmpty(searchMap.get("pageSize"))){
pageSize = Integer.valueOf(searchMap.get("pageSize"));
}
//ES中分页查询pageNo第一页是0,所以要减1
nativeSearchQueryBuilder.withPageable(PageRequest.of(pageNum-1, pageSize));
//高亮设置
//设置高亮HTML标签
HighlightBuilder.Field highLightField = new HighlightBuilder.Field("name").preTags("<span style='color:red'>").postTags("</span>");
nativeSearchQueryBuilder.withHighlightFields(highLightField);
//执行搜索
AggregatedPage<SkuInfo> search = elasticsearchTemplate.queryForPage(nativeSearchQueryBuilder.build(), SkuInfo.class, new SearchResultMapper() {
@Override
public <T> AggregatedPage<T> mapResults(SearchResponse searchResponse, Class<T> aClass, Pageable pageable) {
long totalHits = searchResponse.getHits().getTotalHits(); //搜索命中的条数
SearchHit[] hits = searchResponse.getHits().getHits();//搜索命中的记录
List<T> skuList = new ArrayList<>();
if(totalHits>0){
for (SearchHit hit : hits) {
String skuInfoJson = hit.getSourceAsString(); //搜索命中的每一条记录的JSON字符串
SkuInfo skuInfo = JSON.parseObject(skuInfoJson, SkuInfo.class);
//需求11.2:取出高亮名称,设置到sku对象中
HighlightField field = hit.getHighlightFields().get("name");
//有高亮字段的时候才处理高亮的名称
if(field!=null){
Text[] fragments = field.getFragments();
if(fragments!=null){
String highlightName = fragments[0].toString();
skuInfo.setName(highlightName);
}
}
skuList.add((T)skuInfo);
}
}
//第一个参数:搜索结果集合,第二个参数:分页结果对象 , 第三发参数:搜索命中的条数,第四个参数:聚合结果对象
return new AggregatedPageImpl<>(skuList, pageable, totalHits, searchResponse.getAggregations());
}
});
//根据聚合分组名查找对应聚合结果集(品牌名称的去重结果集)
StringTerms brandTerms = (StringTerms)search.getAggregation(brandGroup);
List<StringTerms.Bucket> brandBuckets = brandTerms.getBuckets();
List<String> brandList = new ArrayList<>();//这里如果返回给前端,数据格式,将是['小米','华为','苹果']
if(brandBuckets!=null && brandBuckets.size()>0){
for (StringTerms.Bucket brandBucket : brandBuckets) {
String brandValue = brandBucket.getKeyAsString();
brandList.add(brandValue);
}
}
searchResult.put("rows", search.getContent());//搜索结果记录,如果没有做分页配置那么此处默认是10条数据
searchResult.put("total",search.getTotalElements());//搜索结果总条数
searchResult.put("totalPage", search.getTotalPages()); //搜索结果总页数,如果没有做分页配置那么此处默认是1页
searchResult.put("brandList", brandList);//品牌聚合结果集合,转换为JSON后格式如 ['小米','华为','苹果']
searchResult.put("pageNum", pageNum);//分页码
searchResult.put("pageSize", pageSize);//每页显示条数
return searchResult;
}
}
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
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
上次更新: 10/23/2024