ElasticSearch索引
ElasticSearch 是一個分布式可擴展的實時搜索引擎,它建立在開源搜索引擎框架Apache Lucene基礎上。
ElasticSearch 不但包括了全文搜索功能,還支持一下特性:
1、分布式實時文件存儲,將每一個字段都編入索引,使其可以被搜索;
2、實時分析的分布式搜索引擎;
3、可以擴展到上百台服務器,處理PB級別的結構化或非結構化數據;
1、與ElasticSearch 通信
Java API
如果使用Java,ElasticSearch(簡寫ES) 內置了兩個客戶端:
1、節點客戶端:以一個無數據節點的身份加入一個集群,它自身沒有數據,但它知道什么數據在集群的哪一個節點上,然后就可以將請求轉發到正確的節點上。
2、傳輸客戶端:它本身並不加入集群,而用來向遠程集群發送請求。
這兩個客戶端都使用ElasticSearch的傳輸協議,通過9300端口與java客戶端進行通信,集群中的每個節點也是通過9300端口進行通信。
HTTP RESTful API
其它非Java語言可以采用HTTP協議,通過9200端口與ES的RESTful API進行通信。
向ES發出HTTP請求,例如請求集群中文件的數量:
http://localhost:9200/_count?pretty
ES返回一個json字符串:
{
count: 0,
_shards: {
total: 0,
successful: 0,
failed: 0
}
}
2、文檔
ES是面向文檔型數據庫,並為它們建立索引,這樣我們可以在ES中索引、搜索、排序和過濾這些文檔。
ES使用JSON作為文檔序列化的格式,例如:
一個文檔不只包含了數據。它還包含了元數據(metadata) —— 關於文檔的信息。有三個元數據元素是必須存在的,它們是:
名字 | 說明 |
---|---|
_index |
文檔存儲的地方 |
_type |
文檔代表的對象種類 |
_id |
文檔的唯一編號 |
一篇文檔通過 _index, _type以及_id來確定它的唯一性,下面對比了這幾個名詞與關系型數據庫的關系:
關系型數據庫 | 數據庫 | 表 | 行 | 列 |
ElasticSearch | 索引 | 類型 | 文檔 | 字段 |
一個ES集群可以包含多個索引(數據庫),每個索引又包含了很多類型(表),類型中包含了很多文檔(行),每個文檔又包含了很多字段(列)。
在ES中,索引有多重含義:
1、索引(名詞),一個索引就類似與傳統關系型數據庫中的數據庫,這里就是存儲相關文檔的地方;
2、索引(動詞),把一個文檔存儲到一個索引中的過程,這樣它才能被檢索,這個過程類似於SQL中的INSERT命令;
3、反向索引,通常每個文檔中的字段都被創建了反向索引,如果一個字段沒有反向索引,那么它將不能被搜索。
下面演示通過HTTP請求如何建立索引:
1、添加文檔(PUT請求)
curl "127.0.0.1:9200/sina/employee/11412" -d '{"name":"chenqi","age":30}'
返回值:
{"_index":"sina","_type":"employee","_id":"11412","_version":1,"created":true}
sina是索引名,employee是類型名,11412是文檔ID,它們組合構成了請求url的路徑;
注意:_version 字段表示文檔的版本,每當文檔發生變化時,_version就會增大。
總結下添加文檔的方法:
PUT /{index}/{type}/{id} { "field": "value", ... }
如果在請求中不指定id,ES會自動分配一個自增ID;
如果指定的文檔id在索引中已存在,則更新原文檔,此時文檔的_version會增加1,並且_created字段為false;
如果只打算在文檔不存在的情況下才創建新文檔,則使用下面兩種方法之一即可:
PUT /{index}/{type}/{id}?op_type=create PUT /{index}/{type}/{id}/_create
創建成功返回201 Created;如果文檔ID已存在,則請求失敗,並返回409 Conflict。
2、局部更新
使用更新請求最簡單的一種用途就是添加新數據。新的數據會被合並到現有數據中,而如果存在相同的字段,就會被新的數據所替換。
例如我們可以為我們的博客添加tags和views字段:
POST /website/blog/1/_update { "doc" : { "tags" : [ "testing" ], "views": 0 } }
ES還可以使用腳本來更新文檔,例如:
POST /sina/employee/11412/_update { "script" : "ctx._source.age+=1" }
更新一篇可能不存在的文檔,使用upsert
參數來設定文檔不存在時,它應該被創建:
POST /website/pageviews/1/_update { "script" : "ctx._source.views+=1", "upsert": { "views": 1 } }
3、檢索文檔(GET請求)
例如:
curl "127.0.0.1:9200/sina/employee/11412"
返回值:
{"_index":"sina","_type":"employee","_id":"11412","_version":1,"found":true,"_source":{"name":"chenqi","age":30}}
如果請求一個不存在的文檔,如:
curl "127.0.0.1:9200/sina/employee/11410"
則返回:
{"_index":"sina","_type":"employee","_id":"11410","found":false}
也可以檢索所有文檔(不指定文檔ID或類型ID),例如:
URL | 說明 |
---|---|
/_search |
搜索所有的索引和類型 |
/gb/_search |
搜索索引gb 中的所有類型 |
/gb,us/_search |
搜索索引gb 以及us 中的所有類型 |
/g*,u*/_search |
搜索所有以g 或u 開頭的索引中的所有類型 |
/gb/user/_search |
搜索索引gb 中類型user 內的所有文檔 |
/gb,us/user,tweet/_search |
搜索索引user 以及tweet 中類型gb and us 內的所有文檔 |
/_all/user,tweet/_search |
搜索所有索引中類型為user 以及tweet 內的所有文檔 |
注意:當我們在索引一個文檔的時候,ES會將所有字段的值都匯總到一個大的字符串中,並將它索引成一個特殊的字段_all:
{
"tweet": "However did I manage before Elasticsearch?", "date": "2014-09-14", "name": "Mary Jones", "user_id": 1 }
就好像我們已經添加了一個叫做_all
的字段:
"However did I manage before Elasticsearch? 2014-09-14 Mary Jones 1"
利用all字段,可以檢索包含指定字符串的文檔,如下面,搜索包含mary的文檔:
GET /_search?q=mary
還可以根據指定字段來檢索文檔:
http://10.69.2.49:9200/_search?q=age:28
只返回字段age=28的文檔;
字段前加前綴"+"
表示必須要滿足我們的查詢匹配條件,而前綴"-"
則表示絕對不能匹配條件。沒有+
或者-
的表示可選條件。匹配的越多,文檔的相關性就越大。
再實現一個復雜查詢:
- 字段
name
包含"mary"
或"john"
date
大於2014-09-10
_all
字段中包含"aggregations"
或"geo"
q=+name:(mary john) +date:>2014-09-10 +(aggregations geo)
另外,size和from參數可以控制翻頁:
參數 | 說明 |
---|---|
size |
每次返回多少個結果,默認值為10 |
from |
忽略最初的幾條結果,默認值為0 |
假設每頁顯示5條結果,那么1至3頁的請求就是:
GET /_search?size=5 GET /_search?size=5&from=5 GET /_search?size=5&from=10
最后,使用source參數可以指定只返回文檔的某幾個字段,例如:
http://10.69.2.49:9200/_search?q=age:28&_source=name
http://10.69.2.49:9200/sina/employee/11412/_source
4、檢查文檔(HEAD請求)
curl -i -XHEAD /{index}/{type}/{id}
存在返回200,否則返回404。
5、刪除文檔(DELETE請求):
curl "127.0.0.1:9200/sina/employee/11412" -XDELETE
刪除文檔也會增加_version字段。
3、搜索
前面提到了可以在GET請求中以q=field:value 的形式進行簡易查詢,此外,ES還提供了更加強大的Query DSL(Domain Specific Language)查詢語言,通過它可以完成更加復雜的搜索任務。
例如下面兩種查詢方式等價:
curl "127.0.0.1:9200/sina/_search" -d '{"query":{"match":{"age":28}}}' curl "127.0.0.1:9200/sina/_search?q=age:28"
其中,json串{"query":{"match":{"age":28}}} 就是一個Query DSL。
下面是一個更為復雜的Query DSL,用於找到name字段等於"chenqi"的文檔,並且附加了一個過濾條件:age>30。
{ "query":{ "filtered":{ "filter":{ "range":{ "age":{"gt":30} } }, "query":{ "match":{ "name":"chenqi" } } } } }
注:將match換成match_phrase,可以進行段落或短語的精確匹配。
4、沖突處理
Elasticsearch是分布式的。當文檔被創建、更新或者刪除時,新版本的文檔就會被復制到集群中的其他節點上。ES即是同步的又是異步的,也就是說復制的請求被平行發送出去,然后可能會混亂地到達目的地。這就需要一種方法能夠保證新的數據不會被舊數據所覆蓋。
我們在上文提到每當有索引、put和刪除的操作時,無論文檔有沒有變化,它的_version都會增加。Elasticsearch使用_version來確保所有的改變操作都被正確排序。如果一個舊的版本出現在新版本之后,它就會被忽略掉。
我們可以利用_version的優點來確保我們程序修改的數據沖突不會造成數據丟失。我們可以按照我們的想法來指定_version的數字。如果數字錯誤,請求就是失敗。
我們來創建一個新的博文:
PUT /website/blog/1/_create { "title": "My first blog entry", "text": "Just trying this out..." }
此時,文檔的version=1;
現在,我們試着重新索引文檔以保存變化,我們這樣指定了version
的數字:
PUT /website/blog/1?version=1 { "title": "My first blog entry", "text": "Starting to get the hang of this..." }
當索引中文檔的_version
是1
時,更新才生效,更新后version=2。
當我們再執行同樣的索引請求,並依舊指定version=1
時,ES就會返回一個409 Conflict
的響應碼,返回內容如下:
{ "error" : "VersionConflictEngineException[[website][2] [blog][1]: version conflict, current [2], provided [1]]", "status" : 409 }
這里面指出了文檔當前的_version
數字是2
,而我們要求的數字是1
。
所有的有關於更新或者刪除文檔的API都支持version
這個參數,有了它你就通過修改你的程序來使用並發控制。
參考文檔:
http://www.learnes.net/data/mget.html