1. 程式人生 > >基於Springboot技術的部落格系統實踐及應用之四(Elasticsearch)

基於Springboot技術的部落格系統實踐及應用之四(Elasticsearch)

本部落格從全文搜尋、ES簡介、ES核心概念、ES與SpringBoot整合以及ES實戰共五個方面進行詳細介紹和應用。

1、全文搜尋介紹

全文搜尋搜尋的物件主要有兩種:

1、結構化資料:具有固定格式或固定長度的資料,例如,資料庫,元資料

2、非結構化資料:無固定格式或者無固定長度的資料,例如:Word,圖片等

非結構化資料的檢索主要方法:

1、順序掃描法(Serial Scanning):從頭到尾一個字一個字的匹配資料,例如:Window作業系統中的檔案都是採用這種方式

這種掃描法適合使用小檔案的資料

2、全文搜尋(Full-text Search):就是把非結構化的資料重新抽取出來,部分變成結構化資料,來建立索引,實現全文檢索

索引:是全文搜尋的關鍵,如何抽取非結構化資料中的關鍵字,抽取哪些關鍵字,是建立索引的關鍵。

全文搜尋就是一種將檔案中所有文字與搜尋項匹配的文字資料檢索方法。

全文搜尋實現原理,一共分四個步驟:

1、首先建立文字庫(搜尋源) 2、第二步是建立索引(就是規則) 3、執行搜尋(一般情況下由使用者發起,由系統解析請求) 4、過濾結果(比如:根據規劃展示不同的內容,或者採用分頁的形式過濾,或者過濾掉敏感內容)

基於JAVA的開源實現有哪些:

1、Lucene  2、ElasticSearch:是基於Lucene而設計,建立的  3、Solr 

ES與Solr進行對比:

  (1)二者安裝都很簡單。

  (2)Solr 利用 Zookeeper 進行分散式管理,而 Elasticsearch 自身帶有分散式協調管理功能。

  (3)Solr 支援更多格式的資料,比如JSON、XML、CSV,而 Elasticsearch 僅支援json檔案格式。

  (4)Solr 官方提供的功能更多,而 Elasticsearch 本身更注重於核心功能,高階功能多有第三方外掛提供

  (5)Solr 在傳統的搜尋應用中表現好於 Elasticsearch,但在處理實時搜尋應用時效率明顯低於 Elasticsearch。

  (6)Solr 是傳統搜尋應用的有力解決方案,但 Elasticsearch 更適用於新興的實時搜尋應用。

2、ElasticSearch簡介

1、高度可擴充套件的開源全文搜尋和分析引擎

2、快速地、近實時地對大資料進行儲存、探索和分析

3、用來支援有複雜的資料搜尋需求的企業級應用

ES特點:

1、分散式:ES中的索引是由N個分片組成,分片分佈到不同的伺服器中 2、高可用:由於分散式造就了高可用 3、多型別 4、多API 5、面向文件 6、非同步寫入 7、近實時 8、基於Lucene 9、Apache協議:開源協調

3、ElasticSearch核心概念

近實時:就是搜尋文件到產生結果,由一個小的延遲,大概1秒左右(Lucene是實時全文搜尋),而ES是需要重新整理索引,佔用大概1秒時間

叢集:是一個或多個節點的集合,是用來儲存應用的全部資料,並提供基於節點的索引和搜尋功能,每一個叢集要有一個唯一的名稱,節點加入叢集是根據叢集名稱來的。

節點:就是單臺的伺服器,是用唯一識別符號來標識。

索引:相似文件的一個集合,是用來快速搜尋資料。

型別:就是索引中包含一些文件的一個細分,比如:產品相關的文件,有不同型別的文件,這些就是產品的型別

文件:就是最小的單位,對於非結構化資料來說,文件就是一個檔案,對於結構化資料來說,就是指資料表中的一行資料,一般用Json來表示

分片 :就是儲存索引的部分資料,就是說每個索引都可以有多個分片,同時也有多個副本。

副本:分片可以設定不同的副本,設定副本的目的是提高系統的高可用性;增加副本能增加吞吐量,把負載能分配到不同的副本中。

4、ElasticSearch與SpringBoot整合

需要的配置環境 :

1、ElasticSearch 2.4.4 

2、Spring Data ElasticSearch 2.1.3 Release

3、JNA 4.3.0

新增依賴:

兩個依賴:spring-boot-starter-data-elasticsearch 和 jna

5、ES實戰

一、在application.properties中增加ES配置類

修改application.properties

有兩項配置:1、配置ES伺服器的地址 2、配置傳輸超時時間

二、後臺編碼

後臺編碼需要做工作有如下(以搜尋部落格內容為例):

第一:先定義一個文件類:EsBlog

第二:先定義一資源庫:EsBlogRepository 這個資源庫其實就是Dao層

第三:測試測試庫介面,編寫測試類

第四:編寫控制器:BlogContorller

