Elasticsearch 快速開始

前言

本文非完全直譯譯文,主要參考的的是 elasticsearch 6.5 版的官網文檔 Getting Started,可以把這篇文章理解爲個人學習筆記,我力求詳略得當吧。

文末會附上 Getting Started 閱讀梳理的思維導圖。


爲了能更加輕鬆地進入 elasticsearch 的世界,本文將先從基礎角度給介紹了 es 的使用。

什麼是 elasticsearch

Elasticsearch 是一款開源的全文搜索與分析引擎,它擁有高擴展、大容量數據的存儲和處理特性,有着近乎實時的處理效果。elasticsearch 的使用場景還是比較多的,比如 APP 的搜索服務、ELK 實現日誌收集與分析、BI 商業智能等。

本文會逐步引導大家進入 elasticsearch 的世界,初步窺探它的使用。相關內容涉及如下:

  • 瞭解 elasticsearch 的一些基礎但非常核心的概念,比如集羣、節點、索引、分片和副本;
  • 學會 elasticsearch 安裝與啓動,同時爲了便於測試,順帶也介紹了 Kibana 的安裝啓動;
  • 探索集羣,介紹集羣涉及的一些基礎操作,比如健康狀態檢查、集羣節點檢查,索引創建等;
  • 更新數據,包括如何進行文檔替換、更新,以及如何進行刪除,最後通過批處理可以將多個操作組合起來;
  • 數據探索,主要是與搜索和聚合分析相關,介紹了常用的一些搜索 API、Query DSL 和聚合 API 的使用;

整體上的內容還是比較多的。

瞭解一些基礎概念

Elasticsearch 中有一些基礎但很核心的概念需要我們提前瞭解,它們對我們的學習將起到很大幫助。

具體是哪些概念呢?

  • 近實時
  • 集羣
  • 節點
  • 索引
  • 類型
  • 文檔
  • 分片副本

下面我們將逐一介紹。

近實時

什麼是近實時?

它表示一個文檔從被索引(存儲使文檔可搜索)到真正能被搜索之間有一個短暫的延遲,而非實時,這個延遲默認是 1 秒。當然,默認延遲可以修改的。

集羣

集羣是節點的集合。

集羣實現了在多節點上進行大容量數據存儲和搜索的能力。每個集羣都擁有唯一名稱,而節點正是根據集羣的名稱決定是否加入某個集羣。不同環境的集羣的名稱不能相同,如開發、測試、線上三套環境,集羣可分別命名爲 logging-dev、logging-test、logging-prod。

節點

節點,集羣組成的一部分,負責具體的事務處理,比如數據存儲、文檔索引、搜索執行等。節點也有唯一個名稱,如果沒有指定將隨機生成。

節點可通過配置集羣名稱,指定加入哪個集羣,節點默認的集羣名稱是 elasticsearch。如果我們在一個網絡環境下啓動多個節點,並且它們之間可以相互發現,就將會自動組織一個名稱爲 elasticsearch 的集羣。

索引

索引是一系列相似文檔的集合,例如,我們把客戶信息存放到一個索引,訂單信息存儲到另一個索引中。索引可通過名稱識別,名稱必須小寫。當操作文檔時,我們需要通過索引名稱指定。

索引的數量,集羣中並沒有限制定義索引的數量。

類型

elasticsearch 6.0 已丟棄功能,不具體介紹了。

有一點需要注意,爲與老版本兼容,該功能暫未徹底移除,當前一個索引仍可設置類型,但當前只能指定一個類型。一般情況下,我們設置一個固定 type 即可,比如 _doc。

文檔

被索引的基礎信息單元,比如一個客戶、一件產品、或是一筆訂單。文檔可用 JSON 形式表示,它是一種非常普遍的數據交換格式。索引中,我們可以存放任意數量的文檔。

分片與副本

分片和副本是 elasticsearch 非常核心的概念。

我們知道,elasticsearch 存儲的數據量能突破單個硬件的限制,數據處理速度有着近實時的水平。這些都和分片和副本有着很大關係。

