Elasticsearch是基於Apache 2.0開源的實時、分布式、分析搜索引擎,相比Lucene,Elasticsearch的上手比較容易,這篇文章主要紀錄Elasticsearch的基本概念和基本API。
官方對Elasticsearch的定義:
Elasticsearch is a distributed, RESTful search and analytics engine capable of solving a growing number of use cases. As the heart of the Elastic Stack, it centrally stores your data so you can discover the expected and uncover the unexpected.
你可能已經在Elasticsearch的文檔中看過如下詞匯:
- NRT: 近實時(Near Realtime),當一個文檔保存至index,短暫的延遲后就可以被搜索。
- Node: 指單個實例,存儲文檔並參與所在集群的索引和搜索,一台機器可以運行多個實例,每個實例啟動時都有自己的唯一標識。
- Cluster: 指多個實例組成的集群,集群中的節點有相同的cluster.name(默認為"elasticsearch"),用戶可以通過集群對多個節點的所有數據進行索引和搜索。
- Index: 索引在Elasticsearch中有兩種含義。作為名詞,索引可以指文檔的集合,作為動詞,可以說"對某個文檔進行索引",也就是把文檔存儲到索引。一個index的名稱即為其標識,名稱必須為小寫。如果和RDBMS相比的話,index的概念可能和database比較相似。
- Type: 用於對同一個index中的數據進行邏輯分類。例如,有個存儲了sns數據的index,這個index中可能存在多個類型,例如用戶資料數據、評論數據、聊天數據...
- Document: 文檔以JSON格式存儲,文檔中包含多個字段,每個文檔都有自己的類型並存儲於index中。
- Shards: index可以切分成多個shard,每個shard分布到不同的節點上。通過shard可以在存儲還是搜索上緩解單個節點的壓力。
- Replicas: 對shard進行復制,通過副本不僅保證了可用性,也提高了搜索效率。
一個index可以分為多個shard,默認配置下,每個index有5個primary shards和1個副本(也就是5個replica shards),也就是說這個index有10個shards。
為了進一步說明,這里簡單說明一下安裝過程。
首先需要確認機器上是否已經安裝了Java,這里建議使用java 8:
java -version
echo $JAVA_HOME
根據自己需要,可以選擇使用apt-get
、yum
進行安裝,或者直接運行docker容器(docker.elastic.co/elasticsearch/elasticsearch:5.3.1), 參考https://www.elastic.co/guide/en/elasticsearch/reference/current/install-elasticsearch.html。
這里我們用TAR包進行安裝
cd
wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-5.2.1.tar.gz
cp elasticsearch-5.2.1.tar.gz /usr/local ; cd $_
tar xzvf elasticsearch-5.2.1.tar.gz
啟動時可以指定集群名稱和節點名稱:
elasticsearch-5.2.1/bin/elasticsearch -Ecluster.name=cluster0 -Enode.name=node0
Elasticsearch的web接口的默認端口為9200,或者可以根據自己的情況,在bin/elasticsearch.yml中對http.port
進行修改。
最后查看響應結果,參考:
$ curl http://localhost:9200/
{
"name" : "F16Po5W",
"cluster_name" : "elasticsearch",
"cluster_uuid" : "gDEP8ViaSVuQkCADjMkWHg",
"version" : {
"number" : "5.2.1",
"build_hash" : "db0d481",
"build_date" : "2017-02-09T22:05:32.386Z",
"build_snapshot" : false,
"lucene_version" : "6.4.1"
},
"tagline" : "You Know, for Search"
}
下面檢查一下集群的狀態,參考:
$ curl http://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
1488018007 12:00:00 cluster0 green 1 1 0 0 0 0 0 0 - 100.0%
輸出顯示,名為"elasticsearch"的狀態為"green",狀態分三種,green(健康)、yellow(服務可用,尚未分配副本)、red(不可用)。
red也並不是完全不可用,部分功能還是可以用的,例如搜索請求還是可以在部分shards中進行。
上面用到的_cat
API,可以參考https://www.elastic.co/guide/en/elasticsearch/reference/current/cat.html,參數v
代表verbose。
如果需要JSON格式,則去掉_cat即可,參考:
curl http://localhost:9200/_cluster/health?pretty
health只反映集群的大體情況,更詳細的信息,比如版本、節點、metadata等可以通過state獲得:
curl http://localhost:9200/_cluster/state?pretty
另外,還有一個stats,和state不同的是,stats返回的是和性能相關的信息,比如各項字節數、OS、fs、jvm等。
類似地,檢查節點狀態:
$ curl http://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 7 90 5 0.00 0.01 0.05 mdi * node0
由於啟動時指定了節點名稱,這里節點的名稱為node0。
檢查index狀態:
$ curl http://localhost:9200/_cat/indices?v
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size
注意不是index的復數為indices,由於尚未創建任何index,這里沒有輸出。
但這並不妨礙試試analyze:
curl -X POST http://localhost:9200/_analyze?pretty -d '{"analyzer" : "standard", "text" : "Kavlez.Kim@gmail.com"}'
下面開始是Elasticsearch各個單位的基本API使用介紹。
Index
接下來創建一個index,注意不是POST,是PUT,參考:
$ curl -X PUT http://localhost:9200/province?pretty
{
"acknowledged" : true,
"shards_acknowledged" : true
}
重新確認下index狀態:
$ curl http://localhost:9200/_cat/indices?v
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size
yellow open province Ks7940k5QYKp9t7AEntbBA 5 1 0 0 650b 650b
如之前所說,默認有5個primary shards和1個replicas,盡管還沒有任何文檔。
第一個字段,健康狀態顯示為yellow,這是因為尚未分配副本,而我們只有一個節點。
創建index時也可以指定其他屬性,比如:
curl -X PUT -d '
{
"settings" : {
"number_of_shards" : 3
},
"mappings" : {
"type1" : {
"_source" : { "enabled" : false }, "properties" : {
"name" : { "type" : "string" }, "code" : {"type":"string"}
}
}
},
"aliases" : {
"alias_1" : {
"filter" : {
"term" : {"subdivision" : "Municipality" }
}
}
}
}' http://localhost:9200/province
至於GET,不只是用於察看index:
curl http://localhost:9200/province
curl http://localhost:9200/province/_settings
curl http://localhost:9200/province/_stats
curl http://localhost:9200/province/_flush
curl http://localhost:9200/province/_refresh
刪除一個index可以通過DELETE,如果並不打算刪除index,但又希望無法訪問該index,此時可以close。
可以通過POST /{index}/_close
和/{index}/_open
對index進行開關,當用戶訪問已關閉的index時會提示錯誤。
也可以通過_all
或通配符對所有的index批量操作,但考慮到風險,可以通過在配置文件修改action.destructive_requires_name
項禁用批量指定。
已關閉的index也會消耗一定的磁盤空間,開關操作可以通過cluster.indices.close.enable
項禁用,該項默認為true。
Document
文檔相關操作,先在province
index中添加兩個文檔:
$ curl -X PUT -d '{"name": "Hong Kong", "code": "CN-91", "native": "香港"}' http://localhost:9200/province/special/1?pretty
{
"_index" : "province",
"_type" : "special",
"_id" : "1",
"_version" : 1,
"result" : "created",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"created" : true
}
$ curl -X PUT -d '{"name": "Beijing", "code": "CN-11", "native": "北京"}' http://localhost:9200/province/municipality/1?pretty
{
"_index" : "province",
"_type" : "municipality",
"_id" : "1",
"_version" : 1,
"result" : "created",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"created" : true
}
請求中的special
、municipality
指的就是文檔的類型,盡管兩個文檔在同一個index,但類型不同。
后面的1
就是文檔的id,可以對特定的index/type/id
重新PUT,如果用同一個id提交多次,每次提交時_version
會自動+1。
如果希望指定操作類型,則加上op_type
參數,例如下面的例子會觸發versionconflictengine_exception,而不是version+1:
curl -X PUT -d '{"name": "Hong Kong", "code": "CN-91", "native": "香港"}' http://localhost:9200/province/special/1?op_type=create
如果希望添加文檔時自動生成id,則使用POST,此時就不能指定op_type了。
添加操作時不小心寫錯了index的名稱,但還是成功了,列出indices(GET _cat/indices
)發現有幾個寫錯名稱的index。
Elasticsearch默認允許向不存在的index添加文檔,不存在則自動創建,該設置可以通過action.autocreateindex項進行修改,比如:
action.auto_create_index: +kav*,-da*
允許名稱以"kav"開頭的index自動創建,禁止以"da"開頭的。
如果想全部禁止,則設置-*
。
如果我想在已有的文檔中添加字段,相比重新將整個文檔重新PUT,不如用腳本進行更新,參考:
$ curl -X POST http://localhost:9200/province/special/1/_update -d '
{
"script":{
"inline": "ctx._source.dc = params.dial_code", "params":{
"dial_code": 852
}
}
}'
{"_index":"province","_type":"special","_id":"1","_version":5,"result":"updated","_shards":{"total":2,"successful":1,"failed":0}}
至於GET,基本用法如下:
$ curl http://10.69.201.47:9200/province/special/1?pretty
{
"_index" : "province",
"_type" : "special",
"_id" : "1",
"_version" : 1,
"found" : true,
"_source" : {
"name" : "Hong Kong",
"code" : "CN-91",
"native" : "香港"
}
}
另外,可以用_source
進行過濾
$ curl http://localhost:9200/province/special/1/_source=name,code?pretty
{
"_index" : "province",
"_type" : "special",
"_id" : "1",
"_version" : 1,
"found" : true,
"_source" : {
"name" : "Hong Kong",
"code" : "CN-91"
}
}
如果想一次請求多個文檔,可以使用_mget
,參考:
curl -X POST http://localhost:9200/_mget -d '
{
"docs":[
{
"_index":"province", "_type": "special", "_id": "1"
},
{
"_index":"province", "_type":"municipality", "_id": "1"
}
]
}'
docs數組的第二項並不存在,於是返回{"_index" : "province", "_type" : "municipality", "_id" : "1", "found" : false }
。
刪除文檔用DELETE方法,刪除時可以加上其他參數,例如:
$ curl -X delete http://localhost:9200/province/special/1?version=1
{
"_index": "province",
"_type": "special",
"_id": "1",
"_version": 1,
"found": true,
"_source": {
"name": "Hong Kong",
"code": "CN-91",
"native": "香港"
}
}
Search
search API提供兩種方式:
- URI Search: GET的URL后面帶上query string
- Request Body Search: 也就是將Query DSL作為body請求
至於搜索的對象,可以是多個index,也可以是某個index下的多個type。
search API默認為跨多個index和type,搜索的范圍需要在請求時指定。
對多個index,比如
curl http://localhost:9200/province,country/_search?q=name:Hong*
對多個type,比如
curl http://localhost:9200/province/special,municipality/_search?q=name:Shang*
下面是GET搜索時用到的一些常見的參數
Name | Description |
---|---|
q | 基本的query string,例如q=name:Hong* |
df | 即default field |
analyzer | 指定要使用的analyzer |
analyze_wildcard | 是否對通配符進行分析,默認為false |
default_operator | 可選值為AND和OR,默認為OR |
lenient | true時忽略數據格式警告,默認為false |
_source | 改項為false時結果將不包含文檔內容, 也可以通過sourceinclude & sourceexclude對返回字段進行篩選(逗號分隔) |
stored_fields | 對返回字段進行篩選(逗號分割) |
sort | 指定排序策略,格式為 fieldName:asc/fieldName:desc |
timeout | 超時時長 |
from | 響應結果起始索引,默認為0 |
size | 響應結果數,默認為10 |
search_type | 可以為dfsquerythenfetch或者querythen_fetch,默認為后者 |
URL參數難以表述復雜的搜索,Elasticsearch提供了基於JSON格式的Query DSL來進行搜索。
雖然Query DSL提供了豐富的查詢類型,但就整體而言,提供不外乎兩種:
- leaf query: 例如match,term,range,也就是匹配特定字段的特定值
- compound query: 將其它leaf聚合到一起或者多個查詢聚合到一起。
由於DSL相關的內容過多,這里只簡單列舉幾種常用用法以作參考。
-
match all: match_all用於返回所有score為1.0內容
$ curl -X post http://10.69.201.47:9200/province/_search -d '{ "query":{ "match_all":{} } }'
-
match: 根據字段和值的匹配進行搜索
$ curl -X post http://10.69.201.47:9200/province/_search -d '{ "query":{ "match":{ "name":"Hong Kong" } } }'
-
multi match: 用多個字段進行匹配,比如下面的例子中"CN-91"是香港的code,而這里卻用name和native字段進行搜索,因此沒有結果
curl -X post http://10.69.201.47:9200/province/_search -d '{ "query":{ "multi_match":{ "query":"CN-91", "fields":["name","native"] } } }'
-
query string: 如果我想multi match所有字段呢? 可以用query string
curl -X post http://10.69.201.47:9200/province/_search -d ' { "query":{ "query_string":{ "query":"Hong" } } }'
-
term: term搜索屬於低級(low-level)搜索,也就是直接查詢倒排索引(inverted index),而不經過分析過程。 在倒排索引中不存在則無結果。 下面的例子中,用"Hong"來搜索是沒有結果的,需要用"hong"
curl -X post http://10.69.201.47:9200/province/_search -d ' { "query":{ "term":{"name":"hong"} } } '
-
range: 也屬於term搜索的一種,根據比較來限制值的范圍,支持的操作有gte,gt,lte,lt,例如
curl -X POST http://10.69.201.47:9200/province/_search -d '{ "query":{ "range":{ "code":{ "gt":100 } } } }'
-
type: 搜索對應type的文檔,屬於term level,因此type要嚴格匹配,例如
curl -X POST http://10.69.201.47:9200/province/_search -d '{ "query":{ "type":{ "value":"special" } } }'