天天看點

Elasticsearch如何建立索引,添加,删除,更新文檔

文章目錄

    • 準備工作
      • 檢查 es 及 Kibana 是否運作正常
    • 建立索引及文檔
      • 建立文檔相關知識點
      • mulit-field 字段
      • 關于兩個type的解釋
      • 關于兩個keyword的解釋
      • mulit-field應用場景
    • es的資料類型:
      • 資料類型相關知識
      • 微調映射帶來了一些優勢
    • 建立文檔
      • 自動 ID 生成
    • 更新文檔
    • UPSERT文檔
    • 删除文檔
    • 檢查一個索引是否存在
    • 删除索引
    • 批處理指令
    • Freeze/unfreeze index
    • 下期更精彩

準備工作

  • 了解es基本概念:elasticsearch(es)背景故事與基本概念
  • 安裝es:Linux安裝Elasticsearch詳細教程
  • 安裝kibana:Linux安裝Kibana詳細教程
  • 熟悉Json
  • 熟悉REST接口

檢查 es 及 Kibana 是否運作正常

Elasticsearch如何建立索引,添加,删除,更新文檔

建立索引及文檔

建立一個名為 twitter 的索引(index),并插入一個文檔(document)

PUT twitter/_doc/1
{
 "user": "GB",
 "uid": 1,
 "city": "Beijing",
 "province": "Beijing",
 "country": "China"
}
           

在關系型資料庫中,需要使用DDL語句來建立資料庫和表,然後才可以插入資料, es 不是必須的。

我們可以看到在 Kibana 右邊的視窗中有下面的輸出:

Elasticsearch如何建立索引,添加,删除,更新文檔

建立文檔相關知識點

  • 在關系型資料庫中,建立表(對應es的文檔)我們必須指定資料類型和長度,而es可以動态建立索引 mapping(mapping 指資料類型) 。
  • 當我們建立文檔時,如果沒有建立對應的mapping,那麼 es會根據所輸入字段的資料猜測資料類型,例如上面的 user 被被認為是 text 類型,而 uid 将被猜測為整數類型。
  • 在插入文檔時,如果該文檔的 ID 已經存在,那麼就更新現有的文檔;如果該文檔從來沒有存在過,那麼就建立新的文檔。
  • 如果更新時該文檔有新的字段并且這個字段在現有的 mapping 中沒有出現,那麼 es會根據 schem on write 的政策來推測該字段的類型,并更新目前的 mapping 到最新的狀态。

動态 mapping 可能會導緻某些字段不是我們想要的資料類型,進而導緻索引請求失敗

解決方案(顯式 mapping)

明确定義索引映射

首先我們檢視索引的資料類型(相當于關系型資料庫的表結構)

GET twitter/_mapping
           

傳回結果

{
  "twitter" : {
    "mappings" : {
      "properties" : {
        "city" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "country" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "province" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "uid" : {
          "type" : "long"
        },
        "user" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        }
      }
    }
  }
}
           

我們拿city字段來分析

"city" : {
      "type" : "text",
      "fields" : {
        "keyword" : {
          "type" : "keyword",
          "ignore_above" : 256
        }
      }
    }
           

mulit-field 字段

關于兩個type的解釋

為一個字段的不同目的建立不同方式索引,我們定義字段 city 為 text 類型, 但是text 類型的資料在攝入的時候會分詞,這樣它可以實作搜尋的功能。 同時,這個字段又被定義為 keyword 類型,keyword 類型可以讓我們針對它進行精确比對(比如區分大小寫,空格等符号),聚合和排序。

關于兩個keyword的解釋

第一個 keyword 可以是随便定義,第二個 keyword 才是它的類型

mulit-field應用場景

一個字段需要同時進行搜尋和聚合

如果我們僅需要搜尋或者聚合,那麼我們隻需要定義一種類型,可以提高資料攝入的速度,同時它也可以減少磁盤的使用。

es的資料類型:

  • text:全文搜尋字元串
  • keyword:用于精确字元串比對和聚合
  • date 及 date_nanos:格式化為日期或數字日期的字元串
  • byte,short,integer,long:整數類型 boolean:布爾類型
  • float,double,half_float:浮點數類型 分級的類型:object 及 nested

