【SpringBoot】⑥SpringBoot与检索

实际2020-05-12 15:28:39
此篇文章使用版本:2.2.2.RELEASE
源码
Elasticsearch是Docker安装的7.4.0版本,并安装了ik分词器
关于Elasticsearch可以查看
1. Elasticsearch没看文档之前,整理的一些知识
2. Elasticsearch中文文档,内容不全
3. Elasticsearch 7.4.0官方文档操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 查看所有节点(* 代表是主节点)
GET /_cat/nodes

# 查看主节点
GET /_cat/master

# 查看es健康状况(green表示健康值正常)
GET /_cat/health

# 查看所有索引(等价于mysql数据库的show databases;)
GET /_cat/indices?v

# 删除索引item
DELETE item

# 查看索引的结构,映射
GET item

# 查看索引的全部文档
GET item/_search
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
2021-02-26 17:39:46 测试分词器


# 默认分词器
GET _analyze
{
"text":"我是中国人"
}
···
{
"tokens" : [
{
"token" : "我",
"start_offset" : 0,
"end_offset" : 1,
"type" : "<IDEOGRAPHIC>",
"position" : 0
},
{
"token" : "是",
"start_offset" : 1,
"end_offset" : 2,
"type" : "<IDEOGRAPHIC>",
"position" : 1
},
{
"token" : "中",
"start_offset" : 2,
"end_offset" : 3,
"type" : "<IDEOGRAPHIC>",
"position" : 2
},
{
"token" : "国",
"start_offset" : 3,
"end_offset" : 4,
"type" : "<IDEOGRAPHIC>",
"position" : 3
},
{
"token" : "人",
"start_offset" : 4,
"end_offset" : 5,
"type" : "<IDEOGRAPHIC>",
"position" : 4
}
]
}
···


# ik_smart
GET _analyze
{
"analyzer": "ik_smart",
"text":"我是中国人"
}

···
{
"tokens" : [
{
"token" : "我",
"start_offset" : 0,
"end_offset" : 1,
"type" : "CN_CHAR",
"position" : 0
},
{
"token" : "是",
"start_offset" : 1,
"end_offset" : 2,
"type" : "CN_CHAR",
"position" : 1
},
{
"token" : "中国人",
"start_offset" : 2,
"end_offset" : 5,
"type" : "CN_WORD",
"position" : 2
}
]
}
···


# ik_max_word
GET _analyze
{
"analyzer": "ik_max_word",
"text":"我是中国人"
}

···
{
"tokens" : [
{
"token" : "我",
"start_offset" : 0,
"end_offset" : 1,
"type" : "CN_CHAR",
"position" : 0
},
{
"token" : "是",
"start_offset" : 1,
"end_offset" : 2,
"type" : "CN_CHAR",
"position" : 1
},
{
"token" : "中国人",
"start_offset" : 2,
"end_offset" : 5,
"type" : "CN_WORD",
"position" : 2
},
{
"token" : "中国",
"start_offset" : 2,
"end_offset" : 4,
"type" : "CN_WORD",
"position" : 3
},
{
"token" : "国人",
"start_offset" : 3,
"end_offset" : 5,
"type" : "CN_WORD",
"position" : 4
}
]
}
···

1)、SpringBoot2.2.2整合Elasticsearch7.4.0
在这里插入图片描述

2)、JPA的方法命名规范
参考文档spring-data-jpa

3)、IndexTest.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
229
230
231
232
233
234
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Springboot03ElasticsearchApplication.class)
public class IndexTest {

@Autowired
private ElasticsearchTemplate elasticsearchTemplate;

@Autowired
private ItemRepository itemRepository;

@Test
public void createIndex(){
// 创建索引,会根据Item类的@Document注解信息来创建,也会自动创建映射
elasticsearchTemplate.createIndex(Item.class);
// 配置映射,会根据Item类中的id、Field等字段来自动完成映射,修改映射
//elasticsearchTemplate.putMapping(Item.class);
}

@Test
public void deleteIndex() {
elasticsearchTemplate.deleteIndex("item");
}

@Test
public void saveObject() {
// 保存的时候,也会自动创建索引,映射
Item item = new Item(1L, "小米手机7", " 手机", "小米", 3499.00, "http://image.panfeng.com/13123.jpg");
itemRepository.save(item);
}

@Test
public void saveList() {
List<Item> list = new ArrayList<>();
list.add(new Item(2L, "坚果手机R1", " 手机", "锤子", 3699.00, "http://image.panfeng.com/123.jpg"));
list.add(new Item(3L, "华为META10", " 手机", "华为", 4499.00, "http://image.panfeng.com/3.jpg"));
// 接收对象集合,实现批量新增
itemRepository.saveAll(list);
}

@Test
public void testQuery(){
Optional<Item> item = itemRepository.findById(1L);
System.out.println(item.get());
// Item(id=1, title=小米手机7, category= 手机, brand=小米, price=3499.0, images=http://image.panfeng.com/13123.jpg)
}

@Test
public void testFind(){
// 查询全部,并按照价格降序排序
Iterable<Item> items = itemRepository.findAll(Sort.by(Sort.Direction.DESC, "price"));
items.forEach(System.out::println);
/*
Item(id=3, title=华为META10, category= 手机, brand=华为, price=4499.0, images=http://image.panfeng.com/3.jpg)
Item(id=2, title=坚果手机R1, category= 手机, brand=锤子, price=3699.0, images=http://image.panfeng.com/123.jpg)
Item(id=1, title=小米手机7, category= 手机, brand=小米, price=3499.0, images=http://image.panfeng.com/13123.jpg)
*/
}

@Test
public void indexList() {
List<Item> list = new ArrayList<>();
list.add(new Item(1L, "小米手机7", "手机", "小米", 1199.00, "http://image.panfeng.com/13123.jpg"));
list.add(new Item(2L, "坚果手机R1", "手机", "锤子", 2299.00, "http://image.panfeng.com/13123.jpg"));
list.add(new Item(3L, "华为META10", "手机", "华为", 3399.00, "http://image.panfeng.com/13123.jpg"));
list.add(new Item(4L, "小米Mix2S", "手机", "小米", 4499.00, "http://image.panfeng.com/13123.jpg"));
list.add(new Item(5L, "荣耀V10", "手机", "华为", 5599.00, "http://image.panfeng.com/13123.jpg"));
// 接收对象集合,实现批量新增。同_id的文档会进行覆盖
itemRepository.saveAll(list);
}

@Test
public void queryByPriceBetween(){
List<Item> list = itemRepository.findByPriceBetween(0, 15000);
list.forEach(System.out::println);
/*
Item(id=3, title=华为META10, category=手机, brand=华为, price=3399.0, images=http://image.panfeng.com/13123.jpg)
Item(id=5, title=荣耀V10, category=手机, brand=华为, price=5599.0, images=http://image.panfeng.com/13123.jpg)
Item(id=4, title=小米Mix2S, category=手机, brand=小米, price=4499.0, images=http://image.panfeng.com/13123.jpg)
Item(id=2, title=坚果手机R1, category=手机, brand=锤子, price=2299.0, images=http://image.panfeng.com/13123.jpg)
Item(id=1, title=小米手机7, category=手机, brand=小米, price=1199.0, images=http://image.panfeng.com/13123.jpg)
*/
}

@Test
public void testQuery2(){
// 词条查询
MatchQueryBuilder queryBuilder = QueryBuilders.matchQuery("title", "小米");
// 执行查询
Iterable<Item> items = this.itemRepository.search(queryBuilder);

items.forEach(System.out::println);
/*
Item(id=1, title=小米手机7, category=手机, brand=小米, price=1199.0, images=http://image.panfeng.com/13123.jpg)
Item(id=4, title=小米Mix2S, category=手机, brand=小米, price=4499.0, images=http://image.panfeng.com/13123.jpg)
*/
}

@Test
public void testNativeQuery(){
// 构建查询条件
NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
// 添加基本的分词查询
queryBuilder.withQuery(QueryBuilders.matchQuery("title", "小米"));
// 执行搜索,获取结果
Page<Item> items = this.itemRepository.search(queryBuilder.build());
System.out.println("总条数:"+items.getTotalElements());// 总条数:2
System.out.println("总页数"+items.getTotalPages());// 总页数1
items.forEach(System.out::println);
/*
Item(id=1, title=小米手机7, category=手机, brand=小米, price=1199.0, images=http://image.panfeng.com/13123.jpg)
Item(id=4, title=小米Mix2S, category=手机, brand=小米, price=4499.0, images=http://image.panfeng.com/13123.jpg)
*/
}

@Test
public void testNativeQuery2(){
// 构建查询条件
NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
// 添加基本的分词查询
queryBuilder.withQuery(QueryBuilders.termQuery("category", "手机"));

// 初始化分页参数
int page = 0;
int size = 3;
// 设置分页参数
queryBuilder.withPageable(PageRequest.of(page, size));

// 执行搜索,获取结果
Page<Item> items = this.itemRepository.search(queryBuilder.build());
System.out.println("总条数:"+items.getTotalElements());// 总条数:5
System.out.println("总页数:"+items.getTotalPages());// 总页数:2
System.out.println("每页大小:"+items.getSize());// 每页大小:3
System.out.println("当前页:"+items.getNumber());// 当前页:0
items.forEach(System.out::println);
/*
Item(id=3, title=华为META10, category=手机, brand=华为, price=3399.0, images=http://image.panfeng.com/13123.jpg)
Item(id=5, title=荣耀V10, category=手机, brand=华为, price=5599.0, images=http://image.panfeng.com/13123.jpg)
Item(id=4, title=小米Mix2S, category=手机, brand=小米, price=4499.0, images=http://image.panfeng.com/13123.jpg)
*/
}

@Test
public void testSort(){
// 构建查询条件
NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
// 添加基本的分词查询
queryBuilder.withQuery(QueryBuilders.termQuery("category", "手机"));

// 排序
queryBuilder.withSort(SortBuilders.fieldSort("price").order(SortOrder.DESC));

// 执行搜索,获取结果
Page<Item> items = this.itemRepository.search(queryBuilder.build());
System.out.println("总条数:"+items.getTotalElements());// 总条数:5
items.forEach(System.out::println);
/*
Item(id=5, title=荣耀V10, category=手机, brand=华为, price=5599.0, images=http://image.panfeng.com/13123.jpg)
Item(id=4, title=小米Mix2S, category=手机, brand=小米, price=4499.0, images=http://image.panfeng.com/13123.jpg)
Item(id=3, title=华为META10, category=手机, brand=华为, price=3399.0, images=http://image.panfeng.com/13123.jpg)
Item(id=2, title=坚果手机R1, category=手机, brand=锤子, price=2299.0, images=http://image.panfeng.com/13123.jpg)
Item(id=1, title=小米手机7, category=手机, brand=小米, price=1199.0, images=http://image.panfeng.com/13123.jpg)
*/
}

@Test
public void testAgg(){
NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
// 不查询任何结果
queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{""}, null));
// 1、添加一个新的聚合,聚合类型为terms(分组),聚合名称为brands,聚合字段为brand
queryBuilder.addAggregation(
AggregationBuilders.terms("brands").field("brand"));
// 2、查询,需要把结果强转为AggregatedPage类型
AggregatedPage<Item> aggPage = (AggregatedPage<Item>) this.itemRepository.search(queryBuilder.build());
// 3、解析
// 3.1、从结果中取出名为brands的那个聚合,
// 因为是利用String类型字段来进行的term聚合,所以结果要强转为StringTerm类型
StringTerms agg = (StringTerms) aggPage.getAggregation("brands");
// 3.2、获取桶
List<StringTerms.Bucket> buckets = agg.getBuckets();
// 3.3、遍历
for (StringTerms.Bucket bucket : buckets) {
// 3.4、获取桶中的key,即品牌名称 3.5、获取桶中的文档数量
System.out.println("品牌名称:"+bucket.getKeyAsString()+",文档数量:"+bucket.getDocCount());
}
/*
品牌名称:华为,文档数量:2
品牌名称:小米,文档数量:2
品牌名称:锤子,文档数量:1
*/

}

@Test
public void testSubAgg(){
NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
// 不查询任何结果
queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{""}, null));
// 1、添加一个新的聚合,聚合类型为terms,聚合名称为brands,聚合字段为brand
queryBuilder.addAggregation(
AggregationBuilders.terms("brands").field("brand")
.subAggregation(AggregationBuilders.avg("priceAvg").field("price")) // 在品牌聚合桶内进行嵌套聚合,求平均值
);
// 2、查询,需要把结果强转为AggregatedPage类型
AggregatedPage<Item> aggPage = (AggregatedPage<Item>) this.itemRepository.search(queryBuilder.build());
// 3、解析
// 3.1、从结果中取出名为brands的那个聚合,
// 因为是利用String类型字段来进行的term聚合,所以结果要强转为StringTerm类型
StringTerms agg = (StringTerms) aggPage.getAggregation("brands");
// 3.2、获取桶
List<StringTerms.Bucket> buckets = agg.getBuckets();
// 3.3、遍历
for (StringTerms.Bucket bucket : buckets) {
// 3.4、获取桶中的key,即品牌名称 3.5、获取桶中的文档数量
System.out.println(bucket.getKeyAsString() + ",共" + bucket.getDocCount() + "台");

// 3.6.获取子聚合结果:
InternalAvg avg = (InternalAvg) bucket.getAggregations().asMap().get("priceAvg");
System.out.println("平均售价:" + avg.getValue());
System.out.println();
}
/*
华为,共2台
平均售价:4499.0

小米,共2台
平均售价:2849.0

锤子,共1台
平均售价:2299.0
*/

}
}