JSON データ型
JSON
型は、JavaScript オブジェクトノーテーション (JSON) ドキュメントを単一カラムに格納します。
この機能はベータ版であり、まだ本番環境では使用できません。 JSON ドキュメントを扱う必要がある場合は、代わりに こちらのガイド を参照してください。
JSON
型を使用したい場合や、このページの例のためには、以下のように設定してください:
JSON
型のカラムを宣言するには、次の構文を使用できます:
上記の構文のパラメータは以下のように定義されています:
パラメータ | 説明 | デフォルト値 |
---|---|---|
max_dynamic_paths | 単一のデータブロックに格納されるサブカラムとして別々に格納できるパスの数を示すオプションのパラメータ(例えば、MergeTree テーブルの単一データパートに対して)。 この制限を超える場合、他のすべてのパスは単一構造にまとめて格納されます。 | 1024 |
max_dynamic_types | 単一のパスカラムに格納できる異なるデータ型の数を示す、1 から 255 のオプションのパラメータ(例えば、MergeTree テーブルの単一データパートに対して)。 この制限を超える場合、新しいタイプはすべて String 型に変換されます。 | 32 |
some.path TypeName | JSON 内の特定のパスのためのオプションの型ヒント。このようなパスは常に指定された型のサブカラムとして格納されます。 | |
SKIP path.to.skip | JSON パース中にスキップすべき特定のパスに対するオプションのヒント。このようなパスは JSON カラムに格納されることはありません。指定したパスがネストされた JSON オブジェクトの場合、全体のネストされたオブジェクトがスキップされます。 | |
SKIP REGEXP 'path_regexp' | JSON パース中にパスをスキップするために使用されるオプションの正規表現付きヒント。この正規表現に一致するすべてのパスは JSON カラムに格納されることはありません。 |
JSONの作成
このセクションでは、JSON
を作成するためのさまざまな方法を見ていきます。
テーブルカラム定義での JSON
の使用
::JSON
を使用したキャスト
特別な構文 ::JSON
を使って、さまざまなタイプをキャストすることができます。
String
から JSON
へのキャスト
Tuple
から JSON
へのキャスト
Map
から JSON
へのキャスト
廃止されている Object('json')
から JSON
へのキャスト
JSON パスはフラットに保存されます。つまり、パス a.b.c
のようにフォーマットされた JSON オブジェクトから構築すべきかどうかを判断することは不可能です。
私たちの実装は常に後者を仮定します。
例えば:
は次のように返されます:
ではなく:
JSON パスをサブカラムとして読み取る
JSON
型は、すべてのパスを別々のサブカラムとして読むことをサポートします。
要求されたパスの型が JSON 型宣言で指定されていない場合、そのパスのサブカラムは常に Dynamic 型になります。
例:
リクエストされたパスがデータ内で見つからなかった場合、その値には NULL
が設定されます:
返されたサブカラムのデータ型を確認してみましょう:
見ることができるように、a.b
の型は、JSON 型宣言で指定した通り UInt32
であり、他のすべてのサブカラムの型は Dynamic
です。
Dynamic
型を持つサブカラムは、特別な構文 json.some.path.:TypeName
を使用して読み取ることもできます:
Dynamic
サブカラムは任意のデータ型にキャストすることができます。この場合、Dynamic
内部の型をリクエストされた型にキャストできない場合は、例外がスローされます:
JSON サブオブジェクトをサブカラムとして読み取る
JSON
型は、特別な構文 json.^some.path
を使用して、タイプ JSON
を持つネストされたオブジェクトをサブカラムとして読み取ることをサポートします:
サブオブジェクトをサブカラムとして読み取ることは非効率である可能性があります。これは、JSON データ全体をスキャンする必要があるためです。
パスの型推論
JSON
のパース中に、ClickHouse は各 JSON パスの最も適切なデータ型を検出しようとします。
これは、入力データからの自動スキーマ推論 と同様に動作し、同じ設定によって制御されます:
- input_format_try_infer_integers
- input_format_try_infer_dates
- input_format_try_infer_datetimes
- schema_inference_make_columns_nullable
- input_format_json_try_infer_numbers_from_strings
- input_format_json_infer_incomplete_types_as_strings
- input_format_json_read_numbers_as_strings
- input_format_json_read_bools_as_strings
- input_format_json_read_bools_as_numbers
- input_format_json_read_arrays_as_strings
いくつかの例を見てみましょう:
JSON オブジェクトの配列の処理
オブジェクトの配列を含む JSON パスは、 Array(JSON)
型として解析され、パスの Dynamic
カラムに挿入されます。
オブジェクトの配列を読み取るためには、 Dynamic
カラムからサブカラムとして抽出することができます:
気づかれたかもしれませんが、ネストされた JSON
型の max_dynamic_types
/ max_dynamic_paths
パラメータはデフォルト値に比べて減少しています。
これは、ネストされた JSON オブジェクトの配列に対して無制限にサブカラムの数が増えるのを避けるために必要です。
ネストされた JSON
カラムからサブカラムを読み取ってみましょう:
Array(JSON)
のサブカラム名を書くことなく、特別な構文を使うことできます:
パスの後に続く []
の数は、配列のレベルを示します。例えば、json.path[][]
は json.path.:Array(Array(JSON))
に変換されます。
Array(JSON)
内のパスと型を確認してみましょう:
Array(JSON)
カラムからサブカラムを読み取ってみましょう:
ネストされた JSON
カラムからサブオブジェクトのサブカラムを読み取ることもできます:
データからの JSON 型の読み取り
すべてのテキスト形式
(JSONEachRow
,
TSV
,
CSV
,
CustomSeparated
,
Values
など) は JSON
型の読み取りをサポートしています。
例:
テキスト形式の CSV
/ TSV
/ などについては、JSON は JSON オブジェクトを含む文字列から解析されます:
JSON 内の動的パスの限界に達する
JSON
データ型には、内部で別々にサブカラムとして保存できるパスの限界があります。
デフォルトでは、この制限は 1024
ですが、max_dynamic_paths
パラメータを使用して型宣言で変更できます。
制限に達すると、新たに挿入されたパスはすべて単一の共有データ構造に格納されます。 そのようなパスをサブカラムとして読み取ることは依然として可能ですが、そのためにはこのパスの値を抽出するために全体の共有データ構造を読み取る必要があります。 この制限は、テーブルが使用不可能になるほど異なるサブカラムの数が膨大になるのを避けるために必要です。
さまざまなシナリオで制限に達した際に何が起こるかを見てみましょう。
データ解析中に制限に達する
データからの JSON オブジェクトの解析中に、現在のデータブロックの制限に達した場合、すべての新しいパスは共有データ構造に格納されます。次の2つのイントロスペクション関数 JSONDynamicPaths
, JSONSharedDataPaths
を使用できます:
見ることができるように、パス e
と f.g
を挿入した後に制限に達し、それらは共有データ構造に挿入されました。
MergeTree テーブルエンジンにおけるデータパーツのマージ中
MergeTree
テーブル内の複数のデータパーツをマージする際、生成されたデータパート内の JSON
カラムは動的パスの制限に達し、ソースパーツからのすべてのパスをサブカラムとして格納できなくなることがあります。この場合、ClickHouse はマージ後にどのパスがサブカラムとして残るか、どのパスが共有データ構造に格納されるかを選択します。ほとんどのケースにおいて、ClickHouse は非 NULL 値が最も多いパスを保持し、最も稀なパスを共有データ構造に移動しようとします。ただし、これは実装によって異なる場合があります。
このようなマージの例を見てみましょう。まず、JSON
カラムがあり、動的パスの制限を 3
に設定したテーブルを作成し、5
つの異なるパスで値を挿入します:
各挿入は、単一のパスを含む JSON
カラムを持つ別々のデータパートを作成します:
次に、すべてのパーツを1つにマージし、何が起こるか見てみましょう:
見ての通り、ClickHouse は最も頻繁に使用されるパス a
、b
、c
を保持し、パス d
および e
を共有データ構造に移動しました。
インストロpection 関数
JSON
カラムの内容を検査するためのいくつかの関数があります:
JSONAllPaths
JSONAllPathsWithTypes
JSONDynamicPaths
JSONDynamicPathsWithTypes
JSONSharedDataPaths
JSONSharedDataPathsWithTypes
distinctDynamicTypes
distinctJSONPaths and distinctJSONPathsAndTypes
例
GH Archive データセットの 2020-01-01
日付の内容を調査しましょう:
ALTER MODIFY COLUMN を JSON 型に変更
既存のテーブルを変更し、カラムの型を新しい JSON
型に変更することが可能です。現在、String
型からの ALTER
のみがサポートされています。
例
JSON 型の値の比較
JSON
カラムの値は less/greater
関数で比較することはできませんが、equal
関数を使用して比較することができます。
2つの JSON オブジェクトは、同じパスのセットを持ち、これらのパスのそれぞれが両方のオブジェクトで同じ型と値を持つ場合に等しいと見なされます。
例えば:
JSON 型のより良い使用のためのヒント
JSON
カラムを作成し、データをロードする前に、以下のヒントを考慮してください:
- データを調査し、できるだけ多くのパスヒントと型を指定してください。これにより、ストレージと読み取りがより効率的になります。
- どのパスが必要で、どのパスが必要ないかを考慮してください。必要ないパスを
SKIP
セクションおよび必要に応じてSKIP REGEXP
セクションに指定してください。これによりストレージが改善されます。 max_dynamic_paths
パラメータを非常に高い値に設定しないでください。これはストレージと読み取りを非効率的にする可能性があります。システムパラメータ(メモリ、CPU など)に大きく依存しますが、一般的な目安としてmax_dynamic_paths
> 10,000 にしないことをお勧めします。
さらなる読み物
- ClickHouse のための新しい強力な JSON データ型をどのように構築したか
- 億ドキュメント JSON チャレンジ: ClickHouse 対 MongoDB、Elasticsearch など