資料類型相關知識

  • 一般情況下,es可以很好的了解文檔的結構并自動建立映射(mapping)定義。
  • 自動建立映射使用無模式(schemaless)方法快速攝取資料,無需擔心字段類型。
  • 為了在索引中獲得更好搜尋的結果和更好性能,我們有時需要需要手動定義映射。

微調映射帶來了一些優勢

  • 減少磁盤上的索引大小(禁用自定義字段的功能) 僅索引感興趣的字段(一般加速) 用于快速搜尋或實時分析(例如聚合)
  • 正确定義字段是否必須分詞為多個 token 或單個 token 定義映射類型,例如地理點、suggester、向量等

假如,我們想建立一個索引 test,并且含有 id 及 message 字段。id 字段為 keyword 類型,而 message 字段為 text 類型,那麼我們可以使用如下的方法來建立:

PUT test
{
  "mappings": {
    "properties": {
      "id": {
        "type": "keyword"
      },
      "message": {
        "type": "text"
      }
    }
  }
}
           

我們甚至可以使用如下的 API 來追加一個新的字段 age,并且它的類型為 long 類型:

PUT test/_mapping
{
  "properties": {
    "age": {
      "type": "long"
    }
  }
}
           

我們可以使用如下的指令來檢視索引 test 的最終 mapping:

GET test/_mapping
           

上面的指令顯示的結果為:

{
  "test" : {
    "mappings" : {
      "properties" : {
        "age" : {
          "type" : "long"
        },
        "id" : {
          "type" : "keyword"
        },
        "message" : {
          "type" : "text"
        }
      }
    }
  }
}
           

在上面,我們可以看出來我們已經成功地建立了一個叫做 twitter 的 index。通過這樣的方法,我們可以自動建立一個 index。如果大家不喜歡自動建立一個 index,我們可以修改如下的一個設定:

PUT _cluster/settings
{
    "persistent": {
        "action.auto_create_index": "false" 
    }
}
           

在絕大多數情況下,我們并不需要這麼做,除非你知道你要做什麼。如果你想開啟自動建立索引,你必須配置 action.auto_create_index 以允許這些建立以下索引的元件:

PUT _cluster/settings
{
  "persistent": {
    "action.auto_create_index": ".monitoring*,.watches,.triggered_watches,.watcher-history*,.ml*"
  }
}
           

如果我們向索引中插入一條文檔這個文檔并不馬上可以進行搜尋。而是要過1s

以為有一個 refresh timer 來定時完成refresh操作。這個周期為1秒。這就是為什麼 es可以實作秒級的搜尋。當然這個 timer 的周期也可以在索引的設定中進行配置。如果我們想讓我們的結果馬上可以對搜尋可見,我們可以用如下的方法:

PUT twitter/_doc/1?refresh=true
{
 "user": "GB",
 "uid": 1,
 "city": "Beijing",
 "province": "Beijing",
 "country": "China"
}
           

上面的方式可以強制使 es進行 refresh 的操作,當然這個是有代價的。頻繁的進行這種操作,會使es 變慢。是以我們一般設定refresh=wait_for。這樣相當于一個同步的操作,它等待下一個 refresh 周期發生完後,才傳回。

PUT twitter/_doc/1?refresh=wait_for
{
 "user": "GB",
 "uid": 1,
 "city": "Beijing",
 "province": "Beijing",
 "country": "China"
}
           

es為我們建立了一個被叫做 _doc 的 type。自從 es 6.0 以後,一個 index 隻能有一個 type。如果我們建立另外一個 type 的話,會報錯。這裡發現有一個版本(_version)資訊,document 第一次建立 _id 為1它會顯示為 1之後如果我們更改這個 document,它的版本會每次自動增加1。

POST twitter/_doc/1
{
 "user": "GB",
 "uid": 1,
 "city": "Shenzhen",
 "province": "Guangdong",
 "country": "China"
}
           
Elasticsearch如何建立索引,添加,删除,更新文檔

建立文檔

PUT twitter/_create/1
{
 "user": "GB",
 "uid": 1,
 "city": "Shenzhen",
 "province": "Guangdong",
 "country": "China"
}
           

如果文檔已經存在的話,會報錯

Elasticsearch如何建立索引,添加,删除,更新文檔

另一種寫法:在請求時帶上 op_type。它可以有兩種值:index 及 create。

