ES 8.x 新版本中,Type 概念被棄用,是以新版 JavaAPI 也相應做出了改變,使用更加簡便。ES 官方從 7.15 起開始建議使用新的 JavaAPI
<!-- elasticsearch-java -->
<dependency>
<groupId>co.elastic.clients</groupId>
<artifactId>elasticsearch-java</artifactId>
<version>8.1.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.3</version>
</dependency>
<dependency>
<groupId>jakarta.json</groupId>
<artifactId>jakarta.json-api</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.13.3</version>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>2.6.7</version>
</dependency>
- NoClassDefFoundError 異常
java.lang.NoClassDefFoundError: jakarta/json/JsonException
如果出現這個異常,要加入 jakarta.json-api 包,版本 2.x ;
- LocalDateTime
如果實體類中有 LocalDateTime 字段,要加入 jackson-datatype-jsr310 ,可以實作序列化與反序列化;同時實體類中的 LocalDateTime 字段加如下注解:
@JsonDeserialize(using = LocalDateTimeDeserializer.class) // 反序列化
@JsonSerialize(using = LocalDateTimeSerializer.class) // 序列化
private LocalDateTime time;
2、實體類
2.1、文檔實體類 EsDocument.java
如果有自定義的構造器,一定不要忘記把無參構造器補上; @NoArgsConstructor ,否則無法反序列化。
package com.tuwer.pojo;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.Instant;
/**
* <p>ES文檔實體類</p>
* ----------------------
* 一定要有無參構造器;否則反序列化會失敗
* ----------------------
*
* @author 土味兒
* Date 2022/8/9
* @version 1.0
*/
@Data
@NoArgsConstructor
public class EsDocument {
/**
* 文檔id
*/
private String id;
/**
* 文檔類型
* 公共 0、私有 1...
*/
private Integer type;
/**
* 文檔标題
*/
private String title;
/**
* 文檔内容
*/
private String content;
/**
* 文檔屬主
* 公開文檔沒有屬主
*/
private String owner;
/**
* 文檔url
*/
private String url;
/**
* 時間(采集/更新)
*/
//@JsonDeserialize(using = LocalDateTimeDeserializer.class) // 反序列化
//@JsonSerialize(using = LocalDateTimeSerializer.class) // 序列化
//private LocalDateTime time;
private Long time;
public EsDocument(String id, Integer type, String title, String content, String owner, String url) {
this.id = id;
this.type = type;
this.title = title;
this.content = content;
this.owner = owner;
this.url = url;
//this.time = LocalDateTime.now();
//this.time = LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant().getEpochSecond();
// 目前時刻
this.time = Instant.now().getEpochSecond();
}
}
2.2、文檔VO對象類
package com.tuwer.pojo;
import lombok.Data;
/**
* <p>文檔視圖層對象</p>
* @author 土味兒
* Date 2022/9/17
* @version 1.0
*/
@Data
public class EsDocVo {
/**
* 文檔id
*/
private String id;
/**
* 文檔标題
*/
private String title;
/**
* 文檔内容
*/
private String content;
/**
* 文檔url
*/
private String url;
/**
* 時間
* 1年前、5個月前、3星期前、5天前、8小時前、47分鐘前、剛剛
*/
private String time;
}
2.3、分頁對象類 ESPage.java
package com.tuwer.pojo;
import lombok.Data;
import java.util.List;
/**
* @author 土味兒
* Date 2022/9/17
* @version 1.0
*/
@Data
public class EsPage {
private String keyword;
private Long total;
private Integer current = 1;
private Integer pageSize = 10;
private List<EsDocVo> records;
}
3、工具類 EsUtil.java
重點類
- 基礎操作:擷取用戶端(同步/異步)、擷取Transport、close();僅供内部調用
- 索引操作類:封裝在内部類 Index 中,crud 操作
- 文檔操作類:封裝在内部類 Doc 中,crud 操作,分頁、高亮…
package com.tuwer.util;
import co.elastic.clients.elasticsearch.ElasticsearchAsyncClient;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.FieldSort;
import co.elastic.clients.elasticsearch._types.SortOptions;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.query_dsl.MatchQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.MultiMatchQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
import co.elastic.clients.elasticsearch._types.query_dsl.TermQuery;
import co.elastic.clients.elasticsearch.core.*;
import co.elastic.clients.elasticsearch.core.ExistsRequest;
import co.elastic.clients.elasticsearch.core.bulk.*;
import co.elastic.clients.elasticsearch.core.search.Highlight;
import co.elastic.clients.elasticsearch.core.search.HighlightField;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.elasticsearch.indices.*;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import co.elastic.clients.transport.ElasticsearchTransport;
import co.elastic.clients.transport.rest_client.RestClientTransport;
import com.tuwer.pojo.EsDocVo;
import com.tuwer.pojo.EsDocument;
import com.tuwer.pojo.EsPage;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.io.IOException;
import java.time.Instant;
import java.util.*;
import java.util.stream.Collectors;
/**
* <p>ES操作工具類</p>
* -----------------------------
* 調用異步用戶端就是異步方法;預設異步
* 索引名稱/Id:一律轉為小寫
* 文檔Id:可以為大寫,無須轉換
* -----------------------------
*
* @author 土味兒
* Date 2022/8/9
* @version 1.0
*/
@Slf4j
@SuppressWarnings("all")
@Component
public class EsUtil {
public Index index = new Index();
public Doc doc = new Doc();
// ===================== 索引操作(封裝在Index内部類中) ============================
public class Index {
/**
* 建立索引(同步)
*
* @param indexName
* @return true:成功,false:失敗
*/
@SneakyThrows
public Boolean createSync(String indexName) {
// 索引名稱轉為小寫
String iName = indexName.toLowerCase(Locale.ROOT);
//String iName = indexName;
// 擷取【索引用戶端對象】
ElasticsearchIndicesClient indexClient = getEsClient().indices();
/**
* ===== 判斷目标索引是否存在(等價于下面的Lambda寫法)=====
* ---- 1、建構【存在請求對象】
* ExistsRequest existsRequest = new ExistsRequest.Builder().index(indexName).build();
* ---- 2、判斷目标索引是否存在
* boolean flag = indexClient.exists(existsRequest).value();
*/
boolean flag = indexClient.exists(req -> req.index(iName)).value();
//CreateIndexResponse createIndexResponse = null;
boolean result = false;
if (flag) {
// 目标索引已存在
//System.out.println("索引【" + indexName + "】已存在!");
log.info("索引【" + iName + "】已存在!");
} else {
// 不存在
// 擷取【建立索引請求對象】
//CreateIndexRequest createIndexRequest = new CreateIndexRequest.Builder().index(indexName).build();
// 建立索引,得到【建立索引響應對象】
//CreateIndexResponse createIndexResponse = indexClient.create(createIndexRequest);
//createIndexResponse = indexClient.create(req -> req.index(indexName));
result = indexClient.create(req -> req.index(iName)).acknowledged();
//System.out.println("建立索引響應對象:" + createIndexResponse);
if (result) {
log.info("索引【" + iName + "】建立成功!");
} else {
log.info("索引【" + iName + "】建立失敗!");
}
}
// 關閉transport
close();
return result;
}
/**
* 建立索引(異步)
*
* @param indexName
* @return
*/
@SneakyThrows
public Boolean create(String indexName) {
// 轉為小寫
String iName = indexName.toLowerCase(Locale.ROOT);
// 異步索引用戶端
ElasticsearchIndicesAsyncClient indexClient = getEsAsyncClient().indices();
// 查詢索引是否存在;get()方法阻塞
boolean isExist = indexClient.exists(
existsRequest -> existsRequest.index(iName)
).get().value();
// 建立索引
boolean result = false;
if (isExist) {
log.info("索引【" + iName + "】已存在!");
} else {
// 目前索引不存在,建立索引
result = indexClient.create(
createIndexRequest -> createIndexRequest.index(iName)
).get().acknowledged();
if (result) {
log.info("索引【" + iName + "】建立成功!");
} else {
log.info("索引【" + iName + "】建立失敗!");
}
}
return result;
}
/**
* 查詢索引(同步)
*
* @param indexName
* @return
*/
@SneakyThrows
public Map<String, IndexState> querySync(String indexName) {
// 轉為小寫
String iName = indexName.toLowerCase(Locale.ROOT);
// 擷取【索引用戶端對象】
ElasticsearchIndicesClient indexClient = getEsClient().indices();
// 查詢結果;得到【查詢索引響應對象】
GetIndexRequest getIndexRequest = new GetIndexRequest.Builder().index(iName).build();
//GetIndexRequest getIndexRequest = new GetIndexRequest.Builder().index("*").build();
GetIndexResponse getIndexResponse = indexClient.get(getIndexRequest);
//GetIndexResponse getIndexResponse = indexClient.get(req -> req.index(iName));
// 關閉transport
close();
return getIndexResponse.result();
}
/**
* 查詢索引(異步)
*
* @param indexName
* @return
*/
@SneakyThrows
public Map<String, IndexState> query(String indexName) {
// 轉為小寫
String iName = indexName.toLowerCase(Locale.ROOT);
//getEsAsyncClient().indices().get()
return getEsAsyncClient().indices().get(req -> req.index(iName)).get().result();
}
/**
* 查詢全部索引
*
* @return 索引名稱 Set 集合
*/
@SneakyThrows
public Set<String> all() {
GetIndexResponse getIndexResponse = getEsAsyncClient().indices().get(req -> req.index("*")).get();
return getIndexResponse.result().keySet();
}
/**
* 删除索引
*
* @param indexName
* @return
*/
@SneakyThrows
public Boolean del(String indexName) {
// 轉為小寫
String iName = indexName.toLowerCase(Locale.ROOT);
// 擷取【索引用戶端對象】
//ElasticsearchIndicesClient indexClient = getEsClient().indices();
// 【删除索引響應對象】
//DeleteIndexResponse deleteIndexResponse = getEsClient().indices().delete(req -> req.index(iName));
DeleteIndexResponse deleteIndexResponse = getEsAsyncClient().indices().delete(req -> req.index(iName)).get();
// 關閉transport
//close();
return deleteIndexResponse.acknowledged();
}
}
// ===================== 文檔異步操作(封裝在Doc内部類中) ============================
public class Doc {
/**
* 建立/更新文檔(異步)
* 存在:
*
* @param indexName 索引名稱
* @param documentId 文檔ID
* @param esDocument 文檔内容
* @return 不存在:created、存在:updated
*/
@SneakyThrows
public String createOrUpdate(
String indexName,
String docId,
EsDocument esDocument
) {
// 轉為小寫
String iName = indexName.toLowerCase(Locale.ROOT);
// 可建立/可更新
IndexRequest<EsDocument> indexRequest = new IndexRequest.Builder<EsDocument>()
.index(iName)
.id(docId)
.document(esDocument)
.build();
// 不存在:created、存在:updated
return getEsAsyncClient().index(indexRequest).get().result().jsonValue();
}
/**
* 批量建立/更新文檔(異步)
* 存在就更新,不存在就建立
*
* @param indexName 索引名稱
* @param userMap 文檔Map,格式:(文檔id : 文檔)
* @return 成功操作的數量
*/
@SneakyThrows
public Integer createOrUpdateBth(
String indexName,
Map<String, EsDocument> userMap
) {
// 轉為小寫
String iName = indexName.toLowerCase(Locale.ROOT);
// 批量操作對象集合
List<BulkOperation> bs = new ArrayList<>();
// 建構【批量操作對象】,并裝入list集合中
userMap.entrySet().stream().forEach(userEntry -> {
// 操作對象(可建立/可更新)
IndexOperation<EsDocument> idxOpe = new IndexOperation.Builder<EsDocument>()
// 文檔id
.id(userEntry.getKey())
// 文檔内容
.document(userEntry.getValue())
.build();
// 建構【批量操作對象】
BulkOperation opt = new BulkOperation.Builder().index(idxOpe).build();
// 裝入list集合
bs.add(opt);
});
// 建構【批理請求對象】
BulkRequest bulkRequest = new BulkRequest.Builder()
// 索引
.index(iName)
// 批量操作對象集合
.operations(bs)
.build();
// 批量操作
BulkResponse bulkResponse = getEsAsyncClient().bulk(bulkRequest).get();
int i = bulkResponse.items().size();
log.info("成功處理 {} 份文檔!", i);
return i;
}
/**
* 檢測文檔是否存在
*
* @param indexName
* @param documentId
* @return
*/
@SneakyThrows
public Boolean isExist(
String indexName,
String docId
) {
// 轉為小寫
String iName = indexName.toLowerCase(Locale.ROOT);
ExistsRequest existsRequest = new ExistsRequest.Builder()
.index(iName)
.id(docId)
.build();
return getEsAsyncClient().exists(existsRequest).get().value();
}
/**
* 查詢距離目前最近的文檔的時間值
*
* @param indexName
* @return
*/
@SneakyThrows
public Long lastTime(String indexName) {
// 轉為小寫
String iName = indexName.toLowerCase(Locale.ROOT);
// 排序字段規則
FieldSort fs = new FieldSort.Builder()
.field("time")
.order(SortOrder.Desc)
.build();
// 排序操作項
SortOptions so = new SortOptions.Builder()
.field(fs)
.build();
// 查詢請求對象
SearchRequest searchRequest = new SearchRequest.Builder()
.index(iName)
// 可以接收多個值
.sort(so)
.size(1).build();
// 異步查詢
SearchResponse<EsDocument> response = getEsAsyncClient().search(searchRequest, EsDocument.class).get();
// 結果集
List<Hit<EsDocument>> hits = response.hits().hits();
// 時間最近的文檔
EsDocument doc = hits.stream().findFirst().get().source();
// 傳回時間值(秒)
return doc.getTime();
}
/**
* 根據關鍵字查文檔
* ---------------------------
* 隻要标題和内容中有一個比對即可
* ---------------------------
*
* @param indexName 索引名稱
* @param keyword 關鍵字
* @return List 集合
*/
@SneakyThrows
public List<EsDocVo> query(
String indexName,
String keyword
) {
// 轉為小寫
String iName = indexName.toLowerCase(Locale.ROOT);
/*MatchQuery matchQuery = new MatchQuery.Builder()
.field(fieldName)
.query(fieldValue)
.build();
Query query = new Query.Builder()
.match(matchQuery)
.build();
//SearchRequest searchRequest = new SearchRequest.Builder().index(indexName).query(query).build();
SearchRequest searchRequest = new SearchRequest.Builder().index(indexName).query(query).build();
SearchResponse<EsDocument> searchResponse = getEsClient().search(searchRequest, EsDocument.class);
*/
// ---------------- lambda表達式寫法(嵌套搜尋查詢)------------------
// 标題中查找
Query byTitle = MatchQuery.of(m -> m
// EsDocument的标題字段名
.field("title")
.query(keyword)
)._toQuery();
// 内容中查找
Query byContent = MatchQuery.of(m -> m
// EsDocument的内容字段名
.field("content")
.query(keyword)
)._toQuery();
// 異步
SearchResponse<EsDocument> response = getEsAsyncClient().search(s -> s
.index(iName)
.query(q -> q
// boolean 嵌套搜尋;must需同時滿足,should一個滿足即可
.bool(b -> b
//
//.must(byTitle )
//.must(byContent )
.should(byTitle)
.should(byContent)
)
),
EsDocument.class
).get();
List<Hit<EsDocument>> hits = response.hits().hits();
// 轉為 List<EsDocument>
//List<EsDocument> docs = hits.stream().map(hit -> hit.source()).collect(Collectors.toList());
//List<EsDocument> docs = hits.stream().map(Hit::source).collect(Collectors.toList());
List<EsDocVo> docs = hits.stream().map(hit -> getEsDocVo(hit.source())).collect(Collectors.toList());
// 關閉transport
//close();
return docs;
}
/**
* 【分頁查找】根據關鍵字查文檔
* ---------------------------
* 隻要标題和内容中有一個比對即可
* 預設目前頁:1
* 預設頁面記錄數:10
* 支援高亮
* ---------------------------
*
* @param indexName 索引名稱
* @param keyword 關鍵字
* @return List 集合
*/
@SneakyThrows
public EsPage page(
String indexName,
String keyword
) {
return page(indexName, keyword, 1, 10);
}
/**
* 【分頁查找】根據關鍵字查文檔
* ---------------------------
* 隻要标題和内容中有一個比對即可
* 支援高亮
* ---------------------------
*
* @param indexName 索引名稱
* @param keyword 關鍵字
* @param current 目前頁
* @param pageSize 頁面記錄數
* @return EsPage 對象
*/
@SneakyThrows
public EsPage page(
String indexName,
String keyword,
Integer current,
Integer pageSize
) {
// 轉為小寫
String iName = indexName.toLowerCase(Locale.ROOT);
/*MatchQuery matchQuery = new MatchQuery.Builder()
.field(fieldName)
.query(fieldValue)
.build();
Query query = new Query.Builder()
.match(matchQuery)
.build();
//SearchRequest searchRequest = new SearchRequest.Builder().index(indexName).query(query).build();
SearchRequest searchRequest = new SearchRequest.Builder().index(indexName).query(query).build();
SearchResponse<EsDocument> searchResponse = getEsClient().search(searchRequest, EsDocument.class);
*/
// ---------------- lambda表達式寫法(嵌套搜尋查詢)------------------
// 多條件查詢(從title或content中查詢keyword)
Query byKeyword = MultiMatchQuery.of(m -> m
.fields("title", "content")
//.fields("title")
.query(keyword)
)._toQuery();
// 起始文檔值(從0開始)
Integer from = (current - 1) * pageSize;
// 存放高亮的字段,預設與文檔字段一緻
HighlightField hf = new HighlightField.Builder().build();
Highlight highlight = new Highlight.Builder()
// 前字尾預設就是em,可省略
//.preTags("<em>")
//.postTags("</em>")
.fields("title", new HighlightField.Builder().build())
.fields("content", new HighlightField.Builder().build())
.requireFieldMatch(false)
.build();
// 異步
SearchResponse<EsDocument> response = getEsAsyncClient().search(s -> s
.index(iName)
.query(byKeyword)
.highlight(highlight)
.from(from).size(pageSize),
EsDocument.class
).get();
// 建構EsPage
EsPage esPage = new EsPage();
esPage.setKeyword(keyword);
esPage.setTotal(response.hits().total().value());
esPage.setCurrent(current);
esPage.setPageSize(pageSize);
// 查詢結果
List<Hit<EsDocument>> hits = response.hits().hits();
// 文檔VO對象集合;實作高亮
List<EsDocVo> docs = new ArrayList<>();
// 流式周遊查詢結果:用高亮字段替換原文檔字段
hits.stream().forEach(hit -> {
// 原文檔
EsDocument doc = hit.source();
// 高亮标題字段
List<String> titles = hit.highlight().get("title");
if(!CollectionUtils.isEmpty(titles)){
// 替換原标題
doc.setTitle(titles.get(0));
}
// 高亮内容字段
List<String> contents = hit.highlight().get("content");
if(!CollectionUtils.isEmpty(contents)){
// 替換原内容
doc.setContent(contents.get(0));
}
// 原文檔轉為VO,加入VO對象集合中
docs.add(getEsDocVo(doc));
});
// VO對象集合注入page對象
esPage.setRecords(docs);
// 關閉transport
//close();
// 傳回page
return esPage;
}
/**
* 【分頁查找】根據屬主、文檔類型、關鍵字查文檔
* 支援高亮
* ---------------------------
* 1、公共文檔:類型 0;任何人都可以查詢,不需要比對屬主
* 2、非公共文檔:類型 1、2、3.;有限制查詢,隻有文檔屬主可以查詢;如:tom的文檔,隻有tom可以查詢
* 3、關鍵字:隻要标題和内容中有一個比對即可
* ---------------------------
* 查詢中文與英文的匹别:
* 1、中文:單個漢字為一個詞;如:中國,可以分為:中、國,有一個比對上就算成功
* 2、英文:一個單詞為一個詞;
* ---------------------------
* 注意:
* 屬主名稱選擇時,不要用中文,全部用英文,且有固定格式,不可修改
* ---------------------------
*
* @param indexName 索引名稱
* @param keyword 關鍵字
* @param owner 文檔屬主
* @param current 目前頁
* @param pageSize 頁面記錄數
* @return EsPage 對象
*/
@SneakyThrows
public EsPage page(
String indexName,
String keyword,
String owner,
Integer current,
Integer pageSize
) {
// 轉為小寫
String iName = indexName.toLowerCase(Locale.ROOT);
// ---------------- lambda表達式寫法(嵌套搜尋查詢)------------------
// 多條件查詢(從title或content中查詢keyword)
Query byKeyword = MultiMatchQuery.of(m -> m
.fields("title", "content")
.query(keyword)
)._toQuery();
// 文檔類型(公共文檔)
Query byType1 = TermQuery.of(m -> m
// EsDocument的内容字段名
.field("type")
.value(0)
)._toQuery();
// 文檔類型(私有文檔)
Query byType2 = TermQuery.of(m -> m
// EsDocument的内容字段名
.field("type")
.value(1)
)._toQuery();
// 文檔屬主(屬主名稱完全比對)
Query byOwner = TermQuery.of(m -> m
// EsDocument的内容字段名
.field("owner")
.value(owner)
)._toQuery();
// 起始文檔值(從0開始)
Integer from = (current - 1) * pageSize;
// 存放高亮的字段,預設與文檔字段一緻
HighlightField hf = new HighlightField.Builder().build();
Highlight highlight = new Highlight.Builder()
// 前字尾預設就是em,可省略
//.preTags("<em>")
//.postTags("</em>")
.fields("title", new HighlightField.Builder().build())
.fields("content", new HighlightField.Builder().build())
.requireFieldMatch(false)
.build();
// 異步
SearchResponse<EsDocument> response = getEsAsyncClient().search(s -> s
.index(iName)
.query(q -> q
// 布爾比較:有一個條件滿足即可
.bool(b -> b
// 條件一:must:兩個子條件都滿足時,條件才成立;【公共文檔】
.should(sq1 -> sq1.bool(sqb1 -> sqb1.must(byType1, byKeyword)))
// 條件二:must:三個子條件都滿足時,條件才成立;【私有文檔】
.should(sq2 -> sq2.bool(sqb2 -> sqb2.must(byType2, byOwner, byKeyword)))
)
).highlight(highlight)
.from(from).size(pageSize),
EsDocument.class
).get();
// 建構EsPage
EsPage esPage = new EsPage();
esPage.setKeyword(keyword);
esPage.setTotal(response.hits().total().value());
esPage.setCurrent(current);
esPage.setPageSize(pageSize);
// 查詢結果
List<Hit<EsDocument>> hits = response.hits().hits();
// 文檔VO對象集合;實作高亮
List<EsDocVo> docs = new ArrayList<>();
// 流式周遊查詢結果:用高亮字段替換原文檔字段
hits.stream().forEach(hit -> {
// 原文檔
EsDocument doc = hit.source();
// 高亮标題字段
List<String> titles = hit.highlight().get("title");
if(!CollectionUtils.isEmpty(titles)){
// 替換原标題
doc.setTitle(titles.get(0));
}
// 高亮内容字段
List<String> contents = hit.highlight().get("content");
if(!CollectionUtils.isEmpty(contents)){
// 替換原内容
doc.setContent(contents.get(0));
}
// 原文檔轉為VO,加入VO對象集合中
docs.add(getEsDocVo(doc));
});
// VO對象集合注入page對象
esPage.setRecords(docs);
// 關閉transport
//close();
// 傳回page
return esPage;
}
/**
* 批量删除文檔
*
* @param indexName 索引名稱
* @param documentIds 文檔ID集合
* @return 成功删除數量
*/
@SneakyThrows
public Integer del(
String indexName,
List<String> docIds
) {
// 轉為小寫
String iName = indexName.toLowerCase(Locale.ROOT);
// 批量操作對象集合
List<BulkOperation> bs = new ArrayList<>();
// 建構【批量操作對象】,并裝入list集合中
docIds.stream().forEach(docId -> {
// 删除操作對象
DeleteOperation delOpe = new DeleteOperation.Builder().id(docId).build();
// 建構【批量操作對象】
BulkOperation opt = new BulkOperation.Builder().delete(delOpe).build();
// 裝入list集合
bs.add(opt);
});
// 建構【批理請求對象】
BulkRequest bulkRequest = new BulkRequest.Builder()
// 索引
.index(iName)
// 批量操作對象集合
.operations(bs)
.build();
// 批量操作
BulkResponse bulkResponse = getEsAsyncClient().bulk(bulkRequest).get();
int i = bulkResponse.items().size();
log.info("成功處理 {} 份文檔!", i);
return i;
}
/**
* 删除所有文檔
* 實際上删除的是索引
*
* @param indexName
* @return
*/
public Boolean delAll(String indexName) {
return index.del(indexName);
}
private EsDocVo getEsDocVo(EsDocument esDocument) {
EsDocVo esDocVo = new EsDocVo();
esDocVo.setId(esDocument.getId());
esDocVo.setTitle(esDocument.getTitle());
esDocVo.setContent(esDocument.getContent());
esDocVo.setUrl(esDocument.getUrl());
// ------ 時間轉換 ------
// 目前時刻
Long now = Instant.now().getEpochSecond();
Long n = now - esDocument.getTime();
// 秒數
Long secOfMinute = 60L;
//Long secOfHour = secOfMinute * 60L;
Long secOfHour = 3600L;
//Long secOfDay = secOfHour * 24L;
Long secOfDay = 86400L;
//Long secOfWeek = secOfDay * 7L;
Long secOfWeek = 604800L;
//Long secOfMonth = secOfDay * 30L;
Long secOfMonth = 2592000L;
//Long secOfYear = secOfMonth * 12L;
Long secOfYear = 31104000L;
if (n > secOfYear) {
Double floor = Math.floor(n / secOfYear);
esDocVo.setTime(floor.intValue() + "年前");
} else if (n > secOfMonth) {
Double floor = Math.floor(n / secOfMonth);
esDocVo.setTime(floor.intValue() + "個月前");
} else if (n > secOfWeek) {
Double floor = Math.floor(n / secOfWeek);
esDocVo.setTime(floor.intValue() + "周前");
} else if (n > secOfDay) {
Double floor = Math.floor(n / secOfDay);
esDocVo.setTime(floor.intValue() + "天前");
} else if (n > secOfHour) {
Double floor = Math.floor(n / secOfHour);
esDocVo.setTime(floor.intValue() + "小時前");
} else if (n > secOfMinute) {
Double floor = Math.floor(n / secOfMinute);
esDocVo.setTime(floor.intValue() + "分鐘前");
} else {
esDocVo.setTime("剛剛");
}
return esDocVo;
}
}
// ===================== 基礎操作(僅供内部調用) ============================
private static ElasticsearchTransport transport;
/**
* 同步用戶端;調用結束後,需調用close()關閉transport
*
* @return
*/
private static ElasticsearchClient getEsClient() {
ElasticsearchClient client = new ElasticsearchClient(getEsTransport());
return client;
}
/**
* 異步用戶端
*
* @return
*/
private static ElasticsearchAsyncClient getEsAsyncClient() {
ElasticsearchAsyncClient asyncClient =
new ElasticsearchAsyncClient(getEsTransport());
return asyncClient;
}
/**
* 擷取Transport
*
* @return
*/
private static ElasticsearchTransport getEsTransport() {
RestClient restClient = RestClient.builder(
new HttpHost("localhost", 9200)).build();
// Create the transport with a Jackson mapper
transport = new RestClientTransport(
restClient, new JacksonJsonpMapper());
return transport;
}
/**
* 關閉transport
*/
private static void close() {
if (transport != null) {
try {
transport.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
4、測試類
package com.tuwer;
import co.elastic.clients.elasticsearch.indices.*;
import com.tuwer.pojo.EsDocVo;
import com.tuwer.pojo.EsDocument;
import com.tuwer.pojo.EsPage;
import com.tuwer.util.EsUtil;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resources;
import java.util.*;
/**
* @author 土味兒
* Date 2022/8/9
* @version 1.0
*/
@SpringBootTest
public class MyTest {
@Autowired
EsUtil esUtil;
// 目标索引
String indexName = "tuwer_index001";
// --------------------------- 工具類方法 ---------------------------------
// -----索引-----
@Test
public void testCreateIndexByUtil() {
//System.out.println(EsClientUtil.createIndex(indexName));
//EsClientUtil.createIndex("INDEX_abc");
esUtil.index.create("INDEX_abc123");
}
@Test
public void testQueryIndexByUtil() {
Map<String, IndexState> result = esUtil.index.query("tuwer_index");
//Map<String, IndexState> result = EsClientUtil.indexQueryAsync("tuwer_index");
/* for (Map.Entry<String, IndexState> entry : result.entrySet()) {
System.out.println(entry.getKey() + " : " + entry.getValue());
}*/
for (String s : result.keySet()) {
System.out.println(result.get(s).dataStream());
}
}
@Test
public void testGetAllIndex(){
Set<String> idxs = esUtil.index.all();
for (String idx : idxs) {
System.out.println(idx);
}
}
@Test
public void testDeleteIndexByUtil() {
boolean b = esUtil.index.del("tuwer_index001");
System.out.println(b);
}
// -----文檔-----
@Test
public void testCreateDocument() {
EsDocument esDocument = new EsDocument("123",0,"标題","測試123","admin","abc123");
String res = esUtil.doc.createOrUpdate(indexName, "ABC", esDocument);
System.out.println(res);
}
@Test
public void testBatchCreateDocument() {
Map<String, EsDocument> userMap = new HashMap<>();
//for (int i = 0; i < 3; i++) {
EsDocument doc1 = new EsDocument("11",0,"中","沒123世213界人","","abc123");
userMap.put(doc1.getId(), doc1);
EsDocument doc2 = new EsDocument("12",0,"世","河231人測123南測試中","","abc123");
userMap.put(doc2.getId(), doc2);
EsDocument doc3 = new EsDocument("13",0,"原中","天大1231去131南","","abc123");
userMap.put(doc3.getId(), doc3);
EsDocument doc4 = new EsDocument("21",1,"中","沒123世213界人","admin","abc123");
userMap.put(doc4.getId(), doc4);
EsDocument doc5 = new EsDocument("22",1,"世","河231人測123南測試中","34admin","abc123");
userMap.put(doc5.getId(), doc5);
EsDocument doc6 = new EsDocument("23",1,"原中","天大1231去131南","admin67","abc123");
userMap.put(doc6.getId(), doc6);
//}
int i = esUtil.doc.createOrUpdateBth(indexName, userMap);
/*for (BulkResponseItem item : bulkResponse.items()) {
System.out.println(item.id());
}*/
System.out.println(i);
}
@Test
public void testDocIsExist(){
//System.out.println(EsClientUtil.docIsExist(indexName, "8001"));
System.out.println(esUtil.doc.isExist("tuwer_IndeX001", "8001"));
}
@Test
public void testDeleteDocument() {
List<String> documentIds = new ArrayList<>();
documentIds.add("101");
documentIds.add("102");
documentIds.add("100");
documentIds.add("201");
documentIds.add("202");
documentIds.add("203");
documentIds.add("ABC");
documentIds.add("_search");
int i = esUtil.doc.del(indexName, documentIds);
System.out.println(i);
}
@Test
public void testDocDelAll(){
esUtil.doc.delAll(indexName);
}
@Test
public void testQueryDocument() {
List<EsDocVo> docs = esUtil.doc.query(indexName, "中");
//List<Hit<User>> users = EsClientUtil.queryDocumentByField(indexName, "name", "test_6001");
for (EsDocVo doc : docs) {
System.out.println(doc);
}
}
@Test
public void testDocPage(){
//EsPage p = esUtil.doc.page(indexName, "中", 1, 5);
EsPage p = esUtil.doc.page(indexName, "世", 1, 20);
//esUtil.doc.page(indexName, "中", 1, 20);
//EsPage p = esUtil.doc.page(indexName, "世", "admin67",1, 20);
//EsPage p = esUtil.doc.page(indexName, "中");
//System.out.println(p);
System.out.println("--------------");
for (EsDocVo record : p.getRecords()) {
System.out.println(record);
}
}
@Test
public void testDocLastTime(){
//EsPage p = esUtil.doc.page(indexName, "中", 1, 5);
//EsPage p = esUtil.doc.page(indexName, "中", "admin",1, 5);
//EsPage p = esUtil.doc.page(indexName, "中");
Long lastTime = esUtil.doc.lastTime(indexName);
System.out.println(lastTime);
}
}