講一下使用情境,如果手邊有一個2GB的JSON檔案,而且JSON檔中有巢狀資料,那勢必要透過 Linux 的命令列直接進行處理才是最快的手法。如果不事先進行預處理,無法上傳到 ElasticSearch。
之前有提過在 R 中的 jsonlite 套件,只能針對結構式資料 data.frame 進行 ND JSON 格式的輸出,面對不結構化的巢狀資料就無法處理。
今天的目標很簡單,就是把以下的JSON檔案 (第1筆資料有3個欄位,但第2筆資料只有1個欄位)
{"id1":{"id":["a0001"],"price":["99"],"made":["Taiwan"]},"id2":{"id":["a0002"]}}
直接透過 linux 的 jq 工具轉換成
{ "index": { "_index" : "test4" } }
{"id":["a0001"],"price":["99"],"made":["Taiwan"]}
{ "index": { "_index" : "test4" } }
{"id":["a0002"]}
因為我們的目標是利用JSON檔案進行分析,並不是成為 jq 大神,所以直接將試出來的解法公布
cat dirty_data1.json | jq -c '.[]' | jq -r ' . | tostring | "{ \"index\": { \"_index\" : \"test4\" } }\n" + .' > clean_nd.json
第1段的 cat 指令:將 json 檔案送往到 jq
第2段的 jq 指令:遇到最外層的[] 後將資料進行處理,讓每筆紀錄加上斷行符號
{"id":["a0001"],"price":["99"],"made":["Taiwan"]}
{"id":["a0002"]}
第3段的 jq 指令:在每一行前面再加上 { "index": { "_index" : "test4" } } 字串。
其中的 -r 參數是指定字串的輸出格式為raw strings,所以你可以使用 \ 跳脫符合的語法去輸出雙引號與斷行。
先看個簡單版本的,就能讓剛剛看起來很長的指令,變得簡單些。
把 "." 小數點符號,想成是每一筆紀錄;tosting 是固定語法,是為了後續進行字串連結。
cat dirty_data1.json | jq -c '.[]' | jq -r ' . | tostring | "我要加上的字串\n" + .'
效果是
我要加上的字串
{"id":["a0001"],"price":["99"],"made":["Taiwan"]}
我要加上的字串
{"id":["a0002"]}
最後再輸出到 > 另一個新檔就完成了。
然後有時,你拿到的 JSON 檔案可能是最外面再加上一組 []符號的JSON 檔案
[{"id1":{"id":["a0001"],"price":["99"],"made":["Taiwan"]},"id2":{"id":["a0002"]}} ]
這時候只要把指令改成
cat dirty_data.json | jq -c '.[][]' | jq -r ' . | tostring | "{ \"index\": { \"_index\" : \"test4\" } }\n" + .' > clean_nd.json
差異只在第一個例子中的第2組操作由
jq -c '.[]'
變成了
jq -c '.[][]'
就這樣簡單就快速可以搞定了!
實務上,在處理大型文件時,linux命令列下的各種工具 awk sed ag jq 還是有其處理速度上的優勢。所以有時候還是得要把這些處理手法給當成筆記抄錄下來。
當然,如果是新的 JSON 資料,平時就透過 API 往 ElasticSearch 丟,就不存在今天介紹範例的使用情境囉。