Elasticsearch學習筆記(六)—Elasticsearch文檔及其CRUD操作

一、文檔

最頂層結構或者根對象(root object)序列化成的JSON數據(以唯一ID標識並存儲於Elasticsearch中),可以認爲對象(object)和文檔(document)是等價相通的。

文檔元數據

節點 說明
_index 文檔存儲的地方
_type 文檔代表的對象的類
_id 文檔的唯一標識

_index
索引(index)類似於關係型數據庫裏的“數據庫”——它是我們存儲和索引關聯數據的地方。數據被存儲和索引在分片(shards)中, 索引只是一個把一個或多個分片分組在一起的邏輯空間。索引名必須是全部小寫, 不能以下劃線開頭, 不能包含逗號

_type
在關係型數據庫中, 我們經常將相同類的對象存儲在一個表裏, 因爲它們有着相同的結構。 同理, 在Elasticsearch中, 我們使用相同類型(type)的文檔表示相同的“事物”, 因爲他們的數據結構也是相同的

_id
id僅僅是一個字符串, 它與 _index 和 _type 組合時, 就可以在ELasticsearch中唯一標識一個文檔。 當創建一個文檔, 你可以自定義 _id , 也可以讓Elasticsearch幫你自動生成

二、文檔CRUD操作

1. 索引文檔

指定id創建文檔 ,使用put
put /website/blog/1
{
  "title":"first blog",
  "date":"2019/10/28"
}

響應
{
  "_index": "website",
  "_type": "blog",
  "_id": "1",
  "_version": 1,
  "result": "created",
  "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
  },
  "created": true
}

響應指出請求的索引已經被成功創建, 這個索引中包含 _index 、 _type 和 _id 元數據,版本號是1

注意使用put並不能保證每次都是新建文檔,如果再次put相同id的文檔,會對原有的文檔進行覆蓋。

PUT /website/blog/1
{
  "title": "three blog",
  "feel":"nice",
  "date": "2019/10/28"
}

//響應
{
  "_index": "website",
  "_type": "blog",
  "_id": "1",
  "_version": 2,   //版本號自增
  "result": "updated",
  "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
  },
  "created": false   //表示不是新建,而是覆蓋
}

在內部, Elasticsearch已經標記舊文檔爲刪除並添加了一個完整的新文檔。 舊版本文檔不會立即消失, 但你也不能去訪問它。 Elasticsearch會在你繼續索引更多數據時清理被刪除的文檔

不指定id,使用post
post /website/blog
{
   "title":"second blog",
  "date":"2019/10/28"
}

//響應
{
  "_index": "website",
  "_type": "blog",
  "_id": "AW4TC7FgHfHdzvmX4JMA",
  "_version": 1,
  "result": "created",
  "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
  },
  "created": true
}

自動生成id:AW4TC7FgHfHdzvmX4JMA,每次都是新建文檔,不會覆蓋

自動生成的id,長度爲20個字符,URL安全,base64編碼,GUID,分佈式系統並行生成時不可能會發生衝突

使用put創建新文檔
PUT /website/blog/1?op_type=create
{
  "title": "four blog",
  "feel":"nice",
  "date": "2019/10/28"
}

//響應
{
  "error": {
    "root_cause": [
      {
        "type": "version_conflict_engine_exception",
        "reason": "[blog][1]: version conflict, document already exists (current version [2])",
        "index_uuid": "oqUDP19MSL2Y9XmPxCf1cA",
        "shard": "3",
        "index": "website"
      }
    ],
    "type": "version_conflict_engine_exception",
    "reason": "[blog][1]: version conflict, document already exists (current version [2])",
    "index_uuid": "oqUDP19MSL2Y9XmPxCf1cA",
    "shard": "3",
    "index": "website"
  },
  "status": 409
}

存在不會進行覆蓋,報409錯誤

也可以這樣寫,效果和上面相同

PUT /website/blog/3/_create
{
  "title": "five blog",
  "feel":"nice",
  "date": "2019/10/28"
}

2. 檢索文檔

使用Get

GET /website/blog/1
{
  "_index": "website",
  "_type": "blog",
  "_id": "1",
  "_version": 1,
  "found": true,
  "_source": {
    "title": "first blog",
    "date": "2019/10/28"
  }
}

響應包含了現在熟悉的元數據節點, 增加了 _source 字段, 它包含了在創建索引時我們發送給Elasticsearch的原始文檔

檢索文檔部分內容

get /website/blog/1?_source=title
{
  "_index": "website",
  "_type": "blog",
  "_id": "1",
  "_version": 1,
  "found": true,
  "_source": {
    "title": "first blog"
  }
}

只需要返回source字段

get /website/blog/1/_source
{
  "title": "first blog",
  "date": "2019/10/28"
}

查詢對應索引下所有文檔

get /website/_search
{
  "took": 1,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "failed": 0
  },
  "hits": {
    "total": 2,
    "max_score": 1,
    "hits": [
      {
        "_index": "website",
        "_type": "blog",
        "_id": "AW4TC7FgHfHdzvmX4JMA",
        "_score": 1,
        "_source": {
          "title": "second blog",
          "date": "2019/10/28"
        }
      },
      {
        "_index": "website",
        "_type": "blog",
        "_id": "1",
        "_score": 1,
        "_source": {
          "title": "first blog",
          "date": "2019/10/28"
        }
      }
    ]
  }
}

