Elasticsearch-37-Java API document 增删改查

前文都是讲的理论知识,用restful API来做的测试. 本文将使用java API来操作索引,document.

添加依赖

1
2
3
4
5
6
7
8
9
10
11
12
<!-- es依赖 -->
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>transport</artifactId>
<version>${elasticsearch.version}</version>
</dependency>

<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>${elasticsearch.version}</version>
</dependency>

yml配置

1
2
3
4
5
6
7
elasticsearch:
ip: 127.0.0.1
port: 9300
pool: 5
# 集群名称
cluster:
name: elasticsearch

配置client

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
@Configuration
@Slf4j
public class ElasticsearchConfig {

/**
* ip地址
*/
@Value("${elasticsearch.ip}")
private String hostName;

@Value("${elasticsearch.port}")
private int port;

@Value("${elasticsearch.pool}")
private int poolSize;

@Value("${elasticsearch.cluster.name}")
private String clusterName;

@Bean
public TransportClient init(){
TransportClient transportClient = null;
try {
// 配置
Settings settings = Settings.builder()
.put("cluster.name", clusterName)
// 集群嗅探机制,找到es集群
.put("client.transport.sniff", true)
// 增加线程池个数
.put("thread_pool.search.size", poolSize)
.build();

transportClient = new PreBuiltTransportClient(settings)
// 设置地址端口号
.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName(hostName), port));

} catch (Exception e){
log.error("elasticsearch TransportClient init error,{}", e);
}

return transportClient;
}
}

基础的配置已经完成了,接下来就是具体的方法

增删改查节点和索引