分片實現了索引文檔分散分佈,並且每個切片都是功能完善的,索引是獨立的,可能分佈在集羣中的任意節點。分片的重要性主要體現在使 elasticsearch 存儲容量的水平擴展和分佈式並行處理都成爲了現實。

副本提高了 elasticsearch 的容錯能力。網絡環境下,異常隨時可能發生,比如一些節點或分片從網絡中消失。一旦設置了副本,索引就會同時擁有主分片和副本分片。一旦某個分片發生異常,還有其他分片可替代。而且,副本也可以提高請求的處理速度,一個分片上的副本可同時並行處理多個請求。

一句話簡述,每個索引可以由多個分片組成,而每個分片也可以擁有多個副本。

介紹完了核心概念,是否已經開始蠢蠢欲動了?開始安裝自己的集羣吧。

安裝與啓動

本節主要介紹 elasticsearch 的安裝。另外,由於要使用 kibana 的 devtools 控制檯,將也會介紹下 kibana 的安裝。

安裝 elasticsearch

首先安裝依賴,elasticsearch 是 Java 開發,6.5 版依賴 Java 8,建議安裝 Oracle JDK 1.8.0_131。Java 安裝完成後,就可以開始安裝 elasticsearch 了。

簡單起見,我們只介紹二進制包的安裝。

安裝過程僅僅三步搞定!

$ curl -L -O https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.5.4.tar.gz
$ tar -xvf elasticsearch-6.5.4.tar.gz
$ cd elasticsearch-6.5.4/bin
$ ./elasticsearch -d # -d 表示後臺啓動

安裝啓動完成後,可通過訪問 localhost:9200 確認下實例運行是否正常。

如果想了解 Mac 的 homebrew 安裝或 windows 下的 MSI Installer Package 安裝,可自行閱讀官方文檔

安裝 kibana

Kibana 的安裝與 elasticsearch 的不同,需要區別不同的平臺系統。本節將介紹 Mac 與 Linux 下的安裝,它們都可以通過下載對應系統的 tar 包完成。

Linux 系統,我們以 Linux x86_64 爲例。安裝命令如下:

$ wget https://artifacts.elastic.co/downloads/kibana/kibana-6.5.4-linux-x86_64.tar.gz
$ shasum -a 512 kibana-6.5.4-linux-x86_64.tar.gz 
$ tar -xzf kibana-6.5.4-linux-x86_64.tar.gz
$ cd kibana-6.5.4-linux-x86_64/
$ ./bin/kibana

MacOS 系統,安裝命令如下:

$ curl -O https://artifacts.elastic.co/downloads/kibana/kibana-6.5.4-darwin-x86_64.tar.gz
$ shasum -a 512 kibana-6.5.4-darwin-x86_64.tar.gz 
$ tar -xzf kibana-6.5.4-darwin-x86_64.tar.gz
$ cd kibana-6.5.4-darwin-x86_64/ 
$ ./bin/kibana

如果希望 kibana 在後臺運行,可以通過 nohup 實現。完成後,訪問 localhost:5601 下確認狀態,它會默認連接本地啓動的 elasticsearch 集羣。

好吧!我還是不想介紹 Windows 下的安裝。

多節點集羣

如果希望啓動一個多節點集羣,我們可以修改下啓動命令,在本地同時啓動兩個節點,集羣名稱相同情況下,它們將會自動組織成一個新的集羣。命令如下:

$ ./bin/elasticsearch -Ecluster.name=cluster_dev -Epath.data=cluster_node01 -Enode.name=node01 -Ehttp.port=9200 -d
$ ./bin/elasticsearch -Ecluster.name=cluster_dev -Epath.data=cluster_node02 -Enode.name=node02 -Ehttp.port=9201 -d

啓動成功後,可以進入 kibana 的 devtool 執行 _cat/nodes?v,輸出如下:

ip        heap.percent ram.percent cpu load_1m load_5m load_15m node.role master name
127.0.0.1           29          75   6    3.31                  mdi       -      node01
127.0.0.1           33          75   6    3.31                  mdi       *      node02

我們將會看到集羣中已經有了兩個節點。