PUT twitter/_doc/1?op_type=create
{
  "user": "雙榆樹-張三",
  "message": "今兒天氣不錯啊,出去轉轉去",
  "uid": 2,
  "age": 20,
  "city": "北京",
  "province": "北京",
  "country": "中國",
  "address": "中國北京市海澱區",
  "location": {
    "lat": "39.970718",
    "lon": "116.325747"
  }
}
           

curl寫法

curl -XPUT 'http://localhost:9200/twitter/_doc/1?pretty' -H 'Content-Type: application/json' -d '
{
 "user": "GB",
 "uid": 1,
 "city": "Shenzhen",
 "province": "Guangdong",
 "country": "China"
}'
           

我們可以通過如下的指令來檢視被修改的文檔:

Elasticsearch如何建立索引,添加,删除,更新文檔

我們可以看到在右邊顯示了我們被修改的文檔的結果。

如果我們隻想得到這個文檔的 _source 部分,我們可以使用如下的指令格式:

GET twitter/_doc/1/_source
           

在 es7.0 之後,在 type 最終要被廢除的情況下,建議使用如下的方法來獲得 _source:

自動 ID 生成

如果我們不指定文檔的 ID,建立一個新的文檔,轉而讓 es自動幫我們生成一個 ID,這樣的速度更快。在這種情況下,我們必須使用 POST,而不是 PUT,例如:

POST my_index/_doc
{
 "content": "this is really cool"
}
           

傳回的結果:

{
 "_index" : "my_index",
 "_type" : "_doc",
 "_id" : "egiY4nEBQTokU_uEEGZz",
 "_version" : 1,
 "result" : "created",
 "_shards" : {
 "total" : 2,
 "successful" : 1,
 "failed" : 0
 },
 "_seq_no" : 0,
 "_primary_term" : 1
}
           

在正常的情況下,目前複制組的所有分片都執行了索引操作時,es 從索引操作傳回。

設定異步複制允許我們在主分片上同步執行索引操作,在副本分片上異步執行。這樣,API 調用會更快地傳回響應操作。我們可以這樣來進行調用:

POST my_index/_doc?replication=async
{
 "content": "this is really cool"
}
           

如果我們隻對 source 的内容感興趣的話,我們可以使用:

GET twitter/_doc/1/_source
           

這樣我們可以直接得到 source 的資訊:

{
 "user" : "雙榆樹-張三",
 "message" : "今兒天氣不錯啊,出去轉轉去",
 "uid" : 2,
 "age" : 20,
 "city" : "北京",
 "province" : "北京",
 "country" : "中國",
 "address" : "中國北京市海澱區",
 "location" : {
 "lat" : "39.970718",
 "lon" : "116.325747"
 }
}
           

我們也可以隻擷取 source 的部分字段:

GET twitter/_doc/1?_source=city,age,province
           
Elasticsearch如何建立索引,添加,删除,更新文檔

如果你想一次請求查找多個文檔,我們可以使用 _mget 接口:

GET _mget
{
  "docs": [
    {
      "_index": "twitter",
      "_id": 1
    },
    {
      "_index": "twitter",
      "_id": 2
    }
  ]
}
           

我們也可以隻獲得部分字段:

GET _mget
{
  "docs": [
    {
      "_index": "twitter",
      "_id": 1,
      "_source":["age", "city"]
    },
    {
      "_index": "twitter",
      "_id": 2,
      "_source":["province", "address"]
    }
  ]
}
           

在這裡,我們同時請求 id 為 1 和 2 的兩個文檔。

我們也可以簡單地寫為:

GET twitter/_doc/_mget
{
 "ids": ["1", "2"]
}
           

它和上面的做一個是一樣的。使用一個指令同時擷取 id 為 1 及 2 的文檔。

在上面當我們寫入資料時,我們有意識地把文檔的 id 在指令中寫了出來。如果我們不寫這個 id 的話,ES 會幫我們自動生産一個 id:

Elasticsearch如何建立索引,添加,删除,更新文檔

我可以看到右邊的一個 id 像是一個随機的數值,同時我們可以看到它的一個版本資訊為1。在實際的需要有大量導入資料的情況下,我們建議讓系統自動幫我們生成一個 id,這樣可以提高導入的速度。假如我們指定一個 id,通常 ES 會先查詢這個 id 是否存在,然後在覺得是更新之前的文檔還是建立一個新的文檔。這裡是分兩步走。顯然它比直接建立一個文檔要慢!

