前言: 畢設項目還要求加了這個做大數據搜索,正好自己也比較感興趣,就一起來學習學習吧! Elasticsearch 簡介 Elasticsearch 是一個分散式、RESTful 風格的搜索和數據分析引擎,能夠解決不斷涌現出的各種用例。作為 Elastic Stack 的核心,它集中存儲您的數據,幫 ...
前言:畢設項目還要求加了這個做大數據搜索,正好自己也比較感興趣,就一起來學習學習吧!
Elasticsearch 簡介
Elasticsearch 是一個分散式、RESTful 風格的搜索和數據分析引擎,能夠解決不斷涌現出的各種用例。作為 Elastic Stack 的核心,它集中存儲您的數據,幫助您發現意料之中以及意料之外的情況。
查詢
保持好奇心。從數據中探尋各種問題的答案。
通過 Elasticsearch,您能夠執行及合併多種類型的搜索(結構化數據、非結構化數據、地理位置、指標),搜索方式隨心而變。先從一個簡單的問題出發,試試看能夠從中發現些什麼。
分析
大處著眼,全局在握。
找到與查詢最匹配的十個文檔是一回事。但如果面對的是十億行日誌,又該如何解讀呢?Elasticsearch 聚合讓您能夠從大處著眼,探索數據的趨勢和模式。
速度
可擴展性
彈性
靈活性
操作的樂趣
客戶端庫
使用您自己的編程語言與 Elasticsearch 進行交互
Elasticsearch 使用的是標準的 RESTful 風格的 API 和 JSON。此外,我們還構建和維護了很多其他語言的客戶端,例如 Java、Python、.NET、SQL 和 PHP。與此同時,我們的社區也貢獻了很多客戶端。這些客戶端使用起來簡單自然,而且就像 Elasticsearch 一樣,不會對您的使用方式進行限制。
Java:
RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(
new HttpHost("localhost", 9200, "http")));
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.matchAllQuery());
searchSourceBuilder.aggregation(AggregationBuilders.terms("top_10_states").field("state").size(10));
SearchRequest searchRequest = new SearchRequest();
searchRequest.indices("social-*");
searchRequest.source(searchSourceBuilder);
SearchResponse searchResponse = client.search(searchRequest);
盡享強大功能
HADOOP 和 SPRAK
照例來說應該是去扒官網,結果一搜就驚了,這官網也忒得勁兒了吧,竟然提供中文版本而且還有中文版本的文檔,友好友好,我看了好長一會兒才反應過來自己還有博客要寫.咳咳,上面的內容都是摘自官網順便附一個官網鏈接:https://www.elastic.co/cn/products/elasticsearch
另外還有一個關於 Elasticsearch 來源很有趣的故事在這裡分享一下:
回憶時光
許多年前,一個剛結婚的名叫 Shay Banon 的失業開發者,跟著他的妻子去了倫敦,他的妻子在那裡學習廚師。 在尋找一個賺錢的工作的時候,為了給他的妻子做一個食譜搜索引擎,他開始使用 Lucene 的一個早期版本。
直接使用 Lucene 是很難的,因此 Shay 開始做一個抽象層,Java 開發者使用它可以很簡單的給他們的程式添加搜索功能。 他發佈了他的第一個開源項目 Compass。
後來 Shay 獲得了一份工作,主要是高性能,分散式環境下的記憶體數據網格。這個對於高性能,實時,分散式搜索引擎的需求尤為突出, 他決定重寫 Compass,把它變為一個獨立的服務並取名 Elasticsearch。
第一個公開版本在2010年2月發佈,從此以後,Elasticsearch 已經成為了 Github 上最活躍的項目之一,他擁有超過300名 contributors(目前736名 contributors )。 一家公司已經開始圍繞 Elasticsearch 提供商業服務,並開發新的特性,但是,Elasticsearch 將永遠開源並對所有人可用。
據說,Shay 的妻子還在等著她的食譜搜索引擎…
安裝 Elasticsearch
官網最新版本 Elasticsearch (6.5.4),但是由於自己的環境使用最新版本的有問題(配合下麵的工具 Kibana 有問題..Kibana 啟動不了),所以不得不換成更低版本的 6.2.2,下載外鏈:戳這裡,當然你也可以試一下最新的版本:
順帶一提:在下載之前你應該確保你的 Java 版本保持在 1.8 及以上(就 1.8 吧..),這是 Elasticsearch 的硬性要求,可以自行打開命令行輸入
java -version
來查看 Java 的版本
下載完成後,可以看到是一個壓縮包,我們直接解壓在 D 盤上,然後打開 bin
目錄下的 elasticsearch.bat 文件
等待一段時間後,可以看到小黑框輸出一行 start ,就說明我們的 Elasticsearch 已經跑起來了,我們訪問地址:http://127.0.0.1:9200/
,看到返回一串 JSON 格式的代碼就說明已經成功了:
安裝 Kibana
這是一個官方推出的把 Elasticsearch 數據可視化的工具,官網在這裡:【傳送門】,不過我們現在暫時還用不到那些數據分析的東西,不過裡面有一個 Dev Tools 的工具可以方便的和 Elasticsearch 服務進行交互,去官網下載了最新版本的 Kibana(6.5.4) 結果不知道為什麼總是啟動不起來,所以換一了一個低版本的(6.2.2)正常,給個下載外鏈:下載點這裡,你們也可以去官網試試能不能把最新的跑起來:
解壓到 D 盤(意外的有點慢..),同樣打開目錄下的bin\kibana.bat
:
等待一段時間後就可以看到提示信息,運行在 5601 埠,我們訪問地址 http://localhost:5601/app/kibana#/dev_tools/console?_g=()
可以成功進入到 Dev-tools 界面:
點擊 【Get to work】,然後在控制台輸入 GET /_cat/health?v
查看伺服器狀態,可以在右側返回的結果中看到 green
即表示伺服器狀態目前是健康的:
快速入門
一、基礎概念-快速入門
節點 Node、集群 Cluster 和分片 Shards
ElasticSearch 是分散式資料庫,允許多台伺服器協同工作,每台伺服器可以運行多個實例。單個實例稱為一個節點(node),一組節點構成一個集群(cluster)。分片是底層的工作單元,文檔保存在分片內,分片又被分配到集群內的各個節點里,每個分片僅保存全部數據的一部分。
索引 Index、類型 Type 和文檔 Document
對比我們比較熟悉的 MySQL 資料庫:
index → db
type → table
document → row
如果我們要訪問一個文檔元數據應該包括囊括 index/type/id
這三種類型,很好理解。
二、使用 RESTful API 與 Elasticsearch 進行交互
所有其他語言可以使用 RESTful API 通過埠 9200 和 Elasticsearch 進行通信,你可以用你最喜愛的 web 客戶端訪問 Elasticsearch 。一個 Elasticsearch 請求和任何 HTTP 請求一樣由若幹相同的部件組成:
curl -X<VERB> '<PROTOCOL>://<HOST>:<PORT>/<PATH>?<QUERY_STRING>' -d '<BODY>'
被 < >
標記的部件:
部件名 | 作用 |
---|---|
VERB |
適當的 HTTP 方法 或 謂詞 : GET 、 POST 、 PUT 、 HEAD 或者 DELETE 。 |
PROTOCOL |
http 或者 https (如果你在 Elasticsearch 前面有一個 https 代理) |
HOST |
Elasticsearch 集群中任意節點的主機名,或者用 localhost 代表本地機器上的節點。 |
PORT |
運行 Elasticsearch HTTP 服務的埠號,預設是 9200 。 |
PATH |
API 的終端路徑(例如 _count 將返回集群中文檔數量)。Path 可能包含多個組件,例如:_cluster/stats 和 _nodes/stats/jvm 。 |
QUERY_STRING |
任意可選的查詢字元串參數 (例如 ?pretty 將格式化地輸出 JSON 返回值,使其更容易閱讀) |
BODY |
一個 JSON 格式的請求體 (如果請求需要的話) |
就比如計算集群中文檔的數量,我們可以用這個:
curl -XGET 'http://localhost:9200/_count?pretty' -d '
{
"query": {
"match_all": {}
}
}
'
不過對於安裝了 Kibana 的我們,可以直接在 Kibana 的控制台輸出以下語句,也是同樣的效果:
GET /_count?pretty
{
"query": {
"match_all": {}
}
}
文檔管理(CRUD)
如果對於 RESTful 不太熟悉的童鞋請右轉:【傳送門】
增加:
POST /db/user/1
{
"username": "wmyskxz1",
"password": "123456",
"age": "22"
}
POST /db/user/2
{
"username": "wmyskxz2",
"password": "123456",
"age": "22"
}
這一段代碼稍微解釋一下,這其實就往索引為 db
類型為 user
的資料庫中插入一條 id
為 1 的一條數據,這條數據其實就相當於一個擁有 username/password/age
三個屬性的一個實體,就是 JSON 數據
執行命令後,Elasticsearch 返回如下數據:
# POST /db/user/1
{
"_index": "db",
"_type": "user",
"_id": "1",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 2,
"_primary_term": 1
}
# POST /db/user/2
{
"_index": "db",
"_type": "user",
"_id": "2",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 1,
"_primary_term": 1
}
version
是版本號的意思,當我們執行操作會自動加 1
刪除:
DELETE /db/user/1
Elasticsearch 返回數據如下:
{
"_index": "db",
"_type": "user",
"_id": "1",
"_version": 2,
"result": "deleted",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 1,
"_primary_term": 1
}
這裡就可以看到 version
變成了 2
修改:
PUT /db/user/2
{
"username": "wmyskxz3",
"password": "123456",
"age": "22"
}
Elasticsearch 返回數據如下:
{
"_index": "db",
"_type": "user",
"_id": "2",
"_version": 2,
"result": "updated",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 2,
"_primary_term": 1
}
查詢:
GET /db/user/2
返回數據如下:
{
"_index": "db",
"_type": "user",
"_id": "2",
"_version": 2,
"found": true,
"_source": {
"username": "wmyskxz3",
"password": "123456",
"age": "22"
}
}
搜索
上面我們已經演示了基本的文檔 CRUD 功能,然而 Elasticsearch 的核心功能是搜索,所以在學習之前,為更好的演示這個功能,我們先往 Elasticsearch 中插入一些數據:
PUT /movies/movie/1
{
"title": "The Godfather",
"director": "Francis Ford Coppola",
"year": 1972,
"genres": [
"Crime",
"Drama"
]
}
PUT /movies/movie/2
{
"title": "Lawrence of Arabia",
"director": "David Lean",
"year": 1962,
"genres": [
"Adventure",
"Biography",
"Drama"
]
}
PUT /movies/movie/3
{
"title": "To Kill a Mockingbird",
"director": "Robert Mulligan",
"year": 1962,
"genres": [
"Crime",
"Drama",
"Mystery"
]
}
PUT /movies/movie/4
{
"title": "Apocalypse Now",
"director": "Francis Ford Coppola",
"year": 1979,
"genres": [
"Drama",
"War"
]
}
PUT /movies/movie/5
{
"title": "Kill Bill: Vol. 1",
"director": "Quentin Tarantino",
"year": 2003,
"genres": [
"Action",
"Crime",
"Thriller"
]
}
PUT /movies/movie/6
{
"title": "The Assassination of Jesse James by the Coward Robert Ford",
"director": "Andrew Dominik",
"year": 2007,
"genres": [
"Biography",
"Crime",
"Drama"
]
}
**_search端點**
現在已經把一些電影信息放入了索引,可以通過搜索看看是否可找到它們。 為了使用 ElasticSearch 進行搜索,我們使用 _search
端點,可選擇使用索引和類型。也就是說,按照以下模式向URL發出請求:<index>/<type>/_search
。其中,index
和 type
都是可選的。
換句話說,為了搜索電影,可以對以下任一URL進行POST請求:
- http://localhost:9200/_search - 搜索所有索引和所有類型。
- http://localhost:9200/movies/_search - 在電影索引中搜索所有類型
- http://localhost:9200/movies/movie/_search - 在電影索引中顯式搜索電影類型的文檔。
搜索請求正文和ElasticSearch查詢DSL
如果只是發送一個請求到上面的URL,我們會得到所有的電影信息。為了創建更有用的搜索請求,還需要向請求正文中提供查詢。 請求正文是一個JSON對象,除了其它屬性以外,它還要包含一個名稱為 “query”
的屬性,這就可使用ElasticSearch的查詢DSL。
{
"query": {
//Query DSL here
}
}
你可能想知道查詢DSL是什麼。它是ElasticSearch自己基於JSON的域特定語言,可以在其中表達查詢和過濾器。你可以把它簡單同SQL對應起來,就相當於是條件語句吧。
基本自由文本搜索:
查詢DSL具有一長列不同類型的查詢可以使用。 對於“普通”自由文本搜索,最有可能想使用一個名稱為“查詢字元串查詢”。
查詢字元串查詢是一個高級查詢,有很多不同的選項,ElasticSearch將解析和轉換為更簡單的查詢樹。如果忽略了所有的可選參數,並且只需要給它一個字元串用於搜索,它可以很容易使用。
現在嘗試在兩部電影的標題中搜索有“kill”這個詞的電影信息:
GET /_search
{
"query": {
"query_string": {
"query": "kill"
}
}
}
執行上面的請求並查看結果,如下所示 -
正如預期的,得到兩個命中結果,每個電影的標題中都帶有“kill”單詞。再看看另一種情況,在特定欄位中搜索。
指定搜索的欄位
在前面的例子中,使用了一個非常簡單的查詢,一個只有一個屬性 “query”
的查詢字元串查詢。 如前所述,查詢字元串查詢有一些可以指定設置,如果不使用,它將會使用預設的設置值。
這樣的設置稱為“fields”,可用於指定要搜索的欄位列表。如果不使用“fields”欄位,ElasticSearch查詢將預設自動生成的名為 “_all”
的特殊欄位,來基於所有文檔中的各個欄位匹配搜索。
為了做到這一點,修改以前的搜索請求正文,以便查詢字元串查詢有一個 fields
屬性用來要搜索的欄位數組:
GET /_search
{
"query": {
"query_string": {
"query": "ford",
"fields": [
"title"
]
}
}
}
執行上面查詢它,看看會有什麼結果(應該只匹配到 1 行數據):
正如預期的得到一個命中,電影的標題中的單詞“ford”。現在,從查詢中移除fields屬性,應該能匹配到 3 行數據:
過濾
前面已經介紹了幾個簡單的自由文本搜索查詢。現在來看看另一個示例,搜索 “drama”
,不明確指定欄位,如下查詢 -
GET /_search
{
"query": {
"query_string": {
"query": "drama"
}
}
}
因為在索引中有五部電影在 _all
欄位(從類別欄位)中包含單詞 “drama”
,所以得到了上述查詢的 5
個命中。 現在,想象一下,如果我們想限制這些命中為只是 1962
年發佈的電影。要做到這點,需要應用一個過濾器,要求 “year”
欄位等於 1962
。要添加過濾器,修改搜索請求正文,以便當前的頂級查詢(查詢字元串查詢)包含在過濾的查詢中:
{
"query": {
"filtered": {
"query": {
"query_string": {
"query": "drama"
}
},
"filter": {
//Filter to apply to the query
}
}
}
}
過濾的查詢是具有兩個屬性(query
和filter
)的查詢。執行時,它使用過濾器過濾查詢的結果。要完成這樣的查詢還需要添加一個過濾器,要求year
欄位的值為1962
。
ElasticSearch查詢DSL有各種各樣的過濾器可供選擇。對於這個簡單的情況,某個欄位應該匹配一個特定的值,一個條件過濾器就能很好地完成工作。
"filter": {
"term": { "year": 1962 }
}
完整的搜索請求如下所示:
GET /_search
{
"query": {
"filtered": {
"query": {
"query_string": {
"query": "drama"
}
},
"filter": {
"term": {
"year": 1962
}
}
}
}
}
當執行上面請求,只得到兩個命中,這個兩個命中的數據的 year
欄位的值都是等於 1962
。
無需查詢即可進行過濾
在上面的示例中,使用過濾器限制查詢字元串查詢的結果。如果想要做的是應用一個過濾器呢? 也就是說,我們希望所有電影符合一定的標準。
在這種情況下,我們仍然在搜索請求正文中使用 “query”
屬性。但是,我們不能只是添加一個過濾器,需要將它包裝在某種查詢中。
一個解決方案是修改當前的搜索請求,替換查詢字元串 query
過濾查詢中的 match_all
查詢,這是一個查詢,只是匹配一切。類似下麵這個:
GET /_search
{
"query": {
"filtered": {
"query": {
"match_all": {}
},
"filter": {
"term": {
"year": 1962
}
}
}
}
}
另一個更簡單的方法是使用常數分數查詢:
GET /_search
{
"query": {
"constant_score": {
"filter": {
"term": {
"year": 1962
}
}
}
}
}
參考文章:Elasticsearch入門教程、Elasticsearch官方文檔、 ElasticSearch 快速上手學習入門教程
三、集成 SpringBoot 簡單示例
第一步:新建 SpringBoot 項目
pom包依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Elasticsearch支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
application.properties:
spring.data.elasticsearch.cluster-nodes=127.0.0.1:9300
第二步:新建實體類
User類:
@Document(indexName = "users", type = "user")
public class User {
private int id;
private String username;
private String password;
private int age;
/** getter and setter */
}
第三步:Dao 層
UserDao:
import com.wmyskxz.demo.domain.User;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
public interface UserDao extends ElasticsearchRepository<User, Integer> {
}
第四步:Controller 層
這裡緊緊是為了演示,所以就省略 service 層,當然 CRUD 不能少:
@RestController
public class UserController {
@Autowired
UserDao userDao;
@PostMapping("/addUser")
public String addUser(String username, String password, Integer age) {
User user = new User();
user.setUsername(username);
user.setPassword(password);
user.setAge(age);
return String.valueOf(userDao.save(user).getId());// 返回id做驗證
}
@DeleteMapping("/deleteUser")
public String deleteUser(Integer id) {
userDao.deleteById(id);
return "Success!";
}
@PutMapping("/updateUser")
public String updateUser(Integer id, String username, String password, Integer age) {
User user = new User();
user.setId(id);
user.setUsername(username);
user.setPassword(password);
user.setAge(age);
return String.valueOf(userDao.save(user).getId());// 返回id做驗證
}
@GetMapping("/getUser")
public User getUser(Integer id) {
return userDao.findById(id).get();
}
@GetMapping("/getAllUsers")
public Iterable<User> getAllUsers() {
return userDao.findAll();
}
}
第五步:測試
使用 REST 測試工具測試沒有問題,過程我就不給了..bingo!
總結
其實使用 SpringBoot 來操作 Elasticsearch 的話使用方法有點類似 JPA 了,而且完全可以把 Elasticsearch 當做 SQL 伺服器來用,也沒有問題...在各種地方看到了各個大大特別是官方,都快把 Elasticsearch 這款工具吹上天了,對於它方便的集成這一點我倒是有感受,關於速度這方面還沒有很深的感受,慢慢來吧...
按照慣例黏一個尾巴:
歡迎轉載,轉載請註明出處!
簡書ID:@我沒有三顆心臟
github:wmyskxz
歡迎關註公眾微信號:wmyskxz
分享自己的學習 & 學習資料 & 生活
想要交流的朋友也可以加qq群:3382693