探索集羣

經歷了前面幾步,elasticsearch 集羣已經安裝完成,但我們要如何與它交流呢?其實,elasticsearch 已經提供了一套全面且功能強大的 REST API,用於與集羣交互。REST API 涉及功能包括:

  • 檢查集羣、節點和索引的健康情況、狀態以及統計信息;
  • 管理集羣、節點、索引和元數據;
  • 針對索引的增刪改查以及搜索 API;
  • 高級搜索功能,比如分頁、排序、過濾、腳本處理、聚合等;

本節我們將簡單介紹上面提到的部分 API。

集羣健康檢查

健康檢查可以讓我們瞭解集羣當前狀態,可通過 _cat API 實現。

示例如下:

GET /_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
1565884060 23:47:40  cluster_dev green           2         2     22  21    0    0        0             0                  -                100.0%

輸出結果顯示,集羣名稱爲 cluster_dev,狀態是 green,當前集羣有 2 個節點。

先重點講講 green 究竟代表什麼意思?

首先,green 表示的是集羣的狀態,對應字段名稱是 status,共有三種狀態,分別是 green、yellow 和 red。

  • Green 表示集羣運行完全正常(主分片和副本分片都運行正常);
  • Yellow 表示所有主分片可用,但副本沒有完全分配;
  • Red 部分數據完全不可用,即無論主分片和副本都非正常運作;

當集羣狀態是 red 時,仍然可以接收處理搜索請求,但我們需要立刻修復它。

查看集羣節點

通過 _cat/nodes,可以列出當前集羣下的所有節點,前面在啓動一個多節點集羣時,我們已經過了使用該 API,不多介紹了。

列出所有索引

_cat/indices?v 能列出集羣中的所有索引。說明一下,indices 是 index 的複數形式。

GET _cat/indices

輸出如下:

health status index    uuid                   pri rep docs.count docs.deleted store.size pri.store.size
green  open   us_tweet WXkAeMEvR0eukVOFOM1q3w   5   0          6            0     15.7kb         15.7kb
green  open   gb_user  pbOu0n0XQ96mouQ642eutQ   5   0          1            0      5.7kb          5.7kb
green  open   us_user  idxuhX0LQlekzfbjd6kB5w   5   0          1            0      5.7kb          5.7kb
green  open   gb_tweet SJTleUerT5CaO9Sl-JwIQg   5   0          6            0     15.7kb         15.7kb

我的本地集羣經常拿來測試,所以這裏會看到已經有很多 index,對於你剛搭建好的集羣,輸出肯定就是空的。

創建索引

接下來,我們學習下如何創建索引,直接看示例吧。嘗試創建一個名爲 "customer" 的索引,如下:

PUT customer?pretty

PUT 加上索引名稱即可。 customer 索引成功創建後,查看下集羣當前索引列表,如下:

GET _cat/indices

輸出結果如下:

health status index    uuid                   pri rep docs.count docs.deleted store.size pri.store.size
...
green  open   customer 71RYoldbQ6uXMo56t4bSog   5   1          0            0      1.1kb           460b
...

customer 成功創建,包含 5 個主分片和 1 個副本分片,索引中當前的文檔數量爲 0,索引狀態爲 green。

索引的狀態和集羣一樣,也是 green、yellow 和 red 這 3 個值,並且含義與集羣狀態類似。green 是全功能正常,yellow 表示副本未完全分配,red 表示部分主分片不可用。我們可以測試一下,停止集羣中的一個節點,這時 customer 的狀態將會馬上切換爲 yellow 狀態。

索引和查詢文檔

在 elasticsearch 中,"索引" 這個名詞常常會搞暈我們。一般講到索引,主要是指它的名詞含義,比如我們說,創建一個 customer 索引。但有時,它又是動詞,比如我們在增加和更新文檔時,常會說索引一個文檔,在這種情況下,可以把它理解爲存儲文檔並使其可搜索。

那如何索引一個文檔呢?比如,索引一個 ID 爲 1 的 customer 文檔到 customer 索引中,如下。

PUT /customer/_doc/1?pretty
{
  "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
}