我們也可以看出來系統所給出來的字段都是以下劃線的形式給出來的,比如:_id, _shards, _index, _typed 等

更新文檔

我們接下來看一下如何修改一個文檔。在上面我們看到了可以使用 POST 的指令來修改改一個文檔。通常我們使用 POST 來建立一個新的文檔。在使用 POST 的時候,我們甚至不用去指定特定的 id,系統會幫我們自動生成。但是我們修改一個文檔時,我們通常會使用 PUT 來進行操作,并且,我們需要指定一個特定的 id 來進行修改:

PUT twitter/_doc/1
{
   "user": "GB",
   "uid": 1,
   "city": "北京",
   "province": "北京",
   "country": "中國",
   "location":{
     "lat":"29.084661",
     "lon":"111.335210"
   }
}
           
Elasticsearch如何建立索引,添加,删除,更新文檔

如上面所示,我們使用 PUT 指令來對我們的 id 為1的文檔進行修改。我們也可以使用我們上面學過的 GET 來進行查詢:

{
  "_index" : "twitter",
  "_type" : "_doc",
  "_id" : "1",
  "_version" : 8,
  "_seq_no" : 13,
  "_primary_term" : 1,
  "found" : true,
  "_source" : {
    "user" : "GB",
    "uid" : 1,
    "city" : "北京",
    "province" : "北京",
    "country" : "中國",
    "location" : {
      "lat" : "29.084661",
      "lon" : "111.335210"
    }
  }
}
           

顯然,我們的這個文檔已經被成功修改了。

我們使用 PUT 的這個方法,每次修改一個文檔時,我們需要把文檔的每一項都要寫出來。這對于有些情況來說,并不友善,我們可以使用如下的方法來進行修改:

POST twitter/_update/1
{
  "doc": {
    "city": "成都",
    "province": "四川"
  }
}
           

我們可以使用如上的指令來修改我們的部分資料。同樣我們可以使用 GET 來查詢我們的修改是否成功:

Elasticsearch如何建立索引,添加,删除,更新文檔

從上面的顯示中,我們可以看出來,我們的修改是成功的,雖然在我們修改時,我們隻提供了部分的資料。

在關系資料庫中,我們通常是對資料庫進行搜尋,讓後才進行修改。在這種情況下,我們事先通常并不知道文檔的 id。我們需要通過查詢的方式來進行查詢,讓後進行修改。ES 也提供了相應的 REST 接口。

POST twitter/_update_by_query
{
  "query": {
    "match": {
      "user": "GB"
    }
  },
  "script": {
    "source": "ctx._source.city = params.city;ctx._source.province = params.province;ctx._source.country = params.country",
    "lang": "painless",
    "params": {
      "city": "上海",
      "province": "上海",
      "country": "中國"
    }
  }
}
           

對于那些名字是中文字段的文檔來說,在 painless 語言中,直接打入中文字段名字,并不能被認可。我們可以使用如下的方式來操作:

POST edd/_update_by_query
{
  "query": {
    "match": {
      "姓名": "張彬"
    }
  },
  "script": {
    "source": "ctx._source[\"簽到狀态\"] = params[\"簽到狀态\"]",
    "lang": "painless",
    "params" : {
      "簽到狀态":"已簽到"
    }
  }
}
           

在上面我們使用一個中括号并 escape 引号的方式來操作。有關 Painless 的程式設計,你可以參閱文章 “Elasticsearch: Painless script 程式設計”。

我們可以通過上面的方法搜尋 user 為 GB 的使用者,并且把它的資料項修改為:

"city" : "上海",
    "province": "上海",
    "country": "中國"
           

我們也可以通過 update 接口,使用 script 的方法來進行修改。這個方法也是需要知道文檔的 id:

POST twitter/_update/1
{
  "script" : {
      "source": "ctx._source.city=params.city",
      "lang": "painless",
      "params": {
        "city": "長沙"
      }
  }
}
           

在我們使用上面的方法更新文檔時,如果目前的文檔 id 不存在,那麼我們甚至可以使用 upsert 屬性來建立一個文檔:

POST twitter/_update/1
{
  "script" : {
      "source": "ctx._source.city=params.city",
      "lang": "painless",
      "params": {
        "city": "長沙"
      }
  },
  "upsert": {
    "city": "長沙"
  }
}
           

