1、Elasticsearch的JAVA客戶端選擇
Elasticsearch官方支持的客戶端
客戶端名稱 | 簡介 | 使用建議 |
---|---|---|
Elasticsearch Java API Client(新客戶端) | 官方推薦的新客戶端,基于 JSON Mapping(如 ElasticsearchClient 類),從 Elasticsearch 7.15 開始推出。 | ?推薦用于 Spring Boot 3+,Elasticsearch 8+ |
RestHighLevelClient(已廢棄) | 基于 REST 的高級客戶端,是 ES 6 ~ 7 的主力客戶端,ES 8 中已標記為 deprecated。 | ?不推薦新項目使用 |
Low Level REST Client | 底層客戶端,只提供 HTTP 封裝,不解析 JSON。 | 🔧適合自定義協議或處理特殊 JSON 請求場景 |
Spring官方對Elasticsearch的封裝
客戶端名稱 | 簡介 | 特點 |
---|---|---|
Spring Data Elasticsearch | Spring 官方對 Elasticsearch 的數據訪問封裝,支持 Repository 風格的接口編程。 | 👍開發效率高、和 JPA 風格一致,但功能不如原生客戶端全 |
easy-es(dromara團隊),國人之光!
客戶端名稱 | 簡介 | 特點 |
---|---|---|
easy-es | 風格類似 MyBatis-Plus,一致的 API 和分頁查詢方式,Java 開發者易于理解。 | 👍開發效率高、但是是對RestHighLevelClient的深層封裝,容易受版本影響,小團隊維護 |
總結:
RestHighLevelClient 是ES7中使用最多的客戶端,但是在ES8中已經廢棄。
easy-es? 是基于RestHighLevelClient封裝的,會比較重,代碼風格類似 MyBatis-Plus,熟悉MP的同學容易上手,但是容易受RestHighLevelClient和Elasticsearch版本的限制,并且目前社區雖然活躍,但項目主要靠小團隊維護,不如官方客戶端那樣穩定長期。
Elasticsearch Java API Client 是 Elasticsearch 7.15 開始推出最新的客戶端,能使用Elasticsearch中所有功能,首選首選!!!!!!
Spring Data Elasticsearch 是Spring 官方對 Elasticsearch的封裝,Springboot3中已經棄用了RestHighLevelClient,選擇引用了Elasticsearch Java API Client,直接解決了依賴版本沖突的問題,Spring社區強大,所以...不用我說了吧....選我!!!!!
2、Spring Data Elasticsearch 官方文檔
Elasticsearch Clients :: Spring Data Elasticsearch
3、Springboot3整合Spring Data Elasticsearch
tips:我使用的是Springboot3.3.4版本
3.1 maven引入
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-elasticsearch</artifactId></dependency>
引入成功之后可以看到:
elasticsearch的新客戶端elasticsearch-java也被引入了進來。
3.2 配置
yml配置文件
spring:application:name: cloud-elasticsearchelasticsearch:uris: http://127.0.0.1:9200 # 這里還要注意是https還是http協議
# username: elastic #如果有賬號密碼就要配置賬號密碼,否則可以不配置
# password: 123456server:port: 20000
4、【簡單使用】Spring Data Elasticsearch
4.1 創建ES實體類
創建完實體類后,啟動項目Spring會自動根據注解,來創建ES的索引(index)和映射(mapping)
@Data
@Document(indexName = "news")
@JsonIgnoreProperties(ignoreUnknown = true)
@AllArgsConstructor
@NoArgsConstructor
public class EsNews {@Idprivate String id;@Field(type = FieldType.Text, analyzer = "ik_max_word",searchAnalyzer = "ik_smart")private String title;//標題@Field(type = FieldType.Text, analyzer = "ik_max_word",searchAnalyzer = "ik_smart")private String content;//內容@Field(type = FieldType.Keyword)private String author;//作者@Field(type = FieldType.Keyword)private List<String> tags;//標簽@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")@Field(type = FieldType.Date,pattern = "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd")@JsonProperty("publish_date")private Date publishDate;//發布時間@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")@Field(type = FieldType.Date,pattern = "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd")@JsonProperty("create_time")private Date createTime;//創建時間@Field(type = FieldType.Long)@JsonProperty("view_count")private Long viewCount;//閱讀量}
注解解析:
@Document(indexName = "news")
該實體對應的索引名稱為:news
@Id
ES的唯一標識
@Field
@Field(type = FieldType.Text, analyzer = "ik_max_word",searchAnalyzer = "ik_smart")
Field(字段名)?, type = FieldType.Text(字段類型為TEXT) ,analyzer = "ik_max_word"(存入時的分詞器為ik_max_word),searchAnalyzer = "ik_smart"(搜索時的分詞器為ik_smart)
@JsonProperty("publish_date")
ES中JSON字段,迎來進行序列化映射
tips:實際開發中業務實體類和ES實體類最好是分開的,業務實體類主要用來做數據庫操作,ES實體類只用來做ES檢索
4.2 繼承ElasticsearchRepository接口
public interface EsNewsRepository extends ElasticsearchRepository<EsNews,String> {}
查看ElasticsearchRepository源碼可以看到
ElasticsearchRepository也繼承了PagingAndSortingRepository(分頁和排序接口)、CrudRepository(常用基礎crud接口)
當你的類接口繼承了ElasticsearchRepository后,你輸入find,你會看到Spring幫你生成的所有常用簡單的查詢語句。
大部分關鍵詞用法:
關鍵詞 | 說明 | 等價 Elasticsearch 查詢類型 |
---|---|---|
findBy | 查詢開始(必須) | - |
And / Or | 條件連接符 | bool 查詢 |
Is / Equals | 等于 | term |
Between | 在兩個值之間 | range |
LessThan | 小于 | range |
LessThanEqual | 小于等于 | range |
GreaterThan | 大于 | range |
GreaterThanEqual | 大于等于 | range |
After | 大于(時間) | range |
Before | 小于(時間) | range |
IsNull | 字段為 null | must_not exists |
IsNotNull / NotNull | 字段非 null | exists |
Like | 類似(不建議用,Elasticsearch 中更推薦 Containing ) | match (部分分詞匹配) |
NotLike | 不類似 | bool + must_not |
StartingWith | 以…開頭(需要 keyword 類型字段,match 不支持) | prefix / wildcard |
EndingWith | 以…結尾(需 keyword 類型字段) | wildcard |
Containing / Contains | 包含(常用于全文檢索) | match |
NotContaining | 不包含 | bool + must_not |
In | 包含在列表中 | terms |
NotIn | 不包含在列表中 | bool + must_not terms |
True / False | 布爾值判斷 | term |
OrderBy | 排序 | sort |
4.3 CRUD接口使用
使用SpringBoot單元測試
4.3.1 新增
文檔單個新增(save):?
@Test@DisplayName("新增單個文檔")void saveDoc(){EsNews news = new EsNews();news.setId("1");//如果不設置ID,Spring則會幫你生成一個ES風格的隨機IDnews.setTitle("電影《不能說的秘密》熱映");news.setContent("內容:不能說的秘密............牛X..");news.setAuthor("周杰倫");news.setTags(Arrays.asList("電影", "國產"));news.setPublishDate(new Date());news.setCreateTime(new Date());news.setViewCount(100L);esNewsRepository.save(news);}
文檔批量新增(saveAll):
@Test@DisplayName("批量新增文檔")void saveBatchDoc(){List<EsNews> newsList = new ArrayList<>();for (int i = 1; i <= 11; i++) {EsNews news = new EsNews();news.setId(String.valueOf(i));news.setTitle("電影《CPW的奇幻世界 " + i + "》");news.setContent("內容 " + i);news.setAuthor("作者" + i);news.setTags(Arrays.asList("電影", "奇幻"));news.setPublishDate(new Date());news.setCreateTime(new Date());news.setViewCount(100L + i);newsList.add(news);}esNewsRepository.saveAll(newsList);}
4.3.3?修改
!!!ElasticsearchRepository!!!的修改跟新增是同一個接口,如果你的對象攜帶ID,那么ES會先查詢文檔庫里是有存在這么一個ID,如果存在的話則進行 先刪除 然后 覆蓋!!
@Test@DisplayName("新增單個文檔")void saveDoc(){EsNews news = new EsNews();news.setId("1");//ES會先找文檔庫里是否存在改ID,先刪除再覆蓋news.setTitle("電影《不能說的秘密》熱映");news.setContent("內容:不能說的秘密........牛X..更新覆蓋操作");news.setAuthor("周杰倫");news.setTags(Arrays.asList("電影", "國產"));news.setPublishDate(new Date());news.setCreateTime(new Date());news.setViewCount(100L);esNewsRepository.save(news);}
如果你想做到只修改文檔中其中一條數據,比如只把作者周杰倫修改成CPW,那就需要用到第五節【高階用法】Elasticsearch Java API Client
4.3.3 查詢
需求:我要查詢文檔編號為999的文檔
tips:簡單的查詢,比如根據ID查詢文檔,ElasticsearchRepository已經自己封裝好了,不用另外寫。(findById)
@Test@DisplayName("根據ID查詢文檔")void searchByID(){Optional<EsNews> news = esNewsRepository.findById("999");System.out.println(news);}
需求:我要分頁查詢,標題包含【奇幻世界】,作者精準是【作者1】的文檔
tips:這種復雜多條件的就需要我們自己寫,如果是模糊查詢的則用Containing
1、EsNewsRepository新增接口findByTitleContainingOrAuthor:
public interface EsNewsRepository extends ElasticsearchRepository<EsNews,String> {Page<EsNews> findByTitleContainingOrAuthor(String Title, String Author,Pageable pageable);}
2、使用
@Test@DisplayName("分頁查詢條件")void searchAll(){Pageable pageable = PageRequest.of(0, 10);Page<EsNews> pageList = esNewsRepository.findByTitleContainingOrAuthor("奇幻世界","作者1",pageable);for (EsNews news : pageList) {System.out.println(news);}}
根據4.2中的關鍵詞,還有更多的用法例如過濾、排序
4.3.4?刪除
刪除就沒什么好說的了,直接上代碼!
@Test@DisplayName("根據ID刪除文檔")void deleteDocById(){esNewsRepository.deleteById("1");System.out.println("ID為1的文檔刪除成功");}@Test@DisplayName("批量刪除文檔")void deleteBatchDoc(){esNewsRepository.deleteAll();System.out.println("文檔批量刪除成功");}@Test@DisplayName("根據ID批量刪除文檔")void deleteBatchDocByIds(){List<String> idList = Arrays.asList("1", "2");esNewsRepository.deleteAllById(idList);System.out.println("根據ID批量刪除文檔刪除成功");}
5、【高階用法】Elasticsearch Java API Client
一句話:Spring Data Elasticsearch不能實現的,再用Elasticsearch Java API Client,例如只更新數據中的個別字段數據,高級查詢(聚合查詢),bulk函數等。。
Elasticsearch Java API Client對熟悉ES語句的同學非常友好,學會基本代碼用法跟寫語句一樣絲滑。
不熟悉語句建議看一下ES入門教程:Elasticsearch8(ES)保姆級菜鳥入門教程-CSDN博客
建議看著文檔配合服用~
5.1?ElasticsearchClient
@Autowiredprivate ElasticsearchClient elasticsearchClient;
所有的ES8功能都在ElasticsearchClient中,先來看看有哪些方法:
可以看出CRUD的方法和參數,分別不同:
查詢的語句用的是SearchRequest
更新的語句用的是UpdateRequest
新增的語句用的是CreateRequest
刪除的語句用的是DeleteRequest
批量操作語句用的是BulkRequest
5.2 更新數據個別字段
需求:更新999號文檔,將作者名修改為CPW,標題修改為:CPW的《不能說的秘密》
@Test@DisplayName("更新數據的個別字段")void updateNews() throws IOException {UpdateRequest<Object, Object> updateRequest = UpdateRequest.of(u -> u.index("news").id("999").doc(Map.of("author", "CPW","title", "CPW的《不能說的秘密》")));elasticsearchClient.update(updateRequest, EsNews.class);}
對比語句:
效果:
5.3 bulk(高性能批量操作)
例子:bulk的批量插入
@Test@DisplayName("bulk的批量使用")void bulkNews() throws IOException {//數據List<EsNews> newsList = new ArrayList<>();EsNews news1 = new EsNews("777", "CPW的Elasticsearch入門1", "CPW寫的Elasticsearch入門教程,整合了Springboot3", "張三", Arrays.asList("教程", "入門"),new Date(), new Date(), 1000L);EsNews news2 = new EsNews("888", "CPW的Elasticsearch入門2", "CPW寫的Elasticsearch入門教程,整合了Springboot3", "李四", Arrays.asList("教程", "入門"),new Date(), new Date(), 1000L);newsList.add(news1);newsList.add(news2);//構建bulkRequest請求BulkRequest.Builder builder = new BulkRequest.Builder();for (EsNews news : newsList) {builder.operations(op -> op.index(idx -> idx.index("news").id(news.getId()).document(news)));}BulkResponse bulk = elasticsearchClient.bulk(builder.build());if (bulk.errors()) {System.out.println("存在失敗的操作:");bulk.items().forEach(item -> {if (item.error() != null) {System.out.println("失敗項: " + item.error().reason());}});} else {System.out.println("批量新增成功,共新增: " + bulk.items().size() + " 條");}}
需要使用到批量更新,刪除,等操作直接修改builder.operations下的接口即可。
5.4 高亮查詢
@Test@DisplayName("高亮查詢")void highLightNews() throws IOException {SearchRequest searchRequest = SearchRequest.of(s -> s.index("news").query(q -> q.multiMatch(m -> m.query("CPW").fields("title", "content"))).highlight(h -> h.fields("title",f -> f)//f->f啟動默認配置,等同于.preTags("<em>").postTags("</em>").fields("content",f -> f).preTags("<em style='color:red'>") // 自定義高亮前綴標簽.postTags("</em>")));SearchResponse<EsNews> search = elasticsearchClient.search(searchRequest, EsNews.class);List<Hit<EsNews>> hits = search.hits().hits();hits.forEach(hit->{if(hit.highlight().containsKey("title") && hit.source() != null){hit.source().setTitle(hit.highlight().get("title").get((0)));}if(hit.highlight().containsKey("content") && hit.source() != null){hit.source().setContent(hit.highlight().get("content").get((0)));}System.out.println(hit.source());});}
ES語句對比:
返回結果:
5.5 聚合查詢
需求:查詢每個作者,各自寫了多少篇新聞。
@Test@DisplayName("聚合查詢")void BucketingNews() throws IOException {SearchRequest searchRequest = SearchRequest.of(s -> s.index("news").size(0).aggregations("author_count",a -> a.terms(t -> t.field("author"))));SearchResponse<EsNews> search = elasticsearchClient.search(searchRequest, EsNews.class);List<StringTermsBucket> CountList = search.aggregations().get("author_count").sterms().buckets().array();System.out.println(CountList);}
6、【最佳實踐】Elasticsearch+消息隊列(RabbitMQ)+數據庫(MYSQL)
實際應用