建立文件類EsBlog程式碼如下:

package com.waylau.spring.boot.elasticsearch.domain;

import java.io.Serializable;

import javax.xml.bind.annotation.XmlRootElement;

import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;


@Document(indexName = "blog", type = "blog", shards = 1, replicas = 0, refreshInterval = "-1")
@XmlRootElement // MediaType 轉為 XML
public class Blog implements Serializable {
 
	private static final long serialVersionUID = 1L;

	@Id  // 主鍵
	private String id; // 使用者的唯一標識

	private String title;
 
	private String content;

	protected Blog() {  // JPA 的規範要求無參建構函式;設為 protected 防止直接使用 
	}

	public Blog(String name, String content) {
		this.title = name;
		this.content = content;
	}
	
	public Blog(String id, String name, String content) {
		this.id = id;
		this.title = name;
		this.content = content;
	}
	
	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public String getContent() {
		return content;
	}

	public void setContent(String content) {
		this.content = content;
	}
 
	@Override
    public String toString() {
        return String.format(
                "User[id=%d, title='%s', content='%s']",
                id, title, content);
    }
}

資源類:BlogRepository 編碼如下:

package com.waylau.spring.boot.elasticsearch.repository;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;

import com.waylau.spring.boot.elasticsearch.domain.Blog;

public interface BlogRepository extends ElasticsearchRepository<Blog, String> {
	/**
	 * 根據使用者名稱分頁查詢使用者列表
	 * @param name
	 * @param pageable
	 * @return
	 */
	Page<Blog> findByTitleLikeOrContentLike(String title, String content, Pageable pageable);
}

編寫測試類:

package com.waylau.spring.boot.elasticsearch.repository;

import static org.assertj.core.api.Assertions.assertThat;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.test.context.junit4.SpringRunner;

import com.waylau.spring.boot.elasticsearch.domain.Blog;


/**
 * BlogRepository 測試類
 */
@RunWith(SpringRunner.class)
@SpringBootTest
public class BlogRepositoryTest {
	
	@Autowired
    private BlogRepository blogRepository;
	
	@Test
	public void testFindByTitleLikeOrContentLike() {
		// 清空所有
		blogRepository.deleteAll();
		
		blogRepository.save(new Blog("1","老衛跟你談談安裝 Elasticsearch",
				"關於如何來安裝 Elasticsearch,這個請看我的部落格 https://waylau.com"));
		blogRepository.save(new Blog("2","老衛跟你談談 Elasticsearch 的幾個用法",
				"關於如何來用 Elasticsearch,還是得看我的部落格 https://waylau.com,妹"));  // 關鍵字"妹"
		blogRepository.save(new Blog("3","老衛和你一起學 Elasticsearch",
				"如何來學習 Elasticsearch,最終看我的部落格 https://waylau.com,酒")); // 關鍵字"酒"
		
		blogRepository.save(new Blog("4","03-05 用大白話聊聊分散式系統",
				"一提起“分散式系統”,大家的第一感覺就是好高大上啊,深不可測"));
		blogRepository.save(new Blog("5","02-19 Thymeleaf 3 引入了新的解析系統",
				"如果你的程式碼使用了 HTML5 的標準,而Thymeleaf 版本來停留在 2.x ,那麼如果沒有把閉合"));  
		blogRepository.save(new Blog("6","02-19 使用 GFM Eclipse 外掛時,不在專案裡面生成 HTML 檔案",
				"GFM 是 GitHub Flavored Markdown Viewer 的簡稱,是一款對 GitHub 友好的 Markdown 編輯器 。"));  
		
		Pageable pageable = new PageRequest(0, 20);
		Page<Blog> page = blogRepository.findByTitleLikeOrContentLike("妹", "酒", pageable);
		assertThat(page.getTotalElements()).isEqualTo(2);
	}
}

編寫controller,程式碼如下:

package com.waylau.spring.boot.elasticsearch.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.waylau.spring.boot.elasticsearch.domain.Blog;
import com.waylau.spring.boot.elasticsearch.repository.BlogRepository;

/**
 * Blog 控制器
 */
@RestController
@RequestMapping("/blogs")
public class BlogController {
	
	@Autowired
    private BlogRepository blogRepository;
	
	@GetMapping
	public List<Blog> list(@RequestParam(value="title",required=false,defaultValue="") String title,
			@RequestParam(value="content",required=false,defaultValue="") String content,
			@RequestParam(value="pageIndex",required=false,defaultValue="0") int pageIndex,
			@RequestParam(value="pageSize",required=false,defaultValue="10") int pageSize) {
			
		// 資料在 Test 裡面先初始化了,這裡只管取資料
		Pageable pageable = new PageRequest(pageIndex, pageSize);
		Page<Blog> page = blogRepository.findByTitleLikeOrContentLike(title, content, pageable);
		
		return page.getContent();
	}
 
}

到此ES實戰結束,啟動ES,就可以直接進行測試了。