Embulk JSON関連のfilterプラグインを触ってみた。


# Embulk JSON関連のfilterプラグインを触ってみた。

Embulkアドベントカレンダー、19日目の記事が埋まらないので私のメモを公開します。

embulkのJSON関連のfilterプラグインは、現在のところ、全て@Civitaspoさんが作成されています。ありがとうございます。
プラグインのサンプルを実際に動かしてみて、各プラグインの動作を確認しました。(各プラグインのexampleディレクトリにあるものを動かしただけとも言います)


名前                        | 説明
----------------------------|-----------------------------------------------------
embulk-filter-expand_json   | jsonから値を抽出して、独立したカラムに値をセットする
embulk-filter-flatten_json  | 配列やハッシュでネストしたJSONを平坦化する
embulk-filter-json_key      | JSONに値を追加したり、JSONから値を削除したりる
  

## embulk-filter-expand_json

embulk-filter-expand_jsonは、jsonから値を抽出して、独立したカラムに分離するプラグインです。

[サンプル](https://github.com/civitaspo/embulk-filter-expand_json/blob/master/example/config.yml) を実行して動作を確認してみましょう。

元データのdata.tsvはタブ区切りのテキストで、JSONのデータはjson_payloadカラムに格納されています。


field_name   | data
-------------|------------------------------------
time         | 2015-10-11 08:06:23 +0900
id           | 0
name         | Avis Lind
score        |-3256.869635206057
json_payload | {"phone_numbers":"1-276-220-7263",...


json_payloadの中身は次のような形式になっています。処理の内容を分かり易くするためにコメントをいれてあります。元のサンプルにコメントはありません。


```javascript
{
  // (1) 独立したカラムに値を抽出(文字列)
  "phone_numbers": "1-276-220-7263",
  // (2) 独立したカラムに値を抽出(整数)
  "app_id": 0,
  // (3) 独立したカラムに値を抽出(浮動小数点)
  "point": -1601.6890336884562,
  // (4) 独立したカラムに値を抽出(日付部分のみ抽出)
  "created_at": "2015-10-07 20:23:57 +0900",
  "profile": {
    "like_words": [ // (5) 配列全体を抽出
      "maiores",
      "eum",　// (6) 配列の一部を抽出
      "aut"   // (7) 配列の一部を抽出
    ],
    "anniversary": { // (8) ハッシュ全体を
      "voluptatem": "dolor",
      "et": "ullam"  // (9) ハッシュ内の一要素を抽出
    }
  }
}
```

次に、embulkのフィルタプラグインの設定です。embulk-filter-expand_jsonは、JSONのパスを[JsonPath](http://goessner.net/articles/JsonPath/)を使って指定します。


```yaml
filters:
  - type: expand_json
    json_column_name: json_payload # 元データの格納先としてjson_payloadカラムを指定
    root: "$." # JsonPATHで、rootを起点とするように設定
    expanded_columns:
      - {name: "phone_numbers", type: string} # (1)
      - {name: "app_id", type: long}          # (2) 
      - {name: "point", type: double}         # (3)
      - {name: "created_at", type: timestamp, format: "%Y-%m-%d"} # (4) 
      - {name: "profile.anniversary.et", type: string} # (9) 
      - {name: "profile.anniversary", type: string}    # (8)
      - {name: "profile.like_words[1]", type: string}  # (5)
      - {name: "profile.like_words[2]", type: string}  # (6) 
      - {name: "profile.like_words", type: string}     # (7) 
```

プレビューを実行すると次のようになり、json_payloadから様々な値が抽出されていることが確認できます。

```
*************************** 1 ***************************
                  time (timestamp) : 2015-10-11 00:00:00 UTC
                    id (     long) : 0
                  name (   string) : Avis Lind
                 score (   double) : -3256.869635206057
         phone_numbers (   string) : 1-276-220-7263            # (1)
                app_id (     long) : 0                         # (2)
                 point (   double) : -1601.6890336884562       # (3) 
            created_at (timestamp) : 2015-10-07 00:00:00 UTC   # (4) 日付のみ抽出されている
profile.anniversary.et (   string) : ullam                     # (9) 
   profile.anniversary (   string) : {"voluptatem":"dolor","et":"ullam"} # (8)
 profile.like_words[1] (   string) : eum  # (5) 
 profile.like_words[2] (   string) : aut  # (6) 
    profile.like_words (   string) : ["maiores","eum","aut"] # (7) 
```

(4)の元データは、``2015-10-07 20:23:57 +0900``でしたが、日付の部分のみ抽出したので、``2015-10-07 0:0:0 UTC``となり日本時刻にすると``2015-10-07 09:00:00 +0900``になっています。

日付を取り扱う場合は、タイムゾーンまでを取得して、出力先で工夫をするのがよいのかもしれません。


## embulk-filter-flatten_json

次にembulk-filter-flatten_jsonを試します。このプラグインは、配列やハッシュでネストしたJSONを平坦化するプラグインです。オリジナルの[サンプル](https://github.com/civitaspo/embulk-filter-flatten_json/tree/master/example)はこちらです。


元データはこちらです。コメントは追記したもので、オリジナルにはありません。

```javascript
{
  "phone_numbers": [ // (1) ハッシュの配列になっている
    "526-126-8730 x85186" 
  ],
  "profile": {  // (2) ハッシュが入れ子になっている
    "like_words": [ // (3) 配列になっている
      "quos",
      "non",
      "nemo"
    ],
    "anniversary": { // (4) ハッシュが入れ子になっている
      "sint": "illo",
      "excepturi": "debitis"
    }
  }
}
```

次にembulkのフィルタプラグインの設定です。

```yaml
filters:
  - type: flatten_json
    json_columns:           # 平坦化するjsonのカラムを配列で指定
      - json_payload        # 元データはjson_payloadカラムを利用
    separator: "."          # ハッシュがあった場合の区切り文字
    array_index_prefix: "_" # 要素が配列だった場合に、要素番号の前に追加する文字
```

実行結果は次のようになります。

```
*************************** 1 ***************************
        time (timestamp) : 2015-10-05 00:00:00 UTC
          id (     long) : 0
        name (   string) : Mia O'Conner
       score (   double) : -4432.147926744348
json_payload (   string) : {"profile.anniversary.sint":"illo","phone_numbers._0":"526-126-8730 x85186","profile.like_words._0":"quos","profile.like_words._2":"nemo","profile.anniversary.excepturi":"debitis","profile.like_words._1":"non"}
```

JSON部分だけ注目します。フラットなハッシュになっていることがわかります。

```javascript
{
  "phone_numbers._0": "526-126-8730 x85186", // (1) 
  "profile.like_words._1": "non"             // (2),(3)
  "profile.like_words._0": "quos",           // (2),(3)
  "profile.like_words._2": "nemo",           // (2),(3)
  "profile.anniversary.excepturi": "debitis",// (4) 
  "profile.anniversary.sint": "illo",        // (4) 
}
```


## embulk-filter-json_key


最後にembulk-filter-json_keyを試します。このプラグインは、JSONのカラムに値を追加したり、値を削除するプラグインです。オリジナルの[サンプル](https://github.com/civitaspo/embulk-filter-json_key/tree/master/example)はこちらです。


### 値の追加

まず値を追加する設定を試します。

オリジナルのデータのjson_payloadの値は次のようになっています。

```json
{
  "phone_numbers": "1-276-220-7263",
  "profile": {
    "like_words": [
      "maiores",
      "eum",
      "aut"
    ],
    "anniversary": {
      "voluptatem": "dolor",
      "et": "ullam"
    }
  }
}
```

次にembulkの設定です。

```yaml
filters:
  - type: json_key
    column: json_payload # 元カラムの指定
    nested_key_delimiter: "." # ネストした属性のデリミタ
    add_keys:
      # (1) キーだけ追加
      - {key: add_key1}
      # (2) キーと値を追加、値は文字列
      - {key: add_key2, value: add_value2}
      # (3) キーと値を追加、値は、数値
      - {key: add_key3, value: 3}
      # (4) キーと値を追加、値は配列
      - {key: add_key4, value: [1,2,3,4]}
      # (5) キー値を追加、値はハッシュ
      - {key: add_key5, value: {key1: value, key2: 2, key3: {key: value}}}
      # (6) 既存のハッシュprofileの下に、新しいキーを追加、値は文字列
      - {key: profile.add_key6, value: add_value6}
```

実行した結果のjson_payloadカラムは次のようになりました。

```javascript
{
  "phone_numbers": "1-276-220-7263",
  "profile": {
    "like_words": [
      "maiores",
      "eum",
      "aut"
    ],
    "anniversary": {
      "voluptatem": "dolor",
      "et": "ullam"
    },
    "add_key6": "add_value6" // (6) 既存のパラメータに値が追加されている 
  },
  "add_key1": null,  // (1) キーだけ増えて値がnull 
  "add_key2": "add_value2", // (2) キーが増え、値が文字列 
  "add_key3": 3, // (3) キーが増えて値が数値
  "add_key4": [ // (4) キーが増えて値が、配列 
    1,
    2,
    3,
    4
  ],
  "add_key5": { // (5) キーが増えて値がハッシュ
    "key1": "value",
    "key2": 2,
    "key3": {
      "key": "value"
    }
  }
}
```

### 値の削除


オリジナルのデータのjson_payloadの値は次のようになっています。


```javascript
{
  "phone_numbers": "1-276-220-7263", // (1) この値を削除
  "profile": {
    "like_words": [
      "maiores",
      "eum",
      "aut"  // (2) この値を削除、ハッシュの中の配列の要素
    ],
    "anniversary": {
      "voluptatem": "dolor", // (3) この値を削除、入れ子になったハッシュ
      "et": "ullam"
    }
  }
}
```

次にembulkの設定です。

```yaml
filters:
  - type: json_key
    column: json_payload # 対象カラムの指定
    nested_key_delimiter: "." # ネストした属性のデリミタ
    drop_keys:
      # (1) phone_numbersを削除
      - {key: phone_numbers}
      # (2) profile.like_wordsの3番目の要素を削除
      - {key: profile.like_words.2}
      # (3) profile.anniversary.voluptatemを削除
      - {key: profile.anniversary.voluptatem}

```

実行した結果のembulkは次のようになりました。

```javascript
{
  // (1) "phone_numbers": "1-276-220-7263"がなくなっている
  "profile": {
    "like_words": [
      "maiores",
      "eum"
      // (2) "aut"がなくなっている
    ],
    "anniversary": {
      // (3) "voluptatem": "dolor" がなくなっている
      "et": "ullam"
    }
  }
}
```

## 最後に

以上で現在利用できるjson関連のフィルタプラグインを試してみました。JSONを編集する際には、非常に役立つプラグインだと思います。

それにしても、気になるのはサンプルデータで使われている文字が何語なのかということです。Google Translateによるとラテン語とでてきたのですが、、、、