結果表明,我們已經成功創建一個 ID 爲 1 的文檔。從這裏也能看出,在索引一個文檔之前,並不要求我們明確創建索引 customer,如果 customer 索引不存在,elasticsearch 將會自動幫助我們創建索引。

檢索剛纔索引的文檔。

GET /customer/_doc/1?pretty

響應如下:

{
  "_index" : "customer",
  "_type" : "_doc",
  "_id" : "1",
  "_version" : 1,
  "found" : true,
  "_source" : { "name": "John Doe" }
}

found 字段表明,我們已經成功檢索到了一個 ID 爲 1 的文檔。_source 中的內容是檢索文檔的完整內容。

刪除索引

讓我們刪除掉剛剛創建的索引。

DELETE /customer?pretty

如此,我們就成功刪除了之前創建的 customer。如果不確定,可以使用 GET _cat/indices 檢查下。

文檔 API

回顧下前面介紹的那些 API,我們重新再走一遍。

PUT /customer
PUT /customer/_doc/1
{
  "name": "John Doe"
}
GET /customer/_doc/1
DELETE /customer

仔細觀察上面的命令,你可能會發現一個比較通用的模式,如下:

<HTTP Verb> /<Index>/<Type>/<ID>

這個模式在 elasticsearch 中非常普遍,記住它,這將對 elasticsearch 學習之旅將會非常有幫助。

更新數據

區別於傳統關係型數據庫的實時性特點,elasticsearch 是近實時的,也就是說,在文檔被 create/update/delete 與搜索結果中出現之間將會有 1 秒的延遲。

文檔替換

創建和替換文檔本質上都屬於索引文檔,因而,我們之前用來創建文檔的命令同樣適用於文檔替換。

因爲 customer 索引重建過,我們重新創建一個 ID 爲 1 的文檔。如下:

PUT /customer/_doc/1?pretty
{
  "name": "John Doe"
}

同樣的命令即可實現文檔更新,我們只需要傳遞不同的文檔內容即可,如下:

PUT /customer/_doc/1?pretty
{
  "name": "Jane Doe"
}

將 ID 爲 1 的文檔中 name 由 Johh Doe 更新爲 Jane Doe。如果指定 ID 文檔不存在,將會創建新的文檔,否則更新當前文檔。

比如,ID 爲 2 的文檔不存在,通過如下命令創建。

PUT /customer/_doc/2?pretty
{
  "name": "Jane Doe"
}

索引文檔時,ID 不是必須的,如果不指定,elasticsearch 會自動爲這個文檔指定一個隨機的 ID,並作爲響應的一部分返回給你。

示例如下:

POST /customer/_doc?pretty
{
  "name": "Jane Doe"
}

這種情況下,我們需要使用 POST 代替 PUT 提交請求。

響應結果:

{
  "_index": "customer",
  "_type": "_doc",
  "_id": "Z-RnpGwBe6KTDC6t3MGV",
  "_version": 1,
  "result": "created",
  "_shards": {
    "total": 1,
    "successful": 1,
    "failed": 0
  },
  "_seq_no": 0,
  "_primary_term": 1
}

上面可以看到 elasticsearch 爲我們生成的文檔 ID,Z-RnpGwBe6KTDC6t3MGV。

更新文檔

說完文檔的索引和替換,我們再來談談文檔的更新。提前說明,elasticsearch 並非真的去更新文檔,它的更新操作與替換類似,包含刪除舊文檔和索引新文檔兩個操作。

示例演示,更新前面創建的 ID 爲 1 的文檔,更新 name 字段爲 Jane Doe。

POST /customer/_doc/1/_update?pretty
{
  "doc": { "name": "Jane Doe" }
}

示例 2,更新 name 爲 Jane Doe 的同時,增加一個字段 age。

POST /customer/_doc/1/_update?pretty
{
  "doc": { "name": "Jane Doe", "age": 20 }
}

示例 3,使用腳本更新文檔,比如將 ID 爲 1 的文檔的字段 age 加 5。

POST /customer/_doc/1/_update?pretty
{
  "script": "ctx._source.age += 5"
}