和前面的方法一下,我們可以使用 GET 來查詢,我們的結果是否已經改變:

{
  "_index" : "twitter",
  "_type" : "_doc",
  "_id" : "1",
  "_version" : 18,
  "_seq_no" : 39,
  "_primary_term" : 1,
  "found" : true,
  "_source" : {
    "uid" : 1,
    "country" : "中國",
    "province" : "上海",
    "city" : "長沙",
    "location" : {
      "lon" : "111.335210",
      "lat" : "29.084661"
    },
    "user" : "GB"
  }
}
           

如果你涉及到多個用戶端同時更新一個索引的情況,你需要閱讀文章 “深刻了解文檔中的 verision 及 樂觀并發控制”。

我們甚至可以使用 _update 接口使用 ctx[‘_op’] 來達到删除一個文檔的目的,比如:

POST twitter/_update/1
{
  "script": {
    "source": """
    if(ctx._source.uid == 1) {
      ctx.op = 'delete'
    } else {
      ctx.op = "none"
    }
    """
  }
}
           

當檢測文檔的 uid 是否為 1,如果為 1 的話,那麼該文檔将被删除,否則将不做任何事情。

我還可以充分使用 script 的一些進階操作,比如我們可以通過如下的方法來添加一個嶄新的字段:

POST twitter/_update/1
{
  "script" : {
      "source": "ctx._source.newfield=4",
      "lang": "painless"
  }
}
           

通過上面的操作,我們可以發現,我們新增加了一個叫做 newfield 的字段。當然我們也可以使用如下的方法來删除一個字段:

POST twitter/_update/1
{
  "script" : {
      "source": "ctx._source.remove(\"newfield\")",
      "lang": "painless"
  }
}
           

在上面的指令中,我們通過 remove 删除了剛才被建立的 newfiled 字段。我們可以通過如下的指令來進行檢視:

GET twitter/_doc/1
           

在這裡請注意的是:一旦一個字段被建立,那麼它就會存在于更新的 mapping 中。即便針對 id 為 1 的文檔删除了 newfield,但是 newfield 還将繼續存在于 twitter 的 mapping 中。我們可以使用如下的指令來檢視 twitter 的 mapping:

GET twitter/_mapping
           

這裡值得注意是:對于多使用者,我們可以從各個用戶端同時更新,這裡可能會造成更新資料的一緻性問題。為了避免這種現象的出現,請閱讀我的另外一篇文章 “Elasticsearch:深刻了解文檔中的 verision 及樂觀并發控制”。

UPSERT文檔

僅在文檔事先存在的情況下,我們在前面的代碼中看到的部分更新才有效。 如果具有給定 id 的文檔不存在,Elasticsearch 将傳回一個錯誤,指出該文檔丢失。 讓我們了解如何使用更新 API 進行 upsert 操作。 術語 “upsert” 寬松地表示更新或插入,即更新文檔(如果存在),否則,插入新文檔。

doc_as_upsert 參數檢查具有給定ID的文檔是否已經存在,并将提供的 doc 與現有文檔合并。 如果不存在具有給定 id 的文檔,則會插入具有給定文檔内容的新文檔。

下面的示例使用 doc_as_upsert 合并到 id 為 3 的文檔中,或者如果不存在則插入一個新文檔:

POST /catalog/_update/3
{
  "doc": {
    "author": "Albert Paro",
    "title": "Elasticsearch 5.0 Cookbook",
    "description": "Elasticsearch 5.0 Cookbook Third Edition",
    "price": "54.99"
  },
  "doc_as_upsert": true
}
           

檢查一個文檔是否存在

有時候我們想知道一個文檔是否存在,我們可以使用如下的方法:

這個 HEAD 接口可以很友善地告訴我們在 twitter 的索引裡是否有一 id 為1的文檔:

Elasticsearch如何建立索引,添加,删除,更新文檔

上面的傳回值表面 id 為1的文檔時存在的。

删除文檔

根據ID删除文檔

DELETE twitter/_doc/1
           
Elasticsearch如何建立索引,添加,删除,更新文檔

根據條件删除

POST twitter/_delete_by_query
{
  "query": {
    "match": {
      "city": "上海"
    }
  }
}
           

這樣我們就把所有的 city 是上海的文檔都删除了。

檢查一個索引是否存在

我們可以使用如下的指令來檢查一個索引是否存在:

HEAD twitter
           

如果 twitter 索引存在,那麼上面的指令會傳回:

200 - OK
           

否則就會傳回:

删除索引

删除一個索引 是非常直接的。我們可以直接使用如下的指令來進行删除:

DELETE twitter
           
Elasticsearch如何建立索引,添加,删除,更新文檔

當我們執行完這一條語句後,所有的在 twitter 中的所有的文檔都将被删除。

批處理指令

上面我們已經了解了如何使用 REST 接口來建立一個 index,并為之建立(Create),讀取(Read),修改(Update),删除文檔(Delete)(CRUD)。因為每一次操作都是一個 REST 請求,對于大量的資料進行操作的話,這個顯得比較慢。ES 建立一個批量處理的指令給我們使用。這樣我們在一次的 REST 請求中,我們就可以完成很多的操作。這無疑是一個非常大的好處。下面,我們來介紹一下這個 _bulk 指令。

我們使用如下的指令來進行 bulk 操作:

POST _bulk
{ "index" : { "_index" : "twitter", "_id": 1} }
{"user":"雙榆樹-張三","message":"今兒天氣不錯啊,出去轉轉去","uid":2,"age":20,"city":"北京","province":"北京","country":"中國","address":"中國北京市海澱區","location":{"lat":"39.970718","lon":"116.325747"}}
{ "index" : { "_index" : "twitter", "_id": 2 }}
{"user":"東城區-老劉","message":"出發,下一站雲南!","uid":3,"age":30,"city":"北京","province":"北京","country":"中國","address":"中國北京市東城區台基廠三條3号","location":{"lat":"39.904313","lon":"116.412754"}}
{ "index" : { "_index" : "twitter", "_id": 3} }
{"user":"東城區-李四","message":"happy birthday!","uid":4,"age":30,"city":"北京","province":"北京","country":"中國","address":"中國北京市東城區","location":{"lat":"39.893801","lon":"116.408986"}}
{ "index" : { "_index" : "twitter", "_id": 4} }
{"user":"朝陽區-老賈","message":"123,gogogo","uid":5,"age":35,"city":"北京","province":"北京","country":"中國","address":"中國北京市朝陽區建國門","location":{"lat":"39.718256","lon":"116.367910"}}
{ "index" : { "_index" : "twitter", "_id": 5} }
{"user":"朝陽區-老王","message":"Happy BirthDay My Friend!","uid":6,"age":50,"city":"北京","province":"北京","country":"中國","address":"中國北京市朝陽區國貿","location":{"lat":"39.918256","lon":"116.467910"}}
{ "index" : { "_index" : "twitter", "_id": 6} }
{"user":"虹橋-老吳","message":"好友來了都今天我生日,好友來了,什麼 birthday happy 就成!","uid":7,"age":90,"city":"上海","province":"上海","country":"中國","address":"中國上海市闵行區","location":{"lat":"31.175927","lon":"121.383328"}}
           

在上面的指令中,我們使用了 bulk 指令來完成我們的操作。在輸入指令時,我們需要特别的注意:千萬不要添加除了換行以外的空格,否則會導緻錯誤。在上面我們使用的 index 用來建立一個文檔。為了說明問題的友善,我們在每一個文檔裡,特别指定了每個文檔的 id。當執行完我們的批處理 bulk 指令後,我們可以看到:

Elasticsearch如何建立索引,添加,删除,更新文檔

顯然,我們的建立時成功的。因為我運作了兩遍的原因,是以你看到的是 version 為 2 的傳回結果。bulk 指令是高效的,因為一個請求就可以處理很多個操作。在實際的使用中,我們必須注意的是:一個好的起點是批量處理 1,000 到 5,000 個文檔,總有效負載在 5MB 到 15MB 之間。如果我們的 payload 過大,那麼可能會造成請求的失敗。如果你想更進一步探讨的話,你可以使用檔案 accounts.json 來做實驗。更多是有資料可以在位址 加載示例資料 | Kibana 使用者手冊 | Elastic 進行下載下傳。

如果你想查詢到所有的輸入的文檔,我們可以使用如下的指令來進行查詢:

POST twitter/_search
           