Util工具类
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
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
@Component
@Slf4j
public class ElasticsearchUtils {

@Autowired
private TransportClient transportClient;

private static TransportClient client;

@PostConstruct
public void init(){
client = this.transportClient;
}

/**
* 判断索引是否存在
* @param indexName 索引名称
* @return true/false
*/
public static boolean indexExist(String indexName){
IndicesExistsResponse indicesExistsResponse = client.admin()
.indices()
.exists(new IndicesExistsRequest(indexName))
.actionGet();

if (indicesExistsResponse.isExists()){
log.info("Index ['{}'] is exists", indexName);
} else {
log.info("Index ['{}'] is not exists", indexName);
}

return indicesExistsResponse.isExists();
}

/**
* 创建索引
* @param indexName 索引名称
* @return isAcknowledged
*/
public static boolean createIndex(String indexName){

if (!indexExist(indexName)){
log.info("Index is not exist");
}

CreateIndexResponse response = client.admin()
.indices()
.prepareCreate(indexName)
.execute()
.actionGet();

return response.isAcknowledged();

}

/**
* 删除索引
* @param indexName 索引名称
* @return isAcknowledged
*/
public static boolean deleteIndex(String indexName){

if (!indexExist(indexName)){
log.info("Index is not exist");
}

DeleteIndexResponse response = client.admin()
.indices()
.prepareDelete(indexName)
.execute()
.actionGet();

return response.isAcknowledged();
}

/**
* 创建一个document,需要手动指定id
* @param indexName 索引名称
* @param typeName 类型名称
* @param id id
* @param xContentBuilder 数据(fields)
* @return id
*/
public static String createDocument(String indexName, String typeName, String id, XContentBuilder xContentBuilder){

IndexResponse response = client
.prepareIndex(indexName, typeName, id)
.setSource(xContentBuilder)
.get();

log.info("add document response:{}", response.toString());

return response.getId();
}

/**
* 创建一个document,不需要手动指定id
* @param indexName 索引名称
* @param typeName 类型名称
* @param xContentBuilder 数据(fields)
* @return id
*/
public static String createDocumentWithNoId(String indexName, String typeName, XContentBuilder xContentBuilder){

IndexResponse response = client
.prepareIndex(indexName, typeName)
.setSource(xContentBuilder)
.get();

log.info("add document response:{}", response.toString());

return response.getId();
}

/**
* 更新document,partial update
* @param indexName 索引名称
* @param typeName 类型名称
* @param id id
* @param xContentBuilder 数据
* @return id
*/
public static String updateDocument(String indexName, String typeName, String id, XContentBuilder xContentBuilder){

UpdateResponse updateResponse = client
.prepareUpdate(indexName, typeName, id)
.setDoc(xContentBuilder)
.get();

log.info("update response:{}", updateResponse.toString());

return updateResponse.getId();
}

/**
* 删除document
* @param indexName 索引名称
* @param typeName 类型名称
* @param id id
* @return id
*/
public static String deleteDocument(String indexName, String typeName, String id){

DeleteResponse response = client
.prepareDelete(indexName, typeName, id)
.get();

log.info("delete response:{}", response.toString());

return response.getId();
}

/**
* 根据id获取document
* @param indexName 索引名称
* @param typeName 类型名称
* @param id id
* @return _source数据
*/
public static String getDocumentById(String indexName, String typeName, String id){

GetResponse response = client
.prepareGet(indexName, typeName, id)
.get();

log.info("get response");

return response.getSourceAsString();
}

/**
* 只做查询,没有排序
* @param indexes 索引
* @param types 类型
* @param matchMap 搜索条件
* @param fields 要显示的fields,不传返回全部
* @return 结果集
*/
public static List<Map<String,Object>> searchDocument(String indexes, String types, Map<String,String> matchMap, String fields){
return searchDocument(indexes, types, 0, 0, matchMap, false, null, fields, null, null, null);
}


/**
* 查询/精准匹配,可以排序
* @param indexes 索引
* @param types 类型
* @param matchMap 查询条件
* @param fields 要显示的fields,不传返回全部
* @param matchPhrase true 使用短语精准匹配
* @param sortField 排序field
* @param sortOrder 正序倒序(正序的话需要字段有正排索引)
* @return 结果集
*/
public static List<Map<String,Object>> searchDocument(String indexes, String types, Map<String,String> matchMap, String fields, boolean matchPhrase, String sortField, SortOrder sortOrder){
return searchDocument(indexes, types, 0, 0, matchMap, matchPhrase, null, fields, sortField, sortOrder, null);
}

/**
* 查询/精准匹配,可以排序,高亮,文档大小限制
* @param indexes 索引
* @param types 类型
* @param matchMap 查询条件
* @param fields 要显示的fields,不传返回全部
* @param matchPhrase true 使用短语精准匹配
* @param sortField 排序field
* @param sortOrder 正序倒序(正序的话需要字段有正排索引)
* @param highlightField 高亮字段
* @param size 文档大小限制
* @return 结果集
*/
public static List<Map<String,Object>> searchDocument(String indexes, String types, Map<String,String> matchMap, String fields, boolean matchPhrase, String sortField,
SortOrder sortOrder, String highlightField, Integer size){
return searchDocument(indexes, types, 0, 0, matchMap, matchPhrase, highlightField, fields, sortField, sortOrder, size);
}

/**
* 搜索document
* @param indexes 索引名
* @param types 类型
* @param startTime 开始时间
* @param endTime 结束时间
* @param matchMap 查询条件(filed:value)
* @param matchPhrase true 使用短语精准匹配
* @param highlightField 高亮显示的field
* @param fields 要显示的fields,不传返回全部
* @param sortField 排序field
* @param sortOrder 正序倒序(正序的话需要字段有正排索引)
* @param size 文档大小限制
* @return 结果集
*/
public static List<Map<String, Object>> searchDocument(String indexes, String types, long startTime, long endTime, Map<String,String> matchMap, boolean matchPhrase,
String highlightField, String fields, String sortField, SortOrder sortOrder, Integer size){
if (StringUtils.isEmpty(indexes)){
return null;
}

// 构建查询的request body
SearchRequestBuilder searchRequestBuilder = client.prepareSearch(indexes.split(","));

// 拆分type
if (StringUtils.isNotEmpty(types)){
searchRequestBuilder.setTypes(types.split(","));
}

// 组合查询 bool
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

// 组装查询条件
boolQueryBuilder = boolQuery(boolQueryBuilder, startTime, endTime, matchMap, matchPhrase);

// 设置高亮字段
searchRequestBuilder = setHighlightField(searchRequestBuilder, highlightField);

// 搜索条件加到request中
searchRequestBuilder.setQuery(boolQueryBuilder);

// 定制返回的fields
if (StringUtils.isNotEmpty(fields)){
searchRequestBuilder.setFetchSource(fields.split(","), null);
}

searchRequestBuilder.setFetchSource(true);

// 设置排序
if (StringUtils.isNotEmpty(sortField)){
searchRequestBuilder.addSort(sortField, sortOrder);
}

// 设置文档大小限制
if (size != null && size > 0){
searchRequestBuilder.setSize(size);
}

// 把请求体打印出来
log.info("查询请求体:{}", searchRequestBuilder);

// 发送请求,执行查询
SearchResponse response = searchRequestBuilder
.execute()
.actionGet();

long totalHits = response.getHits().totalHits();
long length = response.getHits().getHits().length;

log.info("共查询到[{}]条数据,处理数据条数[{}]", totalHits, length);

if (response.status().getStatus() == 200){
return setSearchResponse(response, highlightField);
}

return null;
}

/**
* 分页查询
* @param indexes 索引
* @param types 类型
* @param pageNum 页码
* @param pageSize 每页显示数量
* @param startTime 开始时间
* @param endTime 结束时间
* @param fields 要显示的字段
* @param sortField 排序字段
* @param sortOrder 正序倒序(正序需要排序的字段有正排索引)
* @param matchPhrase true 精准匹配
* @param highlightField 高亮子弹
* @param matchMap 查询条件
* @return PageVO
*/
public static PageVO searchDocumentPage(String indexes, String types, int pageNum, int pageSize, long startTime, long endTime, String fields, String sortField,
SortOrder sortOrder, boolean matchPhrase, String highlightField, Map<String,String> matchMap){
if (StringUtils.isEmpty(indexes)){
return null;
}

SearchRequestBuilder searchRequestBuilder = client.prepareSearch(indexes.split(","));

if (StringUtils.isNotEmpty(types)){
searchRequestBuilder.setTypes(types.split(","));
}
searchRequestBuilder.setSearchType(SearchType.QUERY_THEN_FETCH);

// 设置需要显示的字段
if (StringUtils.isNotEmpty(fields)){
searchRequestBuilder.setFetchSource(fields.split(","), null);
}

// 设置排序字段
if (StringUtils.isNotEmpty(sortField)){
searchRequestBuilder.addSort(sortField, sortOrder);
}

// 组合查询 bool
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

// 组装查询条件
boolQueryBuilder = boolQuery(boolQueryBuilder, startTime, endTime, matchMap, matchPhrase);

// 设置高亮字段
searchRequestBuilder = setHighlightField(searchRequestBuilder, highlightField);

// 搜索条件加到request中
searchRequestBuilder.setQuery(boolQueryBuilder);
searchRequestBuilder.setQuery(QueryBuilders.matchAllQuery());

// 设置分页
searchRequestBuilder.setFrom(pageNum).setSize(pageSize);

// 设置按照匹配度排序
searchRequestBuilder.setExplain(true);

// 打印请求体
log.info("请求体:{}", searchRequestBuilder);

// 发送请求,执行查询
SearchResponse response = searchRequestBuilder
.execute()
.actionGet();

long totalHits = response.getHits().totalHits();
long length = response.getHits().getHits().length;

log.info("共查询到[{}]条数据,处理数据条数[{}]", totalHits, length);

if (response.status().getStatus() == 200){
// 解析查询对象
List<Map<String,Object>> rList = setSearchResponse(response, highlightField);

return new PageVO(pageNum, pageSize, (int) totalHits, rList);
}

return null;
}

/**
* 高亮结果集 特殊处理
* @param searchResponse 查询返回结果
* @param highlightField 高亮字段
* @return 结果
*/
public static List<Map<String,Object>> setSearchResponse(SearchResponse searchResponse, String highlightField){
List<Map<String,Object>> sourceList = new ArrayList<>();
StringBuilder stringBuilder = new StringBuilder();

// 循环查询结果
for (SearchHit searchHitFields : searchResponse.getHits().getHits()) {
// 把id放到_source里面去
searchHitFields.getSource().put("id", searchHitFields.getId());

// 有高亮字段的话做处理
if (StringUtils.isNotEmpty(highlightField)){
log.info("遍历高亮结果集,覆盖正常结果集...{}", searchHitFields.getSource());

Text[] texts = searchHitFields.getHighlightFields().get(highlightField).getFragments();

if (texts != null){
for (Text text : texts) {
stringBuilder.append(text.toString());
}
// 遍历高亮结果集,覆盖正常结果集
searchHitFields.getSource().put(highlightField, stringBuilder.toString());
}
}

sourceList.add(searchHitFields.getSource());
}

return sourceList;
}

/**
* 封装
* @param boolQueryBuilder boolQueryBuilder
* @param startTime 开始时间
* @param endTime 结束时间
* @param matchMap 查询条件
* @param matchPhrase true 使用精准匹配
* @return boolQueryBuilder
*/
public static BoolQueryBuilder boolQuery(BoolQueryBuilder boolQueryBuilder, long startTime, long endTime, Map<String, String> matchMap, boolean matchPhrase){
// TODO 不清楚是做什么
if (startTime > 0 && endTime > 0){
boolQueryBuilder.must(QueryBuilders.rangeQuery("processTime")
.format("epoch_millis")
.from(startTime)
.to(endTime)
.includeLower(true)
.includeUpper(true)
);
}

// 搜索条件
if (!matchMap.isEmpty()){
for (Map.Entry<String,String> entry : matchMap.entrySet()) {
if (StringUtils.isNoneBlank(entry.getKey(),entry.getValue())){
if (matchPhrase == Boolean.TRUE){
// 精准匹配
boolQueryBuilder.must(QueryBuilders.matchPhraseQuery(entry.getKey(), entry.getValue()));
} else {
boolQueryBuilder.must(QueryBuilders.matchQuery(entry.getKey(), entry.getValue()));
}
}
}
}

return boolQueryBuilder;
}

/**
* 封装设置高亮字段
* @param searchRequestBuilder searchRequestBuilder
* @param highlightField 高亮字段
* @return searchRequestBuilder
*/
public static SearchRequestBuilder setHighlightField(SearchRequestBuilder searchRequestBuilder, String highlightField){
// 高亮字段
if (StringUtils.isNotEmpty(highlightField)){
HighlightBuilder highlightBuilder = new HighlightBuilder();
// 设置前缀
// highlightBuilder.preTags("<span style='color:red'>");
// 设置后缀
// highlightBuilder.postTags("</span>");
// 设置高亮字段
highlightBuilder.field(highlightField);
searchRequestBuilder.highlighter(highlightBuilder);
}

return searchRequestBuilder;
}
}
分页model
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
/**
* @author 周泽
* @date Create in 10:38 2018/11/29
* @Description 分页结果集
*/
@Getter
@Setter
public class PageVO {
/**
* 当前页码
*/
private Integer pageNum;

/**
* 一页显示数量
*/
private Integer pageSize;

/**
* 总数量
*/
private Integer total;

/**
* 结果集合
*/
private List<Map<String,Object>> rList;

/**
* 共有多少页
*/
private Integer pageCount;

/**
* 页码列表的开始索引(包含)
*/
private Integer beginPageIndex;

/**
* 码列表的结束索引(包含)
*/
private Integer endPageIndex;

/**
* 只接受前4个必要的属性,会自动的计算出其他3个属性的值
* @param pageNum 当前页码
* @param pageSize 每页显示条数
* @param total 总条数
* @param rList 结果集合
*/
public PageVO(int pageNum, int pageSize, int total, List<Map<String, Object>> rList) {
this.pageNum = pageNum;
this.pageSize = pageSize;
this.total = total;
this.rList = rList;

// 计算总页码
pageCount = (total + pageSize - 1) / pageSize;

// 计算 beginPageIndex 和 endPageIndex
// >> 总页数不多于10页,则全部显示
if (pageCount <= 10) {
beginPageIndex = 1;
endPageIndex = pageCount;
} else {
// >> 总页数多于10页,则显示当前页附近的共10个页码
// 当前页附近的共10个页码(前4个 + 当前页 + 后5个)
beginPageIndex = pageNum - 4;
endPageIndex = pageNum + 5;

// 当前面的页码不足4个时,则显示前10个页码
if (beginPageIndex < 1) {
beginPageIndex = 1;
endPageIndex = 10;
}

// 当后面的页码不足5个时,则显示后10个页码
if (endPageIndex > pageCount) {
endPageIndex = pageCount;
beginPageIndex = pageCount - 10 + 1;
}
}
}

}

单元测试代码在源码里.
源码地址