上面的例子,ctx._source 表示的是我們將要更新的文檔。這裏是通過指定 ID 的方式查詢要更新的文檔,elasticsearch 也可以像 SQL 一樣,通過複雜查詢實現更新。

刪除文檔

刪除文檔最簡單直接,HTTP 方法換成 DELETE,指定文檔 ID 即可。如下:

DELETE /customer/_doc/2?pretty

和更新類似,刪除也可以根據查詢結果執行刪除,API 是 _delete_by_query。如果是刪除索引中的所有文檔,直接刪除索引更直接點。

批處理

經過前面的學習,我們已經瞭解了 elasticsearch 一些基礎 API 的使用,如文檔的索引、更新、刪除。這一小節介紹一個新的 API,_bulk API,它支持將多個操作打包成一個請求,實現批處理。這樣可以更加高效的執行操作,也能減少網絡傳遞次數。

一個快速入門案例,如下:

POST /customer/_doc/_bulk?pretty
{"index":{"_id":"1"}}
{"name": "John Doe" }
{"index":{"_id":"2"}}
{"name": "Jane Doe" }

索引了 2 個文檔,一個文檔 ID 1,name 爲 John Doe,另一個文檔 ID 2,name 爲 Jane Doe。

再看一個案例,如下:

POST /customer/_doc/_bulk?pretty
{"update":{"_id":"1"}}
{"doc": { "name": "John Doe becomes Jane Doe" } }
{"delete":{"_id":"2"}}

批處理包含 2 個操作,更新 ID 爲 1 文檔的 name 字段和刪除 ID 爲 2 的文檔。

批處理中的一個操作失敗並不會導致整個 bulk API 處理失敗,如果一個操作失敗,剩下來的其他操作仍會繼續執行。bulk API 處理完成後,響應結果中會包含每個操作的處理結果。

數據探索

本節內容主要涉及兩個方面:搜索與分析。

數據

搜索分析不可缺少數據,我們將使用 elastic 官方提供的數據樣本,相對而言,應該比自己的生成更符合真實場景。

一個文檔,示例如下:

{
    "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"
}

官方的數據是用工具隨機生成的,工具地址。有興趣,可以設置自己的數據生成規則。

開始數據加載之前,要先下載數據,下載地址

$ wget https://raw.githubusercontent.com/elastic/elasticsearch/master/docs/src/test/resources/accounts.json

下載完成後,執行如下命令加載數據:

$ curl -H "Content-Type: application/json" -XPOST "localhost:9200/bank/_doc/_bulk?pretty&refresh" --data-binary "@accounts.json"

查看索引信息

$ curl "localhost:9200/_cat/indices?v"

響應如下:

health status index uuid                   pri rep docs.count docs.deleted store.size pri.store.size
green  open   bank  FFcSux-ETmmUlMJNBCEyqA   5   1       1000            0      957kb        482.3kb

可以看出,我們已經爲 bank 成功索引了 1000 個文檔。

搜索 API

開始嘗試一些簡單的搜索。有兩種基本的搜索方式:

  • URI Search,通常 URI 參數指定搜索參數。
  • Request Body,在請求內容包含在請求體中發送。

相對而言,Request Body 方式更靈活,包含了全部的搜索支持。而 URI Search 主要在測試時使用,比較方便。

搜索請求通過 _search 執行。一個示例,通過搜索返回 bank 索引中的所有文檔。

GET /bank/_search?q=*&sort=account_number:asc&pretty

URI Search 方式實現搜索,通過 q=* 執行匹配全部文檔,sort=account_number:asc 指定排序方式。

響應如下:

{
  "took" : 63,
  "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",
      "sort": [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"}
    }, ...
    ]
  }
}

結果中的各個參數函數如下:

  • took 表示搜索執行時間
  • timed_out 搜索是否超時
  • _shards 多少分片參與搜索,以及成功與失敗的情況如何。
  • hits 搜索結果
  • hits.total 匹配搜索條件的文檔數量
  • hits.hits.sort 排序
  • hits.hits._score 和 max_score,相關度得分,指定排序,字段會被忽略。

