您好,登錄后才能下訂單哦!
本篇主要是介紹和使用目前最火的搜索引擎ElastiSearch,并和SpringBoot進(jìn)行結(jié)合使用。
ElasticSearch是一個基于Lucene的搜索服務(wù)器,其實就是對Lucene進(jìn)行封裝,提供了 REST API 的操作接口?ElasticSearch作為一個高度可拓展的開源全文搜索和分析引擎,可用于快速地對大數(shù)據(jù)進(jìn)行存儲,搜索和分析。
ElasticSearch主要特點:分布式、高可用、異步寫入、多API、面向文檔 。
ElasticSearch核心概念:近實時,集群,節(jié)點(保存數(shù)據(jù)),索引,分片(將索引分片),副本(分片可設(shè)置多個副本) 。它可以快速地儲存、搜索和分析海量數(shù)據(jù)。
ElasticSearch使用案例:維基百科、Stack Overflow、Github 等等。
在使用SpringBoot整合Elasticsearch?之前,我們應(yīng)該了解下它們之間對應(yīng)版本的關(guān)系。
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?進(jìn)行封裝的,然后再dao層接口繼承ElasticsearchRepository?類,該類實現(xiàn)了很多的方法,比如常用的CRUD方法。
首先,在使用之前,先做好相關(guān)的準(zhǔn)備。
<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?集群名。(默認(rèn)值:?elasticsearch)spring.data.elasticsearch.cluster-nodes?集群節(jié)點地址列表,用逗號分隔。如果沒有指定,就啟動一個客戶端節(jié)點。spring.data.elasticsearch.propertie?用來配置客戶端的額外屬性。spring.data.elasticsearch.repositories.enabled?開啟?Elasticsearch?倉庫。(默認(rèn)值: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;????? ?????/**?創(chuàng)建時間?*/ ?????private?String?createtm;????//?getter和setter?略}
使用SpringData的時候,它需要在實體類中設(shè)置indexName?和type?,如果和傳統(tǒng)型數(shù)據(jù)庫比較的話,就相當(dāng)于庫和表。需要注意的是indexName和type都必須是小寫!!!
dao層
public?interface?UserDao?extends?ElasticsearchRepository<User,?Long>{}
dao層這里就比較簡單了,只需繼承ElasticsearchRepository該類就行了。其中主要的方法就是 save、delete和search。其中save方法相當(dāng)如insert和update,沒有就新增,有就覆蓋。delete方法主要就是刪除數(shù)據(jù)以及索引庫。至于search就是查詢了,包括一些常用的查詢,如分頁、權(quán)重之類的。
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)?{?????????//?分頁參數(shù) ????????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)?{?????//?根據(jù)權(quán)重進(jìn)行查詢 ????????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; ????} }
這里我就簡單的寫了幾個方法,其中主要的方法是查詢。查詢包括全文搜索,分頁查詢和權(quán)重查詢。其中需要說明的是權(quán)重查詢這塊,權(quán)重的分值越高,查詢的結(jié)果也越靠前,如果沒有對其它的數(shù)據(jù)設(shè)置分值,它們默認(rèn)的分值就是1,如果不想查詢這些語句,只需使用setMinScore將其設(shè)為大于1即可。
代碼測試
調(diào)用接口進(jìn)行添加數(shù)據(jù)
新增數(shù)據(jù):
POST?http://localhost:8086/api/user{"id":1,"name":"張三","age":20,"description":"張三是個Java開發(fā)工程師","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"}
進(jìn)行全文查詢
請求
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開發(fā)工程師",?"createtm":?"2018-4-25?11:07:42"}, {"id":3,"name":"王五","age":25,"description":"王五是個運維工程師","createtm":?"2016-8-21?06:11:32"}]
進(jìn)行分頁查詢
請求
http://localhost:8086/api/user?pageNumber=0&pageSize=2&searchContent=工程師
返回
[{"id":2,"name":"李四","age":14,"description":"李四是個測試工程師"},{"id":1,"name":"張三","age":20,"description":"張三是個Java開發(fā)工程師"}]
進(jìn)行權(quán)重查詢
請求
http://localhost:8086/api/user2?searchContent=李四
返回
[{"id":2,"name":"李四","age":24,"description":"李四是個測試工程師","createtm":"1980-2-15?19:01:32"}]
權(quán)重查詢打印的語句:
查詢的語句:{{??"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 ??} }
注:測試中,因為設(shè)置了setMinScore最小權(quán)重分為2的,所以無關(guān)的數(shù)據(jù)是不會顯示出來的。如果想顯示的話,在代碼中去掉即可。
新增完數(shù)據(jù)之后,可以在瀏覽器輸入:http://localhost:9200/_plugin/head/
然后點擊基本查詢,便可以查看添加的數(shù)據(jù)。如果想用語句查詢,可以將程序中控制臺打印的查詢語句粘貼到查詢界面上進(jìn)行查詢!
注:這里的ElasticSearch是我在windows上安裝的,并安裝了ES插件head,具體安裝步驟在文章末尾。
除了SpringData之外,其實還有其它的方法操作ElasticSearch的。
比如使用原生ElasticSearch的Api,使用TransportClient類實現(xiàn)。
或者使用由Spring封裝,只需在Service層,進(jìn)行注入Bean即可。
示例:
@Autowired ?ElasticsearchTemplate?elasticsearchTemplate;
但是,上述方法中都有其局限性,也就是隨著ElasticSearch的版本變更,相關(guān)的Java API也在做不斷的調(diào)整,就是ElasticSearch服務(wù)端版本進(jìn)行更改之后,客戶端的代碼可能需要重新編寫。
因此介紹一個相當(dāng)好用的第三方工具JestClient,它對ElasticSearch進(jìn)行封裝,填補了?ElasticSearch?HttpRest接口 客戶端的空白,它適用于ElasticSearch2.x以上的版本,無需因為ElasticSearch服務(wù)端版本更改而對代碼進(jìn)行更改!
首先在Maven中添加如下依賴:
????<dependency> ????????<groupId>io.searchbox</groupId>? ?????????<artifactId>jest</artifactId> ????????<version>5.3.3</version> ????</dependency>
然后編寫相關(guān)的測試代碼。
代碼中的注釋應(yīng)該很完整,所以這里就不再對代碼過多的講述了。
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開發(fā)工程師","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));? ?????????????????//分頁設(shè)置 ?????????????????searchSourceBuilder.from(0).size(2);? ????????????????System.out.println("全文搜索查詢語句:"+searchSourceBuilder.toString()); ????????????????System.out.println("全文搜索返回結(jié)果:"+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("精確搜索返回結(jié)果:"+search(jestClient,indexName,?typeName,?searchSourceBuilder.toString())); ????????????}?catch?(Exception?e)?{ ????????????????e.printStackTrace(); ????????????} ????????}???????? ???????? ????????/** ?????????*?區(qū)間搜索 ?????????*/ ????????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("區(qū)間搜索語句:"+searchSourceBuilder.toString()); ????????????????System.out.println("區(qū)間搜索返回結(jié)果:"+search(jestClient,indexName,?typeName,?searchSourceBuilder.toString())); ????????????}?catch?(Exception?e)?{ ????????????????e.printStackTrace(); ????????????} ????????}???????? ???????? ????????/** ?????????*?創(chuàng)建索引 ?????????*?@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();?? ????????}?? ?????????? ????????/** ?????????*?新增數(shù)據(jù) ?????????*?@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();?? ????????}?? ?????????? ???????? ?????????/** ??????????*?查詢數(shù)據(jù) ??????????*?@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();?? ?????????}?? ?????????? ???????? ???????? ???????/** ????????*?批量新增數(shù)據(jù) ????????*?@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();?? ????????}?? ?????????? ???????/** ????????*?刪除數(shù)據(jù) ????????*?@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系統(tǒng)安裝的是ElasticSearch版本是2.3.5,linux服務(wù)器上安裝的ElasticSearch版本是6.2。
全文搜索
全文搜索查詢語句:{??"from"?:?0,??"size"?:?2,??"query"?:?{????"query_string"?:?{??????"query"?:?"工程師" ????} ??} } 全文搜索返回結(jié)果:{"id":1,"name":"張三","age":20,"description":"張三是個Java開發(fā)工程師","createtm":"2018-4-25?11:07:42"},{"id":2,"name":"李四","age":24,"description":"李四是個測試工程師","createtm":"1980-2-15?19:01:32"}
匹配搜索
精確搜索查詢語句:{??"query"?:?{????"term"?:?{??????"age"?:?24 ????} ??} } 精確搜索返回結(jié)果:{"id":2,"name":"李四","age":24,"description":"李四是個測試工程師","createtm":"1980-2-15?19:01:32"}
時間區(qū)間搜索
區(qū)間搜索語句:{??"query"?:?{????"range"?:?{??????"createtm"?:?{????????"from"?:?"2016-8-21?06:11:32",????????"to"?:?"2018-8-21?06:11:32",????????"include_lower"?:?true,????????"include_upper"?:?true ??????} ????} ??} } 區(qū)間搜索返回結(jié)果:{"id":1,"name":"張三","age":20,"description":"張三是個Java開發(fā)工程師","createtm":"2018-4-25?11:07:42"}
新增完數(shù)據(jù)之后,我們可以上linux的?Kibana中進(jìn)行相關(guān)的查詢,查詢結(jié)果如下:
注:Kibana 是屬于ELK中一個開源軟件。Kibana可以為 Logstash 和 ElasticSearch 提供的日志分析友好的 Web 界面,可以幫助匯總、分析和搜索重要數(shù)據(jù)日志。
上述代碼中測試返回的結(jié)果符合我們的預(yù)期。其中關(guān)于JestClient只是用到了很少的一部分,更多的使用可以查看JestClient的官方文檔。
1,文件準(zhǔn)備
下載地址:
https://www.elastic.co/downloads
選擇ElasticSearch相關(guān)版本, 然后選擇后綴名為ZIP文件進(jìn)行下載,下載之后進(jìn)行解壓。
2,啟動Elasticsearch
進(jìn)入bin目錄下,運行 elasticsearch.bat
然后在瀏覽上輸入: localhost:9200
成功顯示一下界面表示成功!
3,安裝ES插件
web管理界面head 安裝
進(jìn)入bin目錄下,打開cmd,進(jìn)入dos界面
輸入:plugin install mobz/elasticsearch-head
進(jìn)行下載
成功下載之后,在瀏覽器輸入:http://localhost:9200/_plugin/head/
若顯示一下界面,則安裝成功!
4,注冊服務(wù)
進(jìn)入bin目錄下,打開cmd,進(jìn)入dos界面
依次輸入:
service.bat install
service.bat start
成功之后,再輸入
services.msc
跳轉(zhuǎn)到Service服務(wù)界面,可以直接查看es的運行狀態(tài)!
歡迎工作一到五年的Java工程師朋友們加入Java技術(shù)交流:659270626
群內(nèi)提供免費的Java架構(gòu)學(xué)習(xí)資料(里面有高可用、高并發(fā)、高性能及分布式、Jvm性能調(diào)優(yōu)、Spring源碼,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多個知識點的架構(gòu)資料)合理利用自己每一分每一秒的時間來學(xué)習(xí)提升自己,不要再用"沒有時間“來掩飾自己思想上的懶惰!趁年輕,使勁拼,給未來的自己一個交代!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。