我們在開發上的各個階段,可能都需要對index做不同的調整
例如在測試過程,想要臨時在已經創建好的index做欄位的變更等等
今天我們就是著重在介紹遇到以下情形以及可能的開發需求時,對應處理的API
新增現有index的欄位:
有時候你沒辦法保證索引的欄位數量不會再改動,並且因為ES本身預備很多彈性,我們在建立索引時的欄位不一定是最終版本,那我們要如何去為現有的索引增加欄位呢?
// 現有的test索引只有name一個欄位
PUT /test
{
"mappings": {
"properties": {
"name": {
"type": "text"
}
}
}
}
PUT /test/_doc/1
{
"name": "Eason"
}
PUT /test/_mapping
{
"properties": {
"new_field": {
"type": "text"
}
}
}
GET /test/_mapping
這時候可能會有疑問,這樣我們一開始輸入的文檔會有這個新欄位的預設值嗎?
{
"_index": "test",
"_id": "1",
"_version": 1,
"_seq_no": 0,
"_primary_term": 1,
"found": true,
"_source": {
"name": "Eason"
}
}
我們可以看到_source中沒有新的值,並且因為此時因為新建立欄位是null,無法建立反向索引並且做搜尋等操作
因此我們可以為index field添加默認值,方法有使用script語法跟使用pipeline
我們在後面的章節會再介紹pipeline,所以這邊用script示範
在ES中使用script時通常是要自定義一些項目時會使用到,但是性能可能會因此下降,因此如果非必要盡量不要使用
script的基本語法
"script": {
"lang": "...",
"source" | "id": "...",
"params": { ... }
}
那我們就讓new_field欄上補上默認值
POST test/_update_by_query
POST /test/_update_by_query
{
"script": {
"source": "if (ctx._source.new_field == null) {ctx._source.new_field = 'update!'}",
"lang": "painless"
}
}
Reindex API:
那如果我們今天創建了index,並且在有資料的情況下,能不能直接使用mapping API將欄位的type進行改變?
一般狀況下是不行的~
因為這樣必須要重新index,如果有幾百萬筆資料的話會非常花時間
我們可以使用Reindex API來更改現有index的欄位類型
最基礎的就是如下:
設置好source與destination的index,切記這兩個index都要已經存在
POST /_reindex
{
"source": {
"index": "test-1"
},
"dest": {
"index": "test-2"
}
}
一些進階用法:
POST /_reindex
{
"source": {
"index": "test-1"
"query": {
"match": {}
}
},
"dest": {
"index": "test-2"
}
}
POST /_reindex
{
"source": {
"index": "test-1"
"_source": ["column-1", "column-2"]
},
"dest": {
"index": "test-2"
}
}
// manual
POST /_reindex
{
"source": {
"index": "test-1",
"slice": {
"id": 0,
"max": 2
}
},
"dest": {
"index": "test-2"
}
}
// automatic
POST _reindex?slices=5&refresh
{
"source": {
"index": "test-1"
},
"dest": {
"index": "test-2"
}
}
POST /_reindex
{
"source": {
"remote": {
"host": "http://otherhost:9200",
"username": "",
"password": ""
},
"index": ["remote-index-2", "remote-index-2"],
"_source": ["id", "name"],
"query": {
"match_all": {}
}
}, "dest": {
"index": "local-dest-index",
"routing": "=user"
},
"script": {
"source": """ctx._source.comment = ctx._source-remove("content")""",
"lang": "painless"
},
}
Multi-field mappings:
有時候同一個欄位的資料性質,可能需要多種方式去操作,有時候可能要用全文搜索去找,但是有時又想要能使用term-level query
例如車子的型號是:車廠 型號(ex: Honda CR-V)
我就可能需要使用term-level query去把所有Honda CR-V的車子找出來
但是同時我又想要單獨去找CR-V的型號
因此可以使用multi-fields
PUT /multi_field_test
{
"mappings": {
"properties": {
"name": {
"type": "text"
},
"desc": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword"
}
}
}
}
}
}
以上圖為例:
index template:
之前有提過,ES很常會接收來自filebeat、metricbeat等的資料,或是常要接收一些log值
索引名稱可能如下:
access-log-2023-01
access-log-2023-02
access-log-2023-03
這時後我們就可以使用index template,創建符合特定條件名稱的索引,讓他們的mapping設定一樣的條件
PUT /_template/access-logs
{
"index_patterns": ["access-logs-*"],
"settings": {
"number_of_shards": 1
},
"mappings": {
"properties": {
"@timestamp": {
"type": "date"
},
"http.response.status_code": {
"type": "long"
}
}
}
}
此時我們直接創一個索引,並且直接看mapping
PUT /access-logs-2023-09
GET /access-logs-2023-09/_mapping
可以看到因為匹配到我們的模板,mapping直接設定好了
不過有以下幾點要注意:
今天的內容一樣非常非常多
我們大致介紹了mapping的類型
並且也說明了,如果我們建立好mapping跟index,當需求改變時
我們可以透過一些API來幫助我們去新增欄位,或是轉移資料到新索引上
但是在一開始我們可能對於整個專案業務不熟悉的狀況下
我們甚至可能對於我們是不是要再細分處理原有的欄位
或是對於送進索引的row data,要不要再分割成更適合我們需求的欄位也不太清楚
並且可能我們也不想要使用額外的空間去儲存這些暫時性的資料,那應該怎麼做?
明天來介紹Runtime fields,這個在開發初期也算是很好用的功能,並可以解決上面的問題
也介紹在實務上通常會怎麼使用
參考資料
script:
https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-scripting-using.html
reindex API:
https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-reindex.html
index templates:
https://www.elastic.co/guide/en/elasticsearch/reference/current/index-templates.html