通過 Reuqest Body 方式執行與上面相同的操作,如下:

GET /bank/_search
{
  "query": { "match_all": {} },
  "sort": [
    { "account_number": "asc" }
  ]
}

我們下面主要介紹 Request Body 的使用,畢竟它更加強大。

DSL 介紹

DSL,全稱 Domain Special Language, 即特定領域語言,elasticsearch 制定了一套 JSON 風格的 DSL 語言。它支持的功能非常全面,剛學習它時,會讓我們產生一種恐懼,因爲它真的很難。我們可以先從一些簡單的例子看起。

查詢所有文檔,如下:

GET /bank/_search
{
  "query": { "match_all": {} }
}

解剖下請求體。query 定義查詢語句,match_all 就是我們將會執行的查詢語句,表示匹配索引中的所有文檔。

執行上面的查詢語句默認只會返回 10 條文檔,我們可以通過指定 size 參數改變默認獲取文檔數量。如下:

GET /bank/_search
{
  "query": { "match_all": {} },
  "size": 1
}

通過 from 和 size 可以實現分頁效果,如下:

GET /bank/_search
{
  "query": { "match_all": {} },
  "from": 10,
  "size": 10
}

from 用於指定文檔偏移的開始位置,相當於 SQL 中的 offset。

文檔默認根據搜索相關度得分排序,不過我們這裏是默認匹配全部,所以文檔的相關度得分都是1。除了相關度排序,還可以按其他字段,比如 balance 字段。如下:

GET /bank/_search
{
  "query": { "match_all": {} },
  "sort": { "balance": { "order": "desc" } }
}

搜索查詢語句

繼續看下搜索返回文檔字段,默認情況下,搜索將會返回文檔的所有字段內容。我們可通過 _source 指定只返回部分內容。

GET /bank/_search
{
  "query": { "match_all": {} },
  "_source": ["account_number", "balance"]
}

如此,搜索將只返回 account_numberbalance 兩個字段。 瞭解的 SQL 的朋友可以將其與 SELECT 指定列類比。

繼續看查詢部分吧!

前面,通過 match_all 查詢匹配了全部文檔。現在,我們再引入一個新的查詢語句,match,它是基於一個字段的查詢。

查詢 account_number 爲 20 的文檔。示例如下:

GET /bank/_search
{
  "query": { "match": { "account_number": 20 } }
}

查詢 address 包含 mill 的文檔。示例如下:

GET /bank/_search
{
  "query": { "match": { "address": "mill" } }
}

查詢 address 包含 mill 或 lane 的文檔。示例如下:

GET /bank/_search
{
  "query": { "match": { "address": "mill lane" } }
}

match 基於分詞查詢,2 個查詢單詞是 or 的關係。如果我們就要搜索 "mill lane" 呢? 這時可以使用 match_pharse。示例如下:

查詢 address 包含 "mill lane" 的文檔。示例如下:

GET /bank/_search
{
  "query": { "match_phrase": { "address": "mill lane" } }
}

繼續介紹 bool 查詢,它允許我們將上面這些基礎查詢組合形成一個複合查詢。比如,查詢同時包含 "mill" 和 "jane" 的文檔。

示例如下:

GET /bank/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "address": "mill" } },
        { "match": { "address": "lane" } }
      ]
    }
  }
}

例子中的 bool must 表示文檔必須同時滿足兩個 must 條件。如果是隻要滿足一個條件即可,我們可以使用 bool should,示例如下:

GET /bank/_search
{
  "query": {
    "bool": {
      "should": [
        { "match": { "address": "mill" } },
        { "match": { "address": "lane" } }
      ]
    }
  }
}

如果要求兩者必須不滿足,我們可以使用 bool must_not,示例如下:

GET /bank/_search
{
  "query": {
    "bool": {
      "must_not": [
        { "match": { "address": "mill" } },
        { "match": { "address": "lane" } }
      ]
    }
  }
}

bool 查詢中還可以將上面的這幾種查詢同時組合起來,同時包含 must、must_not 和 should。

示例如下:

GET /bank/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "age": "40" } }
      ],
      "must_not": [
        { "match": { "state": "ID" } }
      ]
    }
  }
}

注意一點,如果是 must、must_not 和 should 組合時,should 就缺少了必須滿足一個纔算匹配的限制,這時,我們可以通過 minimum_should_match 指定 should 匹配個數。

更復雜的,bool 查詢中還可以包含其他的 bool 查詢。這裏先不介紹了。

過濾語句

談到過濾之前,我們就不得不提文檔相關度評分,相關度評分是用於衡量搜索語句與文檔的匹配程度的一個指標。前面已經提過的查詢語句都會參與到這個指標的計算。

但有時,查詢僅僅是爲了過濾一些不滿足條件的文檔,我們並不希望它們也參與到相關度評分的計算中,由此,我們並引入了 filter。

前面介紹的 bool 查詢中便支持 filter 功能。它可以在不影響相關度評分的情況下,實現文檔過濾。如下:

GET /bank/_search
{
  "query": {
    "bool": {
      "must": { "match_all": {} },
      "filter": {
        "range": {
          "balance": {
            "gte": 20000,
            "lte": 30000
          }
        }
      }
    }
  }
}

bool 查詢中包含了 must 和 filter 兩部分。filter 部分通過 range query 實現只查詢 blanace 滿足指定區間的文檔,must 中的 match_all 實現全部文檔返回。

除了上面介紹的查詢,如 match_all、match、bool、range,elasticsearch 還有很多其它查詢可用,這裏不詳細介紹了。只要掌握了前面的知識,我們已經完全可以照貓畫虎。

聚合功能

利用 elasticsearch 的聚合能力,我們可以實現分組統計,可以和 SQL 的 GROUP BY 分組和聚合函數類比。搜索和聚合都是通過 _search 請求實現,同一個請求可同時處理搜索與聚合的請求。這樣也可以幫助我們節省必要的網絡帶寬。

一個例子,按銀行卡賬號狀態(即 state)分組。默認是返回 top 10。如下:

GET bank/_search
{
  "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": 29,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "skipped" : 0,
    "failed": 0
  },
  "hits" : {
    "total" : 1000,
    "max_score" : 0.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
      } ]
    }
  }
}

從上面可以看出,ID 爲 Idaho 的數量爲 27,緊接着是 TX 數量 27,然後是 AL 共 25 個。上面設置 size 爲 0,是爲隱藏搜索結果內容,僅僅顯示聚合結果。

我們可以在前面的聚合結果的基礎上,計算 top 10 的賬戶餘額平均值。如下:

GET /bank/_search
{
  "size": 0,
  "aggs": {
    "group_by_state": {
      "terms": {
        "field": "state.keyword",
        "order": {
          "average_balance": "desc"
        }
      },
      "aggs": {
        "average_balance": {
          "avg": {
            "field": "balance"
          }
        }
      }
    }
  }
}

在 group_by_state 中加入子聚合 average_balance,同時在 group_by_state 中,通過 order 配置實現了按 balance 平均值大小排序的需求。

下面這個例子演示瞭如何按年齡區間分組,比如 20-29、30-39 和 40-49,並在基礎上,繼續按性別分組。最後,計算各個分組 balance 的平均值。

GET /bank/_search
{
  "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"
              }
            }
          }
        }
      }
    }
  }
}

例子中有兩個分組 group_by_age 和 group_by_gener 以及一個聚合計算 average_balance。

總的來說,上面的例子還是比較循環漸進的。但主要還是集中在使用層面,並沒有太多細節的介紹。如果想了解更多與聚合相關的內容,可自行查詢官方文檔。

總結

Elasticsearch 可以說是一款即簡單又複雜的產品。本文只是簡單介紹了它的一些基礎,並初步體驗了下它的使用。到此,僅僅算是開啓了一扇門,elasticsearch 的使用遠沒有那麼簡單。

再接再厲吧!朋友!

附錄

Getting Started

Basic Concepts

Installation

Exploring Your Cluster

Modify Your Data

Exploring Your Data


波羅學的公衆號

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章