ElasticSearch之一——索引


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 搜索所有以gu開頭的索引中的所有類型
/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..."
}

當索引中文檔的_version1時,更新才生效,更新后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

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM