您好,登錄后才能下訂單哦!
本篇主要是介紹和使用目前最火的搜索引擎ElastiSearch,并和SpringBoot進行結合使用。
ElasticSearch是一個基于Lucene的搜索服務器,其實就是對Lucene進行封裝,提供了 REST API 的操作接口?ElasticSearch作為一個高度可拓展的開源全文搜索和分析引擎,可用于快速地對大數據進行存儲,搜索和分析。
ElasticSearch主要特點:分布式、高可用、異步寫入、多API、面向文檔 。
ElasticSearch核心概念:近實時,集群,節點(保存數據),索引,分片(將索引分片),副本(分片可設置多個副本) 。它可以快速地儲存、搜索和分析海量數據。
ElasticSearch使用案例:維基百科、Stack Overflow、Github 等等。
在使用SpringBoot整合Elasticsearch?之前,我們應該了解下它們之間對應版本的關系。
Spring Boot Version (x) | Spring Data Elasticsearch Version (y) | Elasticsearch Version (z) |
---|---|---|
x <= 1.3.5 | y <= 1.3.4 | z <= 1.7.2* |
x >= 1.4.x | 2.0.0 <=y < 5.0.0** | 2.0.0 <= z < 5.0.0** |
這里我們使用的SpringBoot的版本是1.5.9,Elasticsearch的版本是2.3.5。
使用SpringBoot整合Elasticsearch,一般都是使用?SpringData?進行封裝的,然后再dao層接口繼承ElasticsearchRepository?類,該類實現了很多的方法,比如常用的CRUD方法。
首先,在使用之前,先做好相關的準備。
<dependency> ????????????<groupId>org.springframework.boot</groupId> ????????????<artifactId>spring-boot-starter-web</artifactId> ?????????????<version>1.5.9.RELEASE</version> ????????</dependency> ??<dependency> ????????????<groupId>org.springframework.boot</groupId> ????????????<artifactId>spring-boot-starter-data-elasticsearch</artifactId> ?????????????<version>1.5.9.RELEASE</version> ????????</dependency>
spring.data.elasticsearch.repositories.enabled?=?truespring.data.elasticsearch.cluster-nodes?=127.0.0.1\:9300
注: 9300 是 Java 客戶端的端口。9200 是支持 Restful HTTP 的接口。
更多的配置:
spring.data.elasticsearch.cluster-name?Elasticsearch?集群名。(默認值:?elasticsearch)spring.data.elasticsearch.cluster-nodes?集群節點地址列表,用逗號分隔。如果沒有指定,就啟動一個客戶端節點。spring.data.elasticsearch.propertie?用來配置客戶端的額外屬性。spring.data.elasticsearch.repositories.enabled?開啟?Elasticsearch?倉庫。(默認值:true。)
實體類
@Document(indexName?=?"userindex",?type?=?"user") public?class?User?implements?Serializable{?????/**?????*??????*/ ????private?static?final?long?serialVersionUID?=?1L;????/**?編號?*/ ?????private?Long?id;?????/**?姓名?*/ ?????private?String?name;????? ?????/**?年齡?*/ ?????private?Integer?age;????? ?????/**?描述?*/?? ?????private?String?description;????? ?????/**?創建時間?*/ ?????private?String?createtm;????//?getter和setter?略}
使用SpringData的時候,它需要在實體類中設置indexName?和type?,如果和傳統型數據庫比較的話,就相當于庫和表。需要注意的是indexName和type都必須是小寫!!!
dao層
public?interface?UserDao?extends?ElasticsearchRepository<User,?Long>{}
dao層這里就比較簡單了,只需繼承ElasticsearchRepository該類就行了。其中主要的方法就是 save、delete和search。其中save方法相當如insert和update,沒有就新增,有就覆蓋。delete方法主要就是刪除數據以及索引庫。至于search就是查詢了,包括一些常用的查詢,如分頁、權重之類的。
Service層
@Servicepublic?class?UserServiceImpl?implements?UserService?{????@Autowired ????private?UserDao?userDao;????@Override ????public?boolean?insert(User?user)?{????????boolean?falg=false;????????try{ ????????????userDao.save(user); ????????????falg=true; ????????}catch(Exception?e){ ????????????e.printStackTrace(); ????????}????????return?falg; ????}????@Override ????public?List<User>?search(String?searchContent)?{ ??????????QueryStringQueryBuilder?builder?=?new?QueryStringQueryBuilder(searchContent); ??????????System.out.println("查詢的語句:"+builder); ??????????Iterable<User>?searchResult?=?userDao.search(builder); ??????????Iterator<User>?iterator?=?searchResult.iterator(); ??????????List<User>?list=new?ArrayList<User>();??????????while?(iterator.hasNext())?{ ????????????list.add(iterator.next()); ??????????}???????return?list; ????}???? ???? ???? ????@Override ????public?List<User>?searchUser(Integer?pageNumber,?Integer?pageSize,String?searchContent)?{?????????//?分頁參數 ????????Pageable?pageable?=?new?PageRequest(pageNumber,?pageSize); ????????QueryStringQueryBuilder?builder?=?new?QueryStringQueryBuilder(searchContent); ????????SearchQuery?searchQuery?=?new?NativeSearchQueryBuilder().withPageable(pageable).withQuery(builder).build(); ????????System.out.println("查詢的語句:"?+?searchQuery.getQuery().toString()); ????????Page<User>?searchPageResults?=?userDao.search(searchQuery);????????return?searchPageResults.getContent(); ????}???? ????@Override ????public?List<User>?searchUserByWeight(String?searchContent)?{?????//?根據權重進行查詢 ????????FunctionScoreQueryBuilder?functionScoreQueryBuilder?=?QueryBuilders.functionScoreQuery() ????????????????.add(QueryBuilders.boolQuery().should(QueryBuilders.matchQuery("name",?searchContent)), ????????????????????ScoreFunctionBuilders.weightFactorFunction(10)) ????????????????.add(QueryBuilders.boolQuery().should(QueryBuilders.matchQuery("description",?searchContent)), ????????????????????????ScoreFunctionBuilders.weightFactorFunction(100)).setMinScore(2); ????????System.out.println("查詢的語句:"?+?functionScoreQueryBuilder.toString()); ????????Iterable<User>?searchResult?=?userDao.search(functionScoreQueryBuilder); ????????Iterator<User>?iterator?=?searchResult.iterator(); ????????List<User>?list=new?ArrayList<User>();????????while?(iterator.hasNext())?{ ????????????list.add(iterator.next()); ????????}????????return?list; ????} }
這里我就簡單的寫了幾個方法,其中主要的方法是查詢。查詢包括全文搜索,分頁查詢和權重查詢。其中需要說明的是權重查詢這塊,權重的分值越高,查詢的結果也越靠前,如果沒有對其它的數據設置分值,它們默認的分值就是1,如果不想查詢這些語句,只需使用setMinScore將其設為大于1即可。
代碼測試
調用接口進行添加數據
新增數據:
POST?http://localhost:8086/api/user{"id":1,"name":"張三","age":20,"description":"張三是個Java開發工程師","createtm":"2018-4-25?11:07:42"} {"id":2,"name":"李四","age":24,"description":"李四是個測試工程師","createtm":"1980-2-15?19:01:32"} {"id":3,"name":"王五","age":25,"description":"王五是個運維工程師","createtm":"2016-8-21?06:11:32"}
進行全文查詢
請求
http://localhost:8086/api/user?searchContent=工程師
返回
[{"id":2,"name":"李四","age":14,"description":"李四是個測試工程師","createtm":?"1980-2-15?19:01:32"}, {"id":1,"name":"張三","age":20,"description":"張三是個Java開發工程師",?"createtm":?"2018-4-25?11:07:42"}, {"id":3,"name":"王五","age":25,"description":"王五是個運維工程師","createtm":?"2016-8-21?06:11:32"}]
進行分頁查詢
請求
http://localhost:8086/api/user?pageNumber=0&pageSize=2&searchContent=工程師
返回
[{"id":2,"name":"李四","age":14,"description":"李四是個測試工程師"},{"id":1,"name":"張三","age":20,"description":"張三是個Java開發工程師"}]
進行權重查詢
請求
http://localhost:8086/api/user2?searchContent=李四
返回
[{"id":2,"name":"李四","age":24,"description":"李四是個測試工程師","createtm":"1980-2-15?19:01:32"}]
權重查詢打印的語句:
查詢的語句:{{??"function_score"?:?{????"functions"?:?[?{??????"filter"?:?{????????"bool"?:?{??????????"should"?:?{????????????"match"?:?{??????????????"name"?:?{????????????????"query"?:?"李四",????????????????"type"?:?"boolean" ??????????????} ????????????} ??????????} ????????} ??????},??????"weight"?:?10.0 ????},?{??????"filter"?:?{????????"bool"?:?{??????????"should"?:?{????????????"match"?:?{??????????????"description"?:?{????????????????"query"?:?"李四",????????????????"type"?:?"boolean" ??????????????} ????????????} ??????????} ????????} ??????},??????"weight"?:?100.0 ????}?],????"min_score"?:?2.0 ??} }
注:測試中,因為設置了setMinScore最小權重分為2的,所以無關的數據是不會顯示出來的。如果想顯示的話,在代碼中去掉即可。
新增完數據之后,可以在瀏覽器輸入:http://localhost:9200/_plugin/head/
然后點擊基本查詢,便可以查看添加的數據。如果想用語句查詢,可以將程序中控制臺打印的查詢語句粘貼到查詢界面上進行查詢!
注:這里的ElasticSearch是我在windows上安裝的,并安裝了ES插件head,具體安裝步驟在文章末尾。
除了SpringData之外,其實還有其它的方法操作ElasticSearch的。
比如使用原生ElasticSearch的Api,使用TransportClient類實現。
或者使用由Spring封裝,只需在Service層,進行注入Bean即可。
示例:
@Autowired ?ElasticsearchTemplate?elasticsearchTemplate;
但是,上述方法中都有其局限性,也就是隨著ElasticSearch的版本變更,相關的Java API也在做不斷的調整,就是ElasticSearch服務端版本進行更改之后,客戶端的代碼可能需要重新編寫。
因此介紹一個相當好用的第三方工具JestClient,它對ElasticSearch進行封裝,填補了?ElasticSearch?HttpRest接口 客戶端的空白,它適用于ElasticSearch2.x以上的版本,無需因為ElasticSearch服務端版本更改而對代碼進行更改!
首先在Maven中添加如下依賴:
????<dependency> ????????<groupId>io.searchbox</groupId>? ?????????<artifactId>jest</artifactId> ????????<version>5.3.3</version> ????</dependency>
然后編寫相關的測試代碼。
代碼中的注釋應該很完整,所以這里就不再對代碼過多的講述了。
import?java.util.ArrayList;import?java.util.List;import?org.elasticsearch.index.query.QueryBuilders;import?org.elasticsearch.search.builder.SearchSourceBuilder;import?com.pancm.pojo.User;import?io.searchbox.client.JestClient;import?io.searchbox.client.JestClientFactory;import?io.searchbox.client.JestResult;import?io.searchbox.client.config.HttpClientConfig;import?io.searchbox.core.Bulk;import?io.searchbox.core.BulkResult;import?io.searchbox.core.Delete;import?io.searchbox.core.DocumentResult;import?io.searchbox.core.Index;import?io.searchbox.core.Search;import?io.searchbox.indices.CreateIndex;import?io.searchbox.indices.DeleteIndex;import?io.searchbox.indices.mapping.GetMapping;import?io.searchbox.indices.mapping.PutMapping;public?class?JestTest?{?? ????????private?static?JestClient?jestClient;?? ????????private?static?String?indexName?=?"userindex";?? //??????private?static?String?indexName?=?"userindex2";?? ????????private?static?String?typeName?=?"user";?? ????????private?static?String?elasticIps="http://192.169.2.98:9200";//??????private?static?String?elasticIps="http://127.0.0.1:9200"; ???????? ???????? ????????public?static?void?main(String[]?args)?throws?Exception?{ ????????????jestClient?=?getJestClient();?? ????????????insertBatch(); ????????????serach2(); ????????????serach3(); ????????????serach4(); ????????????jestClient.close();?? ???????????? ????????}???????? ????????private?static??JestClient?getJestClient()?{?? ????????????JestClientFactory?factory?=?new?JestClientFactory();?? ????????????factory.setHttpClientConfig(new?HttpClientConfig.Builder(elasticIps).connTimeout(60000).readTimeout(60000).multiThreaded(true).build());?? ????????????return?factory.getObject();?? ????????}?? ???????? ????????public?static?void?insertBatch()?{ ????????????List<Object>?objs?=?new?ArrayList<Object>(); ????????????objs.add(new?User(1L,?"張三",?20,?"張三是個Java開發工程師","2018-4-25?11:07:42")); ????????????objs.add(new?User(2L,?"李四",?24,?"李四是個測試工程師","1980-2-15?19:01:32")); ????????????objs.add(new?User(3L,?"王五",?25,?"王五是個運維工程師","2016-8-21?06:11:32"));????????????boolean?result?=?false;????????????try?{ ????????????????result?=?insertBatch(jestClient,indexName,?typeName,objs); ????????????}?catch?(Exception?e)?{ ????????????????e.printStackTrace(); ????????????} ????????????System.out.println("批量新增:"+result); ????????}???????? ???????? ????????/** ?????????*?全文搜索 ?????????*/ ????????public?static?void?serach2()?{ ????????????String?query?="工程師";????????????try?{ ????????????????SearchSourceBuilder?searchSourceBuilder?=?new?SearchSourceBuilder();? ?????????????????searchSourceBuilder.query(QueryBuilders.queryStringQuery(query));? ?????????????????//分頁設置 ?????????????????searchSourceBuilder.from(0).size(2);? ????????????????System.out.println("全文搜索查詢語句:"+searchSourceBuilder.toString()); ????????????????System.out.println("全文搜索返回結果:"+search(jestClient,indexName,?typeName,?searchSourceBuilder.toString())); ????????????}?catch?(Exception?e)?{ ????????????????e.printStackTrace(); ????????????} ????????}???????? ????????/** ?????????*?精確搜索 ?????????*/ ????????public?static?void?serach3()?{????????????try?{ ????????????????SearchSourceBuilder?searchSourceBuilder?=?new?SearchSourceBuilder();? ????????????????searchSourceBuilder.query(QueryBuilders.termQuery("age",?24));? ????????????????System.out.println("精確搜索查詢語句:"+searchSourceBuilder.toString()); ????????????????System.out.println("精確搜索返回結果:"+search(jestClient,indexName,?typeName,?searchSourceBuilder.toString())); ????????????}?catch?(Exception?e)?{ ????????????????e.printStackTrace(); ????????????} ????????}???????? ???????? ????????/** ?????????*?區間搜索 ?????????*/ ????????public?static?void?serach4()?{ ????????????String?createtm="createtm"; ????????????String?from="2016-8-21?06:11:32"; ????????????String?to="2018-8-21?06:11:32";???????????? ????????????try?{ ????????????????SearchSourceBuilder?searchSourceBuilder?=?new?SearchSourceBuilder();? ????????????????searchSourceBuilder.query(QueryBuilders.rangeQuery(createtm).gte(from).lte(to));? ????????????????System.out.println("區間搜索語句:"+searchSourceBuilder.toString()); ????????????????System.out.println("區間搜索返回結果:"+search(jestClient,indexName,?typeName,?searchSourceBuilder.toString())); ????????????}?catch?(Exception?e)?{ ????????????????e.printStackTrace(); ????????????} ????????}???????? ???????? ????????/** ?????????*?創建索引 ?????????*?@param?indexName ?????????*?@return ?????????*?@throws?Exception ?????????*/ ????????public?boolean?createIndex(JestClient?jestClient,String?indexName)?throws?Exception?{?? ????????????JestResult?jr?=?jestClient.execute(new?CreateIndex.Builder(indexName).build());?? ????????????return?jr.isSucceeded();?? ????????}?? ?????????? ????????/** ?????????*?新增數據 ?????????*?@param?indexName ?????????*?@param?typeName ?????????*?@param?source ?????????*?@return ?????????*?@throws?Exception ?????????*/ ????????public?boolean?insert(JestClient?jestClient,String?indexName,?String?typeName,?String?source)?throws?Exception?{?? ????????????PutMapping?putMapping?=?new?PutMapping.Builder(indexName,?typeName,?source).build();?? ????????????JestResult?jr?=?jestClient.execute(putMapping);?? ????????????return?jr.isSucceeded();?? ????????}?? ?????????? ???????? ?????????/** ??????????*?查詢數據 ??????????*?@param?indexName ??????????*?@param?typeName ??????????*?@return ??????????*?@throws?Exception ??????????*/ ????????public?static?String?getIndexMapping(JestClient?jestClient,String?indexName,?String?typeName)?throws?Exception?{?? ????????????GetMapping?getMapping?=?new?GetMapping.Builder().addIndex(indexName).addType(typeName).build();?? ????????????JestResult?jr?=jestClient.execute(getMapping);?? ????????????return?jr.getJsonString();?? ?????????}?? ?????????? ???????? ???????? ???????/** ????????*?批量新增數據 ????????*?@param?indexName ????????*?@param?typeName ????????*?@param?objs ????????*?@return ????????*?@throws?Exception ????????*/ ????????public?static?boolean?insertBatch(JestClient?jestClient,String?indexName,?String?typeName,?List<Object>?objs)?throws?Exception?{?? ????????????Bulk.Builder?bulk?=?new?Bulk.Builder().defaultIndex(indexName).defaultType(typeName);?? ????????????for?(Object?obj?:?objs)?{?? ????????????????Index?index?=?new?Index.Builder(obj).build();?? ?????????????????bulk.addAction(index);?? ????????????}?? ????????????BulkResult?br?=?jestClient.execute(bulk.build());?? ????????????return?br.isSucceeded();?? ???????????}?? ?????????? ????????/** ?????????*?全文搜索 ?????????*?@param?indexName ?????????*?@param?typeName ?????????*?@param?query ?????????*?@return ?????????*?@throws?Exception ?????????*/ ????????public?static?String?search(JestClient?jestClient,String?indexName,?String?typeName,?String?query)?throws?Exception?{?? ?????????????Search?search?=?new?Search.Builder(query) ?????????????.addIndex(indexName) ?????????????.addType(typeName)?? ?????????????.build();? ????????????JestResult?jr?=?jestClient.execute(search);?? //??????????System.out.println("--"+jr.getJsonString());//??????????System.out.println("--"+jr.getSourceAsObject(User.class)); ????????????return?jr.getSourceAsString();?? ?????????}?? ?????????? ?????????? ???????? ??????? ?????????? ???????/** ????????*?刪除索引 ????????*?@param?indexName ????????*?@return ????????*?@throws?Exception ????????*/ ????????public?boolean?delete(JestClient?jestClient,String?indexName)?throws?Exception?{?? ????????????JestResult?jr?=?jestClient.execute(new?DeleteIndex.Builder(indexName).build());?? ????????????return?jr.isSucceeded();?? ????????}?? ?????????? ???????/** ????????*?刪除數據 ????????*?@param?indexName ????????*?@param?typeName ????????*?@param?id ????????*?@return ????????*?@throws?Exception ????????*/ ????????public?boolean?delete(JestClient?jestClient,String?indexName,?String?typeName,?String?id)?throws?Exception?{?? ????????????DocumentResult?dr?=?jestClient.execute(new?Delete.Builder(id).index(indexName).type(typeName).build());?? ????????????return?dr.isSucceeded();?? ????????}
注:測試之前先說明下,本地windows系統安裝的是ElasticSearch版本是2.3.5,linux服務器上安裝的ElasticSearch版本是6.2。
全文搜索
全文搜索查詢語句:{??"from"?:?0,??"size"?:?2,??"query"?:?{????"query_string"?:?{??????"query"?:?"工程師" ????} ??} } 全文搜索返回結果:{"id":1,"name":"張三","age":20,"description":"張三是個Java開發工程師","createtm":"2018-4-25?11:07:42"},{"id":2,"name":"李四","age":24,"description":"李四是個測試工程師","createtm":"1980-2-15?19:01:32"}
匹配搜索
精確搜索查詢語句:{??"query"?:?{????"term"?:?{??????"age"?:?24 ????} ??} } 精確搜索返回結果:{"id":2,"name":"李四","age":24,"description":"李四是個測試工程師","createtm":"1980-2-15?19:01:32"}
時間區間搜索
區間搜索語句:{??"query"?:?{????"range"?:?{??????"createtm"?:?{????????"from"?:?"2016-8-21?06:11:32",????????"to"?:?"2018-8-21?06:11:32",????????"include_lower"?:?true,????????"include_upper"?:?true ??????} ????} ??} } 區間搜索返回結果:{"id":1,"name":"張三","age":20,"description":"張三是個Java開發工程師","createtm":"2018-4-25?11:07:42"}
新增完數據之后,我們可以上linux的?Kibana中進行相關的查詢,查詢結果如下:
注:Kibana 是屬于ELK中一個開源軟件。Kibana可以為 Logstash 和 ElasticSearch 提供的日志分析友好的 Web 界面,可以幫助匯總、分析和搜索重要數據日志。
上述代碼中測試返回的結果符合我們的預期。其中關于JestClient只是用到了很少的一部分,更多的使用可以查看JestClient的官方文檔。
1,文件準備
下載地址:
https://www.elastic.co/downloads
選擇ElasticSearch相關版本, 然后選擇后綴名為ZIP文件進行下載,下載之后進行解壓。
2,啟動Elasticsearch
進入bin目錄下,運行 elasticsearch.bat
然后在瀏覽上輸入: localhost:9200
成功顯示一下界面表示成功!
3,安裝ES插件
web管理界面head 安裝
進入bin目錄下,打開cmd,進入dos界面
輸入:plugin install mobz/elasticsearch-head
進行下載
成功下載之后,在瀏覽器輸入:http://localhost:9200/_plugin/head/
若顯示一下界面,則安裝成功!
4,注冊服務
進入bin目錄下,打開cmd,進入dos界面
依次輸入:
service.bat install
service.bat start
成功之后,再輸入
services.msc
跳轉到Service服務界面,可以直接查看es的運行狀態!
歡迎工作一到五年的Java工程師朋友們加入Java技術交流:659270626
群內提供免費的Java架構學習資料(里面有高可用、高并發、高性能及分布式、Jvm性能調優、Spring源碼,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多個知識點的架構資料)合理利用自己每一分每一秒的時間來學習提升自己,不要再用"沒有時間“來掩飾自己思想上的懶惰!趁年輕,使勁拼,給未來的自己一個交代!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。