字段解釋:

took
整個搜索請求花費的毫秒數。

timeout
查詢超時與否,如果響應速度比完整的結果更重要,可以指定超時時間

get /website/_search?timeout=10ms

shards

  • total 字段 參與查詢的分片數
  • successful 字段 有多少是成功的
  • failed 字段 有多少的是失敗的

hits
total 字段 表示匹配到的文檔總數

hits 數組包含了匹配到的前10條數據,hits 數組中的每個結果都包含 _index 、 _type 和文檔的 _id 字段,每個節點都有一個 _score 字段, 這是相關性得分(relevance score), 它衡量了文檔與查詢的匹配程度。 默認的, 返回的結果中關聯性最大的文檔排在首位; 這意味着, 它是按照 _score 降序排列的。max_score 指的是所有文檔匹配查詢中 _score 的最大值

3. 修改文檔

  • put是對原有文檔的覆蓋,使用新的文檔替換舊文檔

  • update API對文檔可以進行局部更新

post /website/blog/1/_update
{
  "doc":{
    "action":"go to the moives",
    "date":"2019/10/29"
  }
}

get /website/blog/1
{
  "_index": "website",
  "_type": "blog",
  "_id": "1",
  "_version": 5,
  "found": true,
  "_source": {
    "title": "one blog",
    "feel": "nice",
    "date": "2019/10/29",
    "action": "go to the moives"
  }
}

最簡單的 update 請求表單接受一個局部文檔參數 doc , 它會合併到現有文檔中,存在的標量字段被覆蓋, 新字段被添加。例如上述例子新加了action字段,修改了date值

可以使用Groovy實現局部更新,我們可以在腳本中寫各種邏輯,然後執行即可。

4. 刪除文檔

DELETE /website/blog/AW4TDmKTHfHdzvmX4JMC
{
  "found": true,
  "_index": "website",
  "_type": "blog",
  "_id": "AW4TDmKTHfHdzvmX4JMC",
  "_version": 2,
  "result": "deleted",
  "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
  }
}

刪除一個文檔也不會立即從磁盤上移除, 它只是被標記成已刪除。 Elasticsearch
將會在你之後添加更多索引的時候纔會在後臺進行刪除內容的清理。

未找到對應刪除文檔,刪除失敗

DELETE /website/blog/AW4
{
  "found": false,
  "_index": "website",
  "_type": "blog",
  "_id": "AW4",
  "_version": 1,
  "result": "not_found",
  "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
  }
}

三、版本控制與更新衝突

對於多用戶的局部更新,由於網絡原因,可能會出現舊值覆蓋新值的情況,或者多線程常見的問題。

ES使用樂觀版本併發控制,爲每個文檔指定版本號,通過CAS比較,如果不是期望的版本號,也就是說中間被人修改過該文檔,就讓本次更新請求失敗

使用內部版本號

PUT /website/blog/3?version=2
{
  "title": "five blog",
  "feel":"sad",
  "date": "2019/10/29"
}

//響應
{
  "error": {
    "root_cause": [
      {
        "type": "version_conflict_engine_exception",
        "reason": "[blog][3]: version conflict, current version [1] is different than the one provided [2]",
        "index_uuid": "lhDIRTZ7S9mYFUIj9z6AcA",
        "shard": "4",
        "index": "website"
      }
    ],
    "type": "version_conflict_engine_exception",
    "reason": "[blog][3]: version conflict, current version [1] is different than the one provided [2]",
    "index_uuid": "lhDIRTZ7S9mYFUIj9z6AcA",
    "shard": "4",
    "index": "website"
  },
  "status": 409
}

文檔3的版本號是1,這裏傳入的version參數值爲2,所以發生衝突,更新失敗

注意,這裏傳入參數version值必須和文檔現有的version值完全相等,精確匹配,否則失敗

使用外部版本號

PUT /website/blog/4?version=3&version_type=external
{
  "title": "six blog",
  "feel":"funny",
  "date": "2019/10/29"
}

指定該文檔的版本爲4,外部版本號必須是整數, 大於0小於 9.2e+18

PUT /website/blog/4?version=3&version_type=external
{
  "title": "six blog",
  "feel":"funny",
  "date": "2019/10/29"
}
//響應
{
  "error": {
    "root_cause": [
      {
        "type": "version_conflict_engine_exception",
        "reason": "[blog][4]: version conflict, current version [3] is higher or equal to the one provided [2]",
        "index_uuid": "lhDIRTZ7S9mYFUIj9z6AcA",
        "shard": "2",
        "index": "website"
      }
    ],
    "type": "version_conflict_engine_exception",
    "reason": "[blog][4]: version conflict, current version [3] is higher or equal to the one provided [2]",
    "index_uuid": "lhDIRTZ7S9mYFUIj9z6AcA",
    "shard": "2",
    "index": "website"
  },
  "status": 409
}

報錯的原因是因爲外部版本號要求更新傳入的version > 當前文檔版本號,而不是精確匹配,否則報錯,這裏講version值傳入4即可成功更新

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