前言
在上一篇學習SpringBoot中,整合了Mybatis、Druid和PageHelper並實現了多資料來源的操作。本篇主要是介紹和使用目前最火的搜尋引擎ElastiSearch,並和SpringBoot進行結合使用。
ElasticSearch介紹
ElasticSearch是一個基於Lucene的搜尋伺服器,其實就是對Lucene進行封裝,提供了 REST API 的操作介面 ElasticSearch作為一個高度可擴充的開源全文搜尋和分析引擎,可用於快速地對大資料進行儲存,搜尋和分析。 ElasticSearch主要特點:分散式、高可用、非同步寫入、多API、面向文件 。 ElasticSearch核心概念:近實時,叢集,節點(儲存資料),索引,分片(將索引分片),副本(分片可設定多個副本) 。它可以快速地儲存、搜尋和分析海量資料。 ElasticSearch使用案例:維基百科、Stack Overflow、Github 等等。
SpringBoot整合Elasticsearch
在使用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方法。
SpringData的使用
首先,在使用之前,先做好相關的準備。
Maven的配置如下:
<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>
複製程式碼
application.properties的配置
spring.data.elasticsearch.repositories.enabled = true
spring.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層
@Service
public 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服務端版本更改而對程式碼進行更改!
JestClient
首先在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();
serach1();
serach2();
serach3();
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 serach1() {
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 serach2() {
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 serach3() {
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的官方文件。
Windows安裝ElasticSearch
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的執行狀態!
其它
ElasticSearch官網API地址: https://www.elastic.co/guide/en/elasticsearch/client/java-api/2.3/index.html
JestClientGithub地址: https://github.com/searchbox-io/Jest
專案我放到github上面去了。 https://github.com/xuwujing/springBoot
如果覺得不錯,希望順便給個star。 到此,本文結束,謝謝閱讀。
版權宣告: 作者:虛無境 部落格園出處:http://www.cnblogs.com/xuwujing CSDN出處:http://blog.csdn.net/qazwsxpcm 個人部落格出處:http://www.panchengming.com 原創不易,轉載請標明出處,謝謝!