這是一個查詢的指令,在以後的章節中,我們将再詳細介紹。通過上面的指令,我們可以看到所有的已經輸入的文檔。

Elasticsearch如何建立索引,添加,删除,更新文檔

上面的結果顯示,我們已經有6條生産的文檔記錄已經生産了。

我們可以通過使用 _count 指令來查詢有多少條資料:

GET twitter/_count
           
Elasticsearch如何建立索引,添加,删除,更新文檔

上面我們已經使用了 index 來建立6條文檔記錄。我也可以嘗試其它的指令,比如 create:

POST _bulk
{ "create" : { "_index" : "twitter", "_id": 1} }
{"user":"雙榆樹-張三","message":"今兒天氣不錯啊,出去轉轉去","uid":2,"age":20,"city":"北京","province":"北京","country":"中國","address":"中國北京市海澱區","location":{"lat":"39.970718","lon":"116.325747"}}
{ "index" : { "_index" : "twitter", "_id": 2 }}
{"user":"東城區-老劉","message":"出發,下一站雲南!","uid":3,"age":30,"city":"北京","province":"北京","country":"中國","address":"中國北京市東城區台基廠三條3号","location":{"lat":"39.904313","lon":"116.412754"}}
{ "index" : { "_index" : "twitter", "_id": 3} }
{"user":"東城區-李四","message":"happy birthday!","uid":4,"age":30,"city":"北京","province":"北京","country":"中國","address":"中國北京市東城區","location":{"lat":"39.893801","lon":"116.408986"}}
{ "index" : { "_index" : "twitter", "_id": 4} }
{"user":"朝陽區-老賈","message":"123,gogogo","uid":5,"age":35,"city":"北京","province":"北京","country":"中國","address":"中國北京市朝陽區建國門","location":{"lat":"39.718256","lon":"116.367910"}}
{ "index" : { "_index" : "twitter", "_id": 5} }
{"user":"朝陽區-老王","message":"Happy BirthDay My Friend!","uid":6,"age":50,"city":"北京","province":"北京","country":"中國","address":"中國北京市朝陽區國貿","location":{"lat":"39.918256","lon":"116.467910"}}
{ "index" : { "_index" : "twitter", "_id": 6} }
{"user":"虹橋-老吳","message":"好友來了都今天我生日,好友來了,什麼 birthday happy 就成!","uid":7,"age":90,"city":"上海","province":"上海","country":"中國","address":"中國上海市闵行區","location":{"lat":"31.175927","lon":"121.383328"}}
           

在上面,我們的第一個記錄裡,我們使用了 create 來建立第一個 id 為1的記錄。因為之前,我們已經建立過了,是以我們可以看到如下的資訊:

Elasticsearch如何建立索引,添加,删除,更新文檔

從上面的資訊,我們可以看出來 index 和 create 的差別。index 總是可以成功,它可以覆寫之前的已經建立的文檔,但是 create 則不行,如果已經有以那個 id 為名義的文檔,就不會成功。

我們可以使用 delete 來删除一個已經建立好的文檔:

POST _bulk
{ "delete" : { "_index" : "twitter", "_id": 1 }}
           
Elasticsearch如何建立索引,添加,删除,更新文檔

我們可以看到 id 為1的文檔已經被删除了。我可以通過如下的指令來檢視一下:

Elasticsearch如何建立索引,添加,删除,更新文檔

顯然,我們已經把 id 為1的文檔已經成功删除了。

我們也可以是使用 update 來進行更新一個文檔。

POST _bulk
{ "update" : { "_index" : "twitter", "_id": 2 }}
{"doc": { "city": "長沙"}}
           

運作的結果如下:

Elasticsearch如何建立索引,添加,删除,更新文檔

同樣,我們可以使用如下的方法來檢視我們修改的結果:

Elasticsearch如何建立索引,添加,删除,更新文檔

我們可以清楚地看到我們已經成功地把城市 city 修改為 “長沙”。

注意:通過 bulk API 為資料編制索引時,你不應在叢集上進行任何查詢/搜尋。 這樣做可能會導緻嚴重的性能問題。

如果你對腳本程式設計比較熟悉的話,你可能更希望通過腳本的方法來把大量的資料通過腳本的方式來導入:

$ curl -s -H "Content-Type: application/x-ndjson" -XPOST localhost:9200/_bulk --data-binary @request_example.json
           

