Elasticsearch 是一個分佈式的 RESTful 風格的搜索和數據分析引擎。
- 查詢 : Elasticsearch 允許執行和合並多種類型的搜索 — 結構化、非結構化、地理位置、度量指標 — 搜索方式隨心而變。
- 分析 : 找到與查詢最匹配的十個文檔是一回事。但是如果面對的是十億行日誌,又該如何解讀呢?Elasticsearch 聚合讓您能夠從大處着眼,探索數據的趨勢和模式。
- 速度 : Elasticsearch 很快。真的,真的很快。
- 可擴展性 : 可以在筆記本電腦上運行。 也可以在承載了 PB 級數據的成百上千臺服務器上運行。
- 彈性 : Elasticsearch 運行在一個分佈式的環境中,從設計之初就考慮到了這一點。
- 靈活性 : 具備多個案例場景。數字、文本、地理位置、結構化、非結構化。所有的數據類型都歡迎。
- HADOOP & SPARK : Elasticsearch + Hadoop
準備開始
Elasticsearch是一個高度可伸縮的開源全文搜索和分析引擎。它允許您快速和接近實時地存儲、搜索和分析大量數據。
這裏有一些使用Elasticsearch的用例:
- 你經營一個網上商店,你允許你的顧客搜索你賣的產品。在這種情況下,您可以使用Elasticsearch來存儲整個產品目錄和庫存,併爲它們提供搜索和自動完成建議。
- 你希望收集日誌或事務數據,並希望分析和挖掘這些數據,以查找趨勢、統計、彙總或異常。在這種情況下,你可以使用loghide (Elasticsearch/ loghide /Kibana堆棧的一部分)來收集、聚合和解析數據,然後讓loghide將這些數據輸入到Elasticsearch中。一旦數據在Elasticsearch中,你就可以運行搜索和聚合來挖掘你感興趣的任何信息。
- 你運行一個價格警報平臺,允許精通價格的客戶指定如下規則:“我有興趣購買特定的電子設備,如果下個月任何供應商的產品價格低於X美元,我希望得到通知”。在這種情況下,你可以抓取供應商的價格,將它們推入到Elasticsearch中,並使用其反向搜索(Percolator)功能來匹配價格走勢與客戶查詢,並最終在找到匹配後將警報推送給客戶。
- 你有分析/業務智能需求,並希望快速調查、分析、可視化,並對大量數據提出特別問題(想想數百萬或數十億的記錄)。在這種情況下,你可以使用Elasticsearch來存儲數據,然後使用Kibana (Elasticsearch/ loghide /Kibana堆棧的一部分)來構建自定義儀表板,以可視化對您來說很重要的數據的各個方面。此外,還可以使用Elasticsearch聚合功能對數據執行復雜的業務智能查詢。
基本概念
Near Realtime (NRT)
Elasticsearch是一個近乎實時的搜索平臺。這意味着從索引文檔到可以搜索的時間只有輕微的延遲(通常是1秒)。
Cluster
集羣是一個或多個節點(服務器)的集合,它們共同保存你的整個數據,並提供跨所有節點的聯合索引和搜索功能。一個集羣由一個唯一的名稱標識,默認這個唯一標識的名稱是"elasticsearch"。這個名稱很重要,因爲如果節點被設置爲按其名稱加入集羣,那麼節點只能是集羣的一部分。
確保不要在不同的環境中用相同的集羣名稱,否則可能導致節點加入到錯誤的集羣中。例如,你可以使用"logging-dev", "logging-test", "logging-prod"分別用於開發、測試和正式集羣的名字。
Node
節點是一個單獨的服務器,它是集羣的一部分,存儲數據,並參與集羣的索引和搜索功能。就像集羣一樣,節點由一個名稱來標識,默認情況下,該名稱是在啓動時分配給節點的隨機通用唯一標識符(UUID)。如果不想用默認的節點名,可以定義任何想要的節點名。這個名稱對於管理來說很重要,因爲你希望識別網絡中的哪些服務器對應於你的Elasticsearch集羣中的哪些節點。
一個節點可以通過配置集羣名稱來加入到一個特定的集羣中。默認情況下,每個節點都被設置加入到一個名字叫"elasticsearch"的集羣中,這就意味着如果你啓動了很多個節點,並且假設它們彼此可以互相發現,那麼它們將自動形成並加入到一個名爲"elasticsearch"的集羣中。
一個集羣可以有任意數量的節點。此外,如果在你的網絡上當前沒有運行任何節點,那麼此時啓動一個節點將默認形成一個單節點的名字叫"elasticsearch"的集羣。
Index
索引是具有某種相似特徵的文檔的集合。例如,你可以有一個顧客數據索引,產品目錄索引和訂單數據索引。索引有一個名稱(必須是小寫的)標識,該名稱用於在對其中的文檔執行索引、搜索、更新和刪除操作時引用索引。
Document
文檔是可以被索引的基本信息單元。文檔用JSON表示。
Shards & Replicas
一個索引可能存儲大量數據,這些數據可以超過單個節點的硬件限制。例如,一個包含10億條文檔佔用1TB磁盤空間的索引可能不適合在單個節點上,或者可能太慢而不能單獨處理來自單個節點的搜索請求。
爲了解決這個問題,Elasticsearch提供了將你的索引細分爲多個碎片(或者叫分片)的能力。在創建索引時,可以簡單地定義所需的分片數量。每個分片本身就是一個功能完全獨立的“索引”,可以駐留在集羣中的任何節點上。
分片之所以重要,主要有兩個原因:
- 它允許你水平地分割/擴展內容卷
- 它允許你跨分片(可能在多個節點上)分佈和並行操作,從而提高性能和吞吐量
在一個網絡/雲環境中隨時都有可能出現故障,強烈推薦你有一個容災機制。Elasticsearch允許你將一個或者多個索引分片複製到其它地方,這被稱之爲副本。
複製之所以重要,有兩個主要原因:
- 它提供了在一個shard/node失敗是的高可用性。出於這個原因,很重要的一個點是一個副本從來不會被分配到與它複製的原始分片相同節點上。也就是說,副本是放到另外的節點上的。
- 它允許擴展搜索量/吞吐量,因爲搜索可以在所有副本上並行執行。
總而言之,每個索引都可以分割成多個分片。索引也可以被複制零(意味着沒有副本)或更多次。一旦被複制,每個索引都將具有主分片(被複制的原始分片)和副本分片(主分片的副本)。在創建索引時,可以爲每個索引定義分片和副本的數量。創建索引後,您可以隨時動態地更改副本的數量,但不能更改事後分片的數量。
在默認情況下,Elasticsearch中的每個索引都分配了5個主分片和1個副本,這意味着如果集羣中至少有兩個節點,那麼索引將有5個主分片和另外5個副本分片(PS:這5個副本分片組成1個完整副本),每個索引總共有10個分片。
(畫外音:副本是針對索引而言的,同時需要注意索引和節點數量沒有關係,我們說2個副本指的是索引被複制了2次,而1個索引可能由5個分片組成,那麼在這種情況下,集羣中的分片數應該是 5 × (1 + 2) = 15 )
安裝
默認root用戶下操作,其他用戶請在命令前添加sudo。
- 安裝JDK。
yum -y install java-1.8.0-openjdk |
2. wget –c https://mirrors.huaweicloud.com/elasticsearch/7.2.1/elasticsearch-7.2.1-x86_64.rpm
rpm -ivh elasticsearch-7.2.1-x86_64.rpm
3、啓動Elasticsearch並設置開機啓動
|
systemctl start elasticsearch |
檢查Elasticsearch是否正在運行:
curl http://localhost:9200/
集羣健康
請求:
curl -X GET "localhost:9200/_cat/health?v"
響應:
epoch timestamp cluster status node.total node.data shards pri relo init unassign pending_tasks max_task_wait_time active_shards_percent
1533625274 15:01:14 elasticsearch green 1 1 0 0 0 0 0 0 - 100.0%
我們可以看到,我們命名爲“elasticsearch”的集羣現在是green狀態。
無論何時我們請求集羣健康時,我們會得到green, yellow, 或者 red 這三種狀態。
- Green : everything is good(一切都很好)(所有功能正常)
- Yellow : 所有數據都是可用的,但有些副本還沒有分配(所有功能正常)
- Red : 有些數據不可用(部分功能正常)
從上面的響應中我們可以看到,集羣"elasticsearch"總共有1個節點,0個分片因爲還沒有數據。
下面看一下集羣的節點列表:
請求:
curl -X GET "localhost:9200/_cat/nodes?v"
響應:
ip heap.percent ram.percent cpu load_1m load_5m load_15m node.role master name
127.0.0.1 15 53 0 0.03 0.03 0.05 mdi * Px524Ts
可以看到集羣中只有一個節點,它的名字是“Px524Ts”
查看全部索引
請求:
curl -X GET "localhost:9200/_cat/indices?v"
響應:
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size
上面的輸出意味着:我們在集羣中沒有索引
創建一個索引
現在,我們創建一個名字叫“customer”的索引,然後查看索引:
請求:
curl -X PUT "localhost:9200/customer?pretty"
(畫外音:pretty的意思是響應(如果有的話)以JSON格式返回)
響應:
{
"acknowledged" : true,
"shards_acknowledged" : true,
"index" : "customer"
}
請求:
curl -X GET "localhost:9200/_cat/indices?v"
響應:
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size
yellow open customer rG5fxdruTNmD-bdYIF5zOg 5 1 0 0 1.1kb 1.1kb
結果的第二行告訴我們,我們現在有叫"customer"的索引,並且他有5個主分片和1個副本(默認是1個副本),有0個文檔。
可能你已經注意到這個"customer"索引的健康狀態是yellow。回想一下我們之前的討論,yellow意味着一些副本(尚未)被分配。
之所以會出現這種情況,是因爲Elasticsearch默認情況下爲這個索引創建了一個副本。由於目前我們只有一個節點在運行,所以直到稍後另一個節點加入集羣時,纔會分配一個副本(對於高可用性)。一旦該副本分配到第二個節點上,該索引的健康狀態將變爲green。
索引並查詢一個文檔
現在,讓我們put一些數據到我們的"customer"索引:
請求:
curl -X PUT "localhost:9200/customer/_doc/1?pretty" -H 'Content-Type: application/json' -d'{"name": "John Doe"}'
響應:
{
"_index" : "customer",
"_type" : "_doc",
"_id" : "1",
"_version" : 1,
"result" : "created",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 0,
"_primary_term" : 1
}
從上面的響應可以看到,我們在"customer"索引下成功創建了一個文檔。這個文檔還有一個內部id爲1,這是我們在創建的時候指定的。
需要注意的是,Elasticsearch並不要求你在索引文檔之前就先創建索引,然後才能將文檔編入索引。在前面的示例中,如果事先不存在"customer"索引,Elasticsearch將自動創建"customer"索引。
(畫外音:也就是說,在新建文檔的時候如果指定的索引不存在則會自動創建相應的索引)
現在,讓我重新檢索這個文檔:
請求:
curl -X GET "localhost:9200/customer/_doc/1?pretty"
響應:
{
"_index" : "customer",
"_type" : "_doc",
"_id" : "1",
"_version" : 1,
"found" : true,
"_source" : {
"name" : "John Doe"
}
}
可以看到除了"found"字段外沒什麼不同,"_source"字段返回了一個完整的JSON文檔。
刪除一個索引
現在,讓我們刪除前面創建的索引,然後查看全部索引
請求:
curl -X DELETE "localhost:9200/customer?pretty"
響應:
{
"acknowledged" : true
}
接下來,查看一下
curl -X GET "localhost:9200/_cat/indices?v"
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size
到現在爲止,我們已經學習了創建/刪除索引、索引/查詢文檔這四個命令
curl -X PUT "localhost:9200/customer"
curl -X PUT "localhost:9200/customer/_doc/1" -H 'Content-Type: application/json' -d'{"name": "John Doe"}'
curl -X GET "localhost:9200/customer/_doc/1"
curl -X DELETE "localhost:9200/customer"
如果我們仔細研究上面的命令,我們實際上可以看到如何在Elasticsearch中訪問數據的模式。這種模式可以概括如下:
<REST Verb> /<Index>/<Type>/<ID>
修改數據
更新文檔
事實上,每當我們執行更新時,Elasticsearch就會刪除舊文檔,然後索引一個新的文檔。
下面這個例子展示瞭如何更新一個文檔(ID爲1),改變name字段爲"Jane Doe",同時添加一個age字段:
請求:
curl -X POST "localhost:9200/customer/_doc/1/_update?pretty" -H 'Content-Type: application/json' -d'
{
"doc": { "name": "Jane Doe", "age": 20 }
}
'
響應:
{
"_index" : "customer",
"_type" : "_doc",
"_id" : "1",
"_version" : 2,
"result" : "updated",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 1,
"_primary_term" : 1
}
下面這個例子用腳本來將age增加5
請求:
curl -X POST "localhost:9200/customer/_doc/1/_update?pretty" -H 'Content-Type: application/json' -d'
{
"script" : "ctx._source.age += 5"
}
'
在上面例子中,ctx._source引用的是當前源文檔
響應:
{
"_index" : "customer",
"_type" : "_doc",
"_id" : "1",
"_version" : 3,
"result" : "updated",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 2,
"_primary_term" : 1
}
刪除文檔
刪除文檔相當簡單。這個例子展示瞭如何從"customer"索引中刪除ID爲2的文檔:
請求:
curl -X DELETE "localhost:9200/customer/_doc/2?pretty"
響應:
{
"_index" : "customer",
"_type" : "_doc",
"_id" : "2",
"_version" : 1,
"result" : "not_found",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 0,
"_primary_term" : 1
}
批處理
除了能夠索引、更新和刪除單個文檔之外,Elasticsearch還可以使用_bulk API批量執行上述任何操作。
這個功能非常重要,因爲它提供了一種非常有效的機制,可以在儘可能少的網絡往返的情況下儘可能快地執行多個操作。
下面的例子,索引兩個文檔(ID 1 - John Doe 和 ID 2 - Jane Doe)
請求:
curl -X POST "localhost:9200/customer/_doc/_bulk?pretty" -H 'Content-Type: application/json' -d'
{"index":{"_id":"1"}}
{"name": "John Doe" }
{"index":{"_id":"2"}}
{"name": "Jane Doe" }
'
響應:
{
"took" : 5,
"errors" : false,
"items" : [
{
"index" : {
"_index" : "customer",
"_type" : "_doc",
"_id" : "1",
"_version" : 4,
"result" : "updated",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 3,
"_primary_term" : 1,
"status" : 200
}
},
{
"index" : {
"_index" : "customer",
"_type" : "_doc",
"_id" : "2",
"_version" : 1,
"result" : "created",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 1,
"_primary_term" : 1,
"status" : 201
}
}
]
}
接下來的例子展示了,更新第一個文檔(ID爲1),刪除第二個文檔(ID爲2):
請求:
curl -X POST "localhost:9200/customer/_doc/_bulk?pretty" -H 'Content-Type: application/json' -d'
{"update":{"_id":"1"}}
{"doc": { "name": "John Doe becomes Jane Doe" } }
{"delete":{"_id":"2"}}
'
響應:
{
"took" : 8,
"errors" : false,
"items" : [
{
"update" : {
"_index" : "customer",
"_type" : "_doc",
"_id" : "1",
"_version" : 5,
"result" : "updated",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 4,
"_primary_term" : 1,
"status" : 200
}
},
{
"delete" : {
"_index" : "customer",
"_type" : "_doc",
"_id" : "2",
"_version" : 2,
"result" : "deleted",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 2,
"_primary_term" : 1,
"status" : 200
}
}
]
}
現在,我們來重新查看一下索引文檔
curl -X GET "localhost:9200/customer/_doc/1?pretty"
檢索數據
示例數據
現在我們已經瞭解了基礎知識,讓我們嘗試處理一個更真實的數據集。我準備了一個關於客戶銀行賬戶信息的虛構JSON文檔示例。每個文檔都有以下格式:
{
"account_number": 0,
"balance": 16623,
"firstname": "Bradshaw",
"lastname": "Mckenzie",
"age": 29,
"gender": "F",
"address": "244 Columbus Place",
"employer": "Euron",
"email": "[email protected]",
"city": "Hobucken",
"state": "CO"
}
加載示例數據
你可以從這裏下載示例數據
提取它到我們的當前目錄,並且加載到我們的集羣中:
新建一個文件accounts.json,然後將數據複製粘貼到該文件中,保存退出
在這個accounts.json文件所在目錄下執行如下命令:
curl -H "Content-Type: application/json" -XPOST "localhost:9200/bank/_doc/_bulk?pretty&refresh" --data-binary "@accounts.json"
此時,accounts.json中的文檔數據便被索引到"bank"索引下
讓我們查看一下索引:
請求:
curl "localhost:9200/_cat/indices?v"
響應:
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size
yellow open customer DoM-O7QmRk-6f3Iuls7X6Q 5 1 1 0 4.5kb 4.5kb
yellow open bank 59jD3B4FR8iifWWjrdMzUg 5 1 1000 0 474.7kb 474.7kb
可以看到,現在我們的集羣中有兩個索引,分別是"customer"和"bank"
"customer"索引,1個文檔,"bank"索引有1000個文檔
The Search API
現在讓我們從一些簡單的搜索開始。運行搜索有兩種基本方法:一種是通過REST請求URI發送檢索參數,另一種是通過REST請求體發送檢索參數。
(畫外音:一種是把檢索參數放在URL後面,另一種是放在請求體裏面。相當於HTTP的GET和POST請求)
請求體方法允許你更有表現力,也可以用更可讀的JSON格式定義搜索。
用於搜索的REST API可從_search端點訪問。下面的例子返回"bank"索引中的所有文檔:
curl -X GET "localhost:9200/bank/_search?q=*&sort=account_number:asc&pretty"
讓我們來剖析一下上面的請求。
我們在"bank"索引中檢索,q=*參數表示匹配所有文檔;sort=account_number:asc表示每個文檔的account_number字段升序排序;pretty參數表示返回漂亮打印的JSON結果。
響應結果看起來是這樣的:
{
"took" : 96,
"timed_out" : false,
"_shards" : {
"total" : 5,
"successful" : 5,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : 1000,
"max_score" : null,
"hits" : [
{
"_index" : "bank",
"_type" : "_doc",
"_id" : "0",
"_score" : null,
"_source" : {
"account_number" : 0,
"balance" : 16623,
"firstname" : "Bradshaw",
"lastname" : "Mckenzie",
"age" : 29,
"gender" : "F",
"address" : "244 Columbus Place",
"employer" : "Euron",
"email" : "[email protected]",
"city" : "Hobucken",
"state" : "CO"
},
"sort" : [
0
]
},
{
"_index" : "bank",
"_type" : "_doc",
"_id" : "1",
"_score" : null,
"_source" : {
"account_number" : 1,
"balance" : 39225,
"firstname" : "Amber",
"lastname" : "Duke",
"age" : 32,
"gender" : "M",
"address" : "880 Holmes Lane",
"employer" : "Pyrami",
"email" : "[email protected]",
"city" : "Brogan",
"state" : "IL"
},
"sort" : [
1
]
},
{
"_index" : "bank",
"_type" : "_doc",
"_id" : "2",
"_score" : null,
"_source" : {
"account_number" : 2,
"balance" : 28838,
"firstname" : "Roberta",
"lastname" : "Bender",
"age" : 22,
"gender" : "F",
"address" : "560 Kingsway Place",
"employer" : "Chillium",
"email" : "[email protected]",
"city" : "Bennett",
"state" : "LA"
},
"sort" : [
2
]
},
......
]
}
可以看到,響應由下列幾部分組成:
- took : Elasticsearch執行搜索的時間(以毫秒爲單位)
- timed_out : 告訴我們檢索是否超時
- _shards : 告訴我們檢索了多少分片,以及成功/失敗的分片數各是多少
- hits : 檢索的結果
- hits.total : 符合檢索條件的文檔總數
- hits.hits : 實際的檢索結果數組(默認爲前10個文檔)
- hits.sort : 排序的key(如果按分值排序的話則不顯示)
- hits._score 和 max_score 現在我們先忽略這些字段
下面是一個和上面相同,但是用請求體的例子:
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'
{
"query": { "match_all": {} },
"sort": [
{ "account_number": "asc" }
]
}
'
區別在於,我們沒有在URI中傳遞q=*,而是向_search API提供json風格的查詢請求體
很重要的一點是,一旦返回搜索結果,Elasticsearch就完全完成了對請求的處理,不會在結果中維護任何類型的服務器端資源或打開遊標。這是許多其他平臺如SQL形成鮮明對比。
查詢語言
Elasticsearch提供了一種JSON風格的語言,您可以使用這種語言執行查詢。這被成爲查詢DSL。
查詢語言非常全面,乍一看可能有些嚇人,但實際上最好的學習方法是從幾個基本示例開始。
回到我們上一個例子,我們執行這樣的查詢:
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'
{
"query": { "match_all": {} }
}
'
查詢部分告訴我們查詢定義是什麼,match_all部分只是我們想要運行的查詢類型。這裏match_all查詢只是在指定索引中搜索所有文檔。
除了查詢參數外,我們還可以傳遞其他參數來影響搜索結果。在上面部分的例子中,我們傳的是sort參數,這裏我們傳size:
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'
{
"query": { "match_all": {} },
"size": 1
}
'
注意:如果size沒有指定,則默認是10
下面的例子執行match_all,並返回第10到19條文檔:
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'
{
"query": { "match_all": {} },
"from": 10,
"size": 10
}
'
from參數(從0開始)指定從哪個文檔索引開始,並且size參數指定從from開始返回多少條。這個特性在分頁查詢時非常有用。
注意:如果沒有指定from,則默認從0開始
這個示例執行match_all,並按照帳戶餘額降序對結果進行排序,並返回前10個(默認大小)文檔。
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'
{
"query": { "match_all": {} },
"sort": { "balance": { "order": "desc" } }
}
'
搜索
繼續學習查詢DSL。首先,讓我們看一下返回的文檔字段。默認情況下,會返回完整的JSON文檔(PS:也就是返回所有字段)。這被成爲source(hits._source)
如果我們不希望返回整個源文檔,我們可以從源文檔中只請求幾個字段來返回。
下面的例子展示了只返回文檔中的兩個字段:account_number 和 balance字段
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'
{
"query": { "match_all": {} },
"_source": ["account_number", "balance"]
}
'
(畫外音:相當於SELECT account_number, balance FROM bank)
現在讓我們繼續查詢部分。以前,我們已經看到了如何使用match_all查詢匹配所有文檔。現在讓我們引入一個名爲match query的新查詢,它可以被看作是基本的字段搜索查詢(即針對特定字段或字段集進行的搜索)。
下面的例子返回account_number爲20的文檔
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'
{
"query": { "match": { "account_number": 20 } }
}
'
(畫外音:相當於SELECT * FROM bank WHERE account_number = 20)
下面的例子返回address中包含"mill"的賬戶:
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'
{
"query": { "match": { "address": "mill" } }
}
'
(畫外音:相當於SELECT * FROM bank WHERE address LIKE '%mill%')
下面的例子返回address中包含"mill"或者"lane"的賬戶:
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'
{
"query": { "match": { "address": "mill lane" } }
}
'
(畫外音:相當於SELECT * FROM bank WHERE address LIKE '%mill' OR address LIKE '%lane%')
讓我們來引入bool查詢,bool查詢允許我們使用布爾邏輯將較小的查詢組合成較大的查詢。
下面的例子將兩個match查詢組合在一起,返回address中包含"mill"和"lane"的賬戶:
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'
{
"query": {
"bool": {
"must": [
{ "match": { "address": "mill" } },
{ "match": { "address": "lane" } }
]
}
}
}
'
(畫外音:相當於SELECT * FROM bank WHERE address LIKE '%mill%lane%')
上面是bool must查詢,下面這個是bool shoud查詢:
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'
{
"query": {
"bool": {
"should": [
{ "match": { "address": "mill" } },
{ "match": { "address": "lane" } }
]
}
}
}
'
(畫外音:must相當於and,shoud相當於or,must_not相當於!)
(畫外音:邏輯運算符:與/或/非,and/or/not,在這裏就是must/should/must_not)
我們可以在bool查詢中同時組合must、should和must_not子句。此外,我們可以在任何bool子句中編寫bool查詢,以模擬任何複雜的多級布爾邏輯。
下面的例子是一個綜合應用:
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'
{
"query": {
"bool": {
"must": [
{ "match": { "age": "40" } }
],
"must_not": [
{ "match": { "state": "ID" } }
]
}
}
}
'
(畫外音:相當於SELECT * FROM bank WHERE age LIKE '%40%' AND state NOT LIKE '%ID%')
過濾
分數是一個數值,它是文檔與我們指定的搜索查詢匹配程度的相對度量(PS:相似度)。分數越高,文檔越相關,分數越低,文檔越不相關。
但是查詢並不總是需要產生分數,特別是當它們僅用於“過濾”文檔集時。Elasticsearch檢測到這些情況並自動優化查詢執行,以便不計算無用的分數。
我們在前一節中介紹的bool查詢還支持filter子句,該子句允許使用查詢來限制將由其他子句匹配的文檔,而不改變計算分數的方式。
作爲一個例子,讓我們引入range查詢,它允許我們通過一系列值篩選文檔。這通常用於數字或日期過濾。
下面這個例子用一個布爾查詢返回所有餘額在20000到30000之間(包括30000,BETWEEN...AND...是一個閉區間)的賬戶。換句話說,我們想要找到餘額大於等於20000並且小於等等30000的賬戶。
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'
{
"query": {
"bool": {
"must": { "match_all": {} },
"filter": {
"range": {
"balance": {
"gte": 20000,
"lte": 30000
}
}
}
}
}
}
'
聚集
(畫外音:相當於SQL中的聚集函數,比如分組、求和、求平均數之類的)
首先,這個示例按state對所有帳戶進行分組,然後按照count數降序(默認)返回前10條(默認):
(畫外音:相當於按state分組,然後count(*),每個組中按照COUNT(*)數取 top 10)
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'
{
"size": 0,
"aggs": {
"group_by_state": {
"terms": {
"field": "state.keyword"
}
}
}
}
'
在SQL中,上面的聚集操作類似於:
SELECT state, COUNT(*) FROM bank GROUP BY state ORDER BY COUNT(*) DESC LIMIT 10;
響應:
{
"took":50,
"timed_out":false,
"_shards":{
"total":5,
"successful":5,
"skipped":0,
"failed":0
},
"hits":{
"total":1000,
"max_score":0,
"hits":[
]
},
"aggregations":{
"group_by_state":{
"doc_count_error_upper_bound":20,
"sum_other_doc_count":770,
"buckets":[
{
"key":"ID",
"doc_count":27
},
{
"key":"TX",
"doc_count":27
},
{
"key":"AL",
"doc_count":25
},
{
"key":"MD",
"doc_count":25
},
{
"key":"TN",
"doc_count":23
},
{
"key":"MA",
"doc_count":21
},
{
"key":"NC",
"doc_count":21
},
{
"key":"ND",
"doc_count":21
},
{
"key":"ME",
"doc_count":20
},
{
"key":"MO",
"doc_count":20
}
]
}
}
}
注意,我們將size=0設置爲不顯示搜索結果,因爲我們只想看到響應中的聚合結果。
接下來的例子跟上一個類似,按照state分組,然後取balance的平均值
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'
{
"size": 0,
"aggs": {
"group_by_state": {
"terms": {
"field": "state.keyword"
},
"aggs": {
"average_balance": {
"avg": {
"field": "balance"
}
}
}
}
}
}
'
在SQL中,相當於:
SELECT state, COUNT(*), AVG(balance) FROM bank GROUP BY state ORDER BY COUNT(*) DESC LIMIT 10;
下面這個例子展示了我們如何根據年齡段(20-29歲,30-39歲,40-49歲)來分組,然後根據性別分組,最後得到平均賬戶餘額,每個年齡等級,每個性別:
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'
{
"size": 0,
"aggs": {
"group_by_age": {
"range": {
"field": "age",
"ranges": [
{
"from": 20,
"to": 30
},
{
"from": 30,
"to": 40
},
{
"from": 40,
"to": 50
}
]
},
"aggs": {
"group_by_gender": {
"terms": {
"field": "gender.keyword"
},
"aggs": {
"average_balance": {
"avg": {
"field": "balance"
}
}
}
}
}
}
}
}
'
es集羣化配置
不能不說es的集羣化配置,比mysql和oracle等這些關係型數據庫要簡單太多了。同一個局域網建議使用dns服務器吧,修改方便
1.修改/etc/hosts
192.168.1.186 node-1
192.168.1.187 node-2
192.168.1.188 node-3
2.node-1,node-2,node-3修改elasticsearch.yml
cluster.name: mmm
node.name: node-1
network.host: 0.0.0.0
discovery.seed_hosts: ["node-1", "node-2","node-3"]
cluster.initial_master_nodes: ["node-1","node-2","node-3"]
http.cors.enabled: true
http.cors.allow-origin: "*"
3. 修改完成後,重新啓動即可。