springboot中使用ElasticSearch的詳細(xì)教程
新建項(xiàng)目
新建一個(gè)springboot項(xiàng)目springboot_es
用于本次與ElasticSearch的整合,如下圖
引入依賴
修改我們的pom.xml
,加入spring-boot-starter-data-elasticsearch
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-elasticsearch</artifactId> </dependency>
編寫配置文件
由于ElasticSearch從7.x版本開始淡化TransportClient甚至于在8.x版本中遺棄,所以spring data elasticsearch推薦我們使用rest客戶端RestHingLevelClient
(端口號(hào)使用9200)以及接口ElasticSearchRespositoy
。
- RestHighLevelClient 更強(qiáng)大,更靈活,但是不能友好的操作對(duì)象
- ElasticSearchRepository 對(duì)象操作友好
首先我們編寫配置文件如下
/** * ElasticSearch Rest Client config * @author Christy * @date 2021/4/29 19:40 **/ @Configuration public class ElasticSearchRestClientConfig extends AbstractElasticsearchConfiguration{ @Override @Bean public RestHighLevelClient elasticsearchClient() { final ClientConfiguration clientConfiguration = ClientConfiguration.builder() .connectedTo("192.168.8.101:9200") .build(); return RestClients.create(clientConfiguration).rest(); } }
springboot操作ES
RestHighLevelClient方式
有了上面的rest client,我們就可以在其他的地方注入該客戶端對(duì)ElasticSearch進(jìn)行操作。我們新建一個(gè)測試文件,使用客戶端對(duì)ElasticSearch進(jìn)行基本的操作
1.注入RestClient
/** * ElasticSearch Rest client操作 * * RestHighLevelClient 更強(qiáng)大,更靈活,但是不能友好的操作對(duì)象 * ElasticSearchRepository 對(duì)象操作友好 * * 我們使用rest client 主要測試文檔的操作 * @Author Christy * @Date 2021/4/29 19:51 **/ @SpringBootTest public class TestRestClient { // 復(fù)雜查詢使用:比如高亮查詢 @Autowired private RestHighLevelClient restHighLevelClient; }
2.插入一條文檔
/** * 新增一條文檔 * @author Christy * @date 2021/4/29 20:17 */ @Test public void testAdd() throws IOException { /** * 向ES中的索引christy下的type類型中添加一天文檔 */ IndexRequest indexRequest = new IndexRequest("christy","user","11"); indexRequest.source("{\"name\":\"齊天大圣孫悟空\",\"age\":685,\"bir\":\"1685-01-01\",\"introduce\":\"花果山水簾洞美猴王齊天大圣孫悟空是也!\"," + "\"address\":\"花果山\"}", XContentType.JSON); IndexResponse indexResponse = restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT); System.out.println(indexResponse.status()); }
我們可以看到文檔插入成功,我們?nèi)ibana中查詢?cè)摋l文檔
完全沒有問題的。
3.刪除一條文檔
/** * 刪除一條文檔 * @author Christy * @date 2021/4/29 20:18 */ @Test public void deleteDoc() throws IOException { // 我們把特朗普刪除了 DeleteRequest deleteRequest = new DeleteRequest("christy","user","rYBNG3kBRz-Sn-2f3ViU"); DeleteResponse deleteResponse = restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT); System.out.println(deleteResponse.status()); } }
4.更新一條文檔
/** * 更新一條文檔 * @author Christy * @date 2021/4/29 20:19 */ @Test public void updateDoc() throws IOException { UpdateRequest updateRequest = new UpdateRequest("christy","user","p4AtG3kBRz-Sn-2fMFjj"); updateRequest.doc("{\"name\":\"調(diào)皮搗蛋的hardy\"}",XContentType.JSON); UpdateResponse updateResponse = restHighLevelClient.update(updateRequest, RequestOptions.DEFAULT); System.out.println(updateResponse.status()); }
5.批量更新文檔
/** * 批量更新 * @author Christy * @date 2021/4/29 20:42 */ @Test public void bulkUpdate() throws IOException { BulkRequest bulkRequest = new BulkRequest(); // 添加 IndexRequest indexRequest = new IndexRequest("christy","user","13"); indexRequest.source("{\"name\":\"天蓬元帥豬八戒\",\"age\":985,\"bir\":\"1685-01-01\",\"introduce\":\"天蓬元帥豬八戒因調(diào)戲嫦娥被貶下凡\",\"address\":\"高老莊\"}", XContentType.JSON); bulkRequest.add(indexRequest); // 刪除 DeleteRequest deleteRequest01 = new DeleteRequest("christy","user","pYAtG3kBRz-Sn-2fMFjj"); DeleteRequest deleteRequest02 = new DeleteRequest("christy","user","uhTyGHkBExaVQsl4F9Lj"); DeleteRequest deleteRequest03 = new DeleteRequest("christy","user","C8zCGHkB5KgTrUTeLyE_"); bulkRequest.add(deleteRequest01); bulkRequest.add(deleteRequest02); bulkRequest.add(deleteRequest03); // 修改 UpdateRequest updateRequest = new UpdateRequest("christy","user","10"); updateRequest.doc("{\"name\":\"煉石補(bǔ)天的女媧\"}",XContentType.JSON); bulkRequest.add(updateRequest); BulkResponse bulkResponse = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT); BulkItemResponse[] items = bulkResponse.getItems(); for (BulkItemResponse item : items) { System.out.println(item.status()); } }
在kibana中查詢結(jié)果
6.查詢文檔
@Test public void testSearch() throws IOException { //創(chuàng)建搜索對(duì)象 SearchRequest searchRequest = new SearchRequest("christy"); //搜索構(gòu)建對(duì)象 SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); searchSourceBuilder.query(QueryBuilders.matchAllQuery())//執(zhí)行查詢條件 .from(0)//起始條數(shù) .size(10)//每頁展示記錄 .postFilter(QueryBuilders.matchAllQuery()) //過濾條件 .sort("age", SortOrder.DESC);//排序 //創(chuàng)建搜索請(qǐng)求 searchRequest.types("user").source(searchSourceBuilder); SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT); System.out.println("符合條件的文檔總數(shù): "+searchResponse.getHits().getTotalHits()); System.out.println("符合條件的文檔最大得分: "+searchResponse.getHits().getMaxScore()); SearchHit[] hits = searchResponse.getHits().getHits(); for (SearchHit hit : hits) { System.out.println(hit.getSourceAsMap()); } }
ElasticSearchRepository方式
1.準(zhǔn)備工作
ElasticSearchRepository方式主要通過注解和對(duì)接口實(shí)現(xiàn)的方式來實(shí)現(xiàn)ES的操作,我們?cè)趯?shí)體類上通過注解配置ES索引的映射關(guān)系后,當(dāng)實(shí)現(xiàn)了ElasticSearchRepository接口的類第一次操作ES進(jìn)行插入文檔的時(shí)候,ES會(huì)自動(dòng)生成所需要的一切。但是該種方式無法實(shí)現(xiàn)高亮查詢,想要實(shí)現(xiàn)高亮查詢只能使用RestHighLevelClient
開始之前我們需要熟悉一下接口方式為我們提供的注解,以及編寫一些基礎(chǔ)的類
1.清空ES數(shù)據(jù)
2.了解注解
@Document
: 代表一個(gè)文檔記錄
indexName
: 用來指定索引名稱
type
: 用來指定索引類型
@Id
: 用來將對(duì)象中id和ES中_id映射
@Field
: 用來指定ES中的字段對(duì)應(yīng)Mapping
type
: 用來指定ES中存儲(chǔ)類型
analyzer
: 用來指定使用哪種分詞器
3.新建實(shí)體類
/** * 用在類上作用:將Emp的對(duì)象映射成ES中一條json格式文檔 * indexName: 用來指定這個(gè)對(duì)象的轉(zhuǎn)為json文檔存入那個(gè)索引中 要求:ES服務(wù)器中之前不能存在此索引名 * type : 用來指定在當(dāng)前這個(gè)索引下創(chuàng)建的類型名稱 * * @Author Christy * @Date 2021/4/29 21:22 */ @Data @Document(indexName = "christy",type = "user") public class User { @Id //用來將對(duì)象中id屬性與文檔中_id 一一對(duì)應(yīng) private String id; // 用在屬性上 代表mapping中一個(gè)屬性 一個(gè)字段 type:屬性 用來指定字段類型 analyzer:指定分詞器 @Field(type = FieldType.Text,analyzer = "ik_max_word") private String name; @Field(type = FieldType.Integer) private Integer age; @Field(type = FieldType.Date) @JsonFormat(pattern = "yyyy-MM-dd") private Date bir; @Field(type = FieldType.Text,analyzer = "ik_max_word") private String content; @Field(type = FieldType.Text,analyzer = "ik_max_word") private String address; }
4.UserRepository
/** * @Author Christy * @Date 2021/4/29 21:23 **/ public interface extends ElasticsearchRepository<User,String> { }
5.TestUserRepository
/** * @Author Christy * @Date 2021/4/29 21:51 **/ @SpringBootTest public class TestUserRepository { @Autowired private UserRepository userRepository; }
2.保存文檔
@Test public void testSaveAndUpdate(){ User user = new User(); // id初識(shí)為空,此操作為新增 user.setId(UUID.randomUUID().toString()); user.setName("唐三藏"); user.setBir(new Date()); user.setIntroduce("西方世界如來佛祖大弟子金蟬子轉(zhuǎn)世,十世修行的好人,得道高僧!"); user.setAddress("大唐白馬寺"); userRepository.save(user); }
3.修改文檔
@Test public void testSaveAndUpdate(){ User user = new User(); // 根據(jù)id修改信息 user.setId("1666eb47-0bbf-468b-ab45-07758c741461"); user.setName("唐三藏"); user.setBir(new Date()); user.setIntroduce("俗家姓陳,狀元之后。西方世界如來佛祖大弟子金蟬子轉(zhuǎn)世,十世修行的好人,得道高僧!"); user.setAddress("大唐白馬寺"); userRepository.save(user); }
4.刪除文檔
repository接口默認(rèn)提供了4種刪除方式,我們演示根據(jù)id進(jìn)行刪除
@Test public void deleteDoc(){ userRepository.deleteById("1666eb47-0bbf-468b-ab45-07758c741461"); }
5.檢索一條記錄
@Test public void testFindOne(){ Optional<User> optional = userRepository.findById("1666eb47-0bbf-468b-ab45-07758c741461"); System.out.println(optional.get()); }
6.查詢所有
@Test public void testFindAll(){ Iterable<User> all = userRepository.findAll(); all.forEach(user-> System.out.println(user)); }
7.排序
@Test public void testFindAllSort(){ Iterable<User> all = userRepository.findAll(Sort.by(Sort.Order.asc("age"))); all.forEach(user-> System.out.println(user)); }
8.分頁
@Test public void testFindPage(){ //PageRequest.of 參數(shù)1: 當(dāng)前頁-1 Page<User> search = userRepository.search(QueryBuilders.matchAllQuery(), PageRequest.of(1, 1)); search.forEach(user-> System.out.println(user)); }
9.自定義查詢
先給大家看一個(gè)表,是不是很暈_(¦3」∠)_
Keyword | Sample | Elasticsearch Query String |
---|---|---|
And |
findByNameAndPrice |
{"bool" : {"must" : [ {"field" : {"name" : "?"}}, {"field" : {"price" : "?"}} ]}} |
Or |
findByNameOrPrice |
{"bool" : {"should" : [ {"field" : {"name" : "?"}}, {"field" : {"price" : "?"}} ]}} |
Is |
findByName |
{"bool" : {"must" : {"field" : {"name" : "?"}}}} |
Not |
findByNameNot |
{"bool" : {"must_not" : {"field" : {"name" : "?"}}}} |
Between |
findByPriceBetween |
{"bool" : {"must" : {"range" : {"price" : {"from" : ?,"to" : ?,"include_lower" : true,"include_upper" : true}}}}} |
LessThanEqual |
findByPriceLessThan |
{"bool" : {"must" : {"range" : {"price" : {"from" : null,"to" : ?,"include_lower" : true,"include_upper" : true}}}}} |
GreaterThanEqual |
findByPriceGreaterThan |
{"bool" : {"must" : {"range" : {"price" : {"from" : ?,"to" : null,"include_lower" : true,"include_upper" : true}}}}} |
Before |
findByPriceBefore |
{"bool" : {"must" : {"range" : {"price" : {"from" : null,"to" : ?,"include_lower" : true,"include_upper" : true}}}}} |
After |
findByPriceAfter |
{"bool" : {"must" : {"range" : {"price" : {"from" : ?,"to" : null,"include_lower" : true,"include_upper" : true}}}}} |
Like |
findByNameLike |
{"bool" : {"must" : {"field" : {"name" : {"query" : "?*","analyze_wildcard" : true}}}}} |
StartingWith |
findByNameStartingWith |
{"bool" : {"must" : {"field" : {"name" : {"query" : "?*","analyze_wildcard" : true}}}}} |
EndingWith |
findByNameEndingWith |
{"bool" : {"must" : {"field" : {"name" : {"query" : "*?","analyze_wildcard" : true}}}}} |
Contains/Containing |
findByNameContaining |
{"bool" : {"must" : {"field" : {"name" : {"query" : "**?**","analyze_wildcard" : true}}}}} |
In |
findByNameIn (Collection<String>names) |
{"bool" : {"must" : {"bool" : {"should" : [ {"field" : {"name" : "?"}}, {"field" : {"name" : "?"}} ]}}}} |
NotIn |
findByNameNotIn (Collection<String>names) |
{"bool" : {"must_not" : {"bool" : {"should" : {"field" : {"name" : "?"}}}}}} |
Near |
findByStoreNear |
Not Supported Yet ! |
True |
findByAvailableTrue |
{"bool" : {"must" : {"field" : {"available" : true}}}} |
False |
findByAvailableFalse |
{"bool" : {"must" : {"field" : {"available" : false}}}} |
OrderBy |
findByAvailable TrueOrderByNameDesc |
{"sort" : [{ "name" : {"order" : "desc"} }],"bool" : {"must" : {"field" : {"available" : true}}}} |
這個(gè)表格看起來復(fù)雜,實(shí)際上也不簡單,但是確實(shí)很牛逼。我們只要按照上面的定義在接口中定義相應(yīng)的方法,無須寫實(shí)現(xiàn)就可實(shí)現(xiàn)我們想要的功能
舉個(gè)例子,上面有個(gè)findByName是下面這樣定義的
假如我們現(xiàn)在有個(gè)需求需要按照名字查詢用戶,我們可以在UserRepository
中定義一個(gè)方法,如下
// 根據(jù)姓名查詢 List<User> findByName(String name);
系統(tǒng)提供的查詢方法中findBy
是一個(gè)固定寫法,像上面我們定義的方法findByName
,其中Name是我們實(shí)體類中的屬性名,這個(gè)必須對(duì)應(yīng)上。也就是說這個(gè)findByName不僅僅局限于name
,還可以findByAddress、findByAge等等;
現(xiàn)在就拿findByName來講,我們要查詢名字叫唐三藏的用戶
@Test public void testFindByName(){ List<User> userList = userRepository.findByName("唐三藏"); userList.forEach(user-> System.out.println(user)); }
其實(shí)就是框架底層直接使用下面的命令幫我們實(shí)現(xiàn)的查詢
GET /christy/user/_search { "query": { "bool": { "must": [ { "term": { "name":"?" } } ] } } }
10.高亮查詢
我們上面說了,ElasticSearchRepository實(shí)現(xiàn)不了高亮查詢,想要實(shí)現(xiàn)高亮查詢還是需要使用RestHighLevelClient方式。最后我們使用rest clientl實(shí)現(xiàn)一次高亮查詢
@Test public void testHighLightQuery() throws IOException, ParseException { // 創(chuàng)建搜索請(qǐng)求 SearchRequest searchRequest = new SearchRequest("christy"); // 創(chuàng)建搜索對(duì)象 SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); searchSourceBuilder.query(QueryBuilders.termQuery("introduce", "唐僧")) // 設(shè)置查詢條件 .from(0) // 起始條數(shù)(當(dāng)前頁-1)*size的值 .size(10) // 每頁展示條數(shù) .sort("age", SortOrder.DESC) // 排序 .highlighter(new HighlightBuilder().field("*").requireFieldMatch(false).preTags("<span style='color:red;'>").postTags("</span>")); // 設(shè)置高亮 searchRequest.types("user").source(searchSourceBuilder); SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT); SearchHit[] hits = searchResponse.getHits().getHits(); List<User> userList = new ArrayList<>(); for (SearchHit hit : hits) { Map<String, Object> sourceAsMap = hit.getSourceAsMap(); User user = new User(); user.setId(hit.getId()); user.setAge(Integer.parseInt(sourceAsMap.get("age").toString())); user.setBir(new SimpleDateFormat("yyyy-MM-dd").parse(sourceAsMap.get("bir").toString())); user.setIntroduce(sourceAsMap.get("introduce").toString()); user.setName(sourceAsMap.get("name").toString()); user.setAddress(sourceAsMap.get("address").toString()); Map<String, HighlightField> highlightFields = hit.getHighlightFields(); if(highlightFields.containsKey("name")){ user.setName(highlightFields.get("name").fragments()[0].toString()); } if(highlightFields.containsKey("introduce")){ user.setIntroduce(highlightFields.get("introduce").fragments()[0].toString()); } if(highlightFields.containsKey("address")){ user.setAddress(highlightFields.get("address").fragments()[0].toString()); } userList.add(user); } userList.forEach(user -> System.out.println(user)); }
到此這篇關(guān)于ElasticSearch在springboot中使用的詳細(xì)教程的文章就介紹到這了,更多相關(guān)springboot使用ElasticSearch內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java通過百度API實(shí)現(xiàn)圖片車牌號(hào)識(shí)別
這段時(shí)間做項(xiàng)目需要用java程序進(jìn)行車牌識(shí)別,因此嘗試做了下這個(gè)程序,本代碼功能是通過調(diào)用百度API實(shí)現(xiàn)的,感興趣的可以了解一下2021-06-06java:找不到符號(hào)報(bào)錯(cuò)的排錯(cuò)方案舉例
當(dāng)你使用一個(gè)未定義或未導(dǎo)入的類時(shí),編譯器會(huì)報(bào)錯(cuò),下面這篇文章主要給大家介紹了關(guān)于java:找不到符號(hào)報(bào)錯(cuò)的排錯(cuò)方案,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-01-01AsyncHttpClient的ConnectionSemaphore方法源碼流程解讀
這篇文章主要為大家介紹了AsyncHttpClient的ConnectionSemaphore方法源碼流程解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12Java 實(shí)現(xiàn)跨平臺(tái)的操作方式
這篇文章主要介紹了Java 實(shí)現(xiàn)跨平臺(tái)的操作方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-09-09