這裡的 request_example.json 就是我們的 JSON 資料檔案。我們可以做如下的實驗:

下載下傳測試資料:

wget https://github.com/liu-xiao-guo/elasticsearch-bulk-api-data/blob/master/es.json
           

然後在指令行中打入如下的指令:

curl -u elastic:123456 -s -H "Content-Type: application/x-ndjson" -XPOST localhost:9200/_bulk --data-binary @es.json
           

這裡的 “elastic:123456” 是我們的 Elasticsearch 的使用者名及密碼,如果我們沒有為我們的 Elasticsearch 設定安全,那麼可以把 “-u elastic:123456” 整個去掉。正對配置有 https 的 Elasticsearch 伺服器,我們可以使用如下格式的指令來進行操作:

curl --cacert /home/elastic/ca.crt -u elastic:123456 -s -H "Content-Type: application/x-ndjson" -XPOST localhost:9200/_bulk --data-binary @es.json
           

在上面, 我們使用 --cacert /home/elastic/ca.crt 來定義證書的位址。

等我們運作完上面的指令後,我們可以在 Kibana 中檢視到我們的叫做 “bank_account” 的索引。

Elasticsearch如何建立索引,添加,删除,更新文檔

Open/close Index

Elasticsearch 支援索引的線上/離線模式。 使用脫機模式時,在群集上幾乎沒有任何開銷地維護資料。 關閉索引後,将阻止讀/寫操作。 當你希望索引重新聯機時,隻需打開它即可。 但是,關閉索引會占用大量磁盤空間。 你可以通過将 cluster.indices.close.enable 的預設值從 true 更改為 false 來禁用關閉索引功能,以避免發生意外。

Elasticsearch如何建立索引,添加,删除,更新文檔

一旦 twitter 索引被關閉了,那麼我們再通路時會出現如下的錯誤:

Elasticsearch如何建立索引,添加,删除,更新文檔

我們可以通過 _open 接口來重新打開這個 index:

Elasticsearch如何建立索引,添加,删除,更新文檔

關于關閉索引有很多用例:

它可以禁用基于日期的索引(按日期存儲其記錄的索引)— 例如,當你将索引保留一周、一個月或一天,并且你希望保留固定數量的舊索引(即 2 個月 舊)線上和一些離線(即從 2 個月到 6 個月)。

當你搜尋叢集的所有 active 索引并且不想搜尋某些索引時(在這種情況下,使用 alias 是最好的解決方案,但你可以使用具有關閉索引的 alias 來實作相同的效果)。

Freeze/unfreeze index

當機索引(freeze index)在群集上幾乎沒有開銷(除了将其中繼資料保留在記憶體中),并且是隻讀的。 隻讀索引被阻止進行寫操作,例如 docs-index 或 force merge。 請參閱當機索引和取消當機索引。

當機索引受到限制,以限制每個節點的記憶體消耗。 每個節點的并發加載的當機索引數受 search_throttled 線程池中的線程數限制,預設情況下為1。 預設情況下,即使已明确命名當機索引,也不會針對當機索引執行搜尋請求。 這是為了防止由于誤将當機的索引作為目标而導緻的意外減速。 如果要包含當機索引做搜尋,必須使用查詢參數 ignore_throttled = false 來執行搜尋請求。

我們可以使用如下的指令來對 twitter 索引來當機:

POST twitter/_freeze
           

在執行上面的指令後,我們再對 twitter 進行搜尋:

Elasticsearch如何建立索引,添加,删除,更新文檔

我們搜尋不到任何的結果。按照我們上面所說的,我們必須加上 ignore_throttled=false 參數來進行搜尋:

Elasticsearch如何建立索引,添加,删除,更新文檔

顯然對于一個 frozen 的索引來說,我們是可以對它進行搜尋的。我們可以通過如下的指令來對這個已經當機的索引來進行解凍:

POST twitter/_unfreeze
           

一旦我們的索引被成功解凍,那麼它就可以像我們正常的索引來進行操作了,而不用添加參數 ignore_throttled=false 來進行通路。

下期更精彩

在這篇文章中,我們詳細地介紹了如果在 es 中建立索引,文檔,并對他們進行更改,删除,查詢的操作。在接下來的文章裡,我們将重點介紹如何對 Elasticsearch 裡的 index 進行搜尋和分析。