CData JSON Drivers がJSON オブジェクトをパースする手法とは?



SaaS・ウェブアプリのAPI レスポンスなど、さまざまな場面でJSON オブジェクト、配列、またはそれらの組み合わせとしてデータを返したり、保存したりします。これは階層化されたデータを扱う場合は便利ですが、BI、帳票、ETL ツール側の扱いが難しいことがあります。このようなネストされたJSON データを扱うテクニックとしては、主に2つ存在します。

  • JSON データをフラット化して、ネストされた配列とオブジェクトにアクセスする
  • データ構造を解析し、階層に基づいてリレーショナルモデルを構築する

この記事では、2つのテクニックの違いと、それぞれのテクニックを使用するためのCData JSON Drivers の設定方法について説明します。ナレッジベースの別の記事では、フラット化を使用してJSON データをどのようにパースできるのか、さらに深く掘り下げています。

ドキュメント、フラット化したドキュメント、リレーショナルモデル



CData Drivers for JSON では、ドキュメント全体をテーブルとして返す、フラット化したドキュメントを暗黙的に結合する、リレーショナルモデルを構築する、という3つの方法でNoSQL データと連携できます。ドキュメント全体を返す場合、ドライバーはネストされたオブジェクト配列を集約されたJSON オブジェクトとして返します。フラット化されたドキュメントを扱う場合、ドライバーはネストした配列オブジェクトを1つのテーブルとして解釈することで、SELECT クエリを実行して暗黙的に複数のネストした配列オブジェクトを結合します。リレーショナルモデルを構築する場合、ドライバーは検出されたオブジェクトと構造に基づいて、結果を個々のテーブルとして解釈します。これにより、NoSQL データをリレーショナルモデルとして扱うことが可能となり、データに対してSQL での結合を実行できます。以下でそれぞれの手法の例を見ていきましょう。

ここから例を見ていきますが、以下で紹介する例はこちらのサンプルJSON ドキュメントに基づいています。

サンプルドキュメント



サンプルドキュメントは、人、所有する自動車、およびさまざまな保守サービスのレコードを含みます。

people.json

{
  "people": [
    {
      "personal": {
        "age": 20,
        "gender": "M",
        "name": {
          "first": "John",
          "last": "Doe"
        }
      },
      "vehicles": [
        {
          "type": "car",
          "model": "Honda Civic",
          "insurance": {
            "company": "ABC Insurance",
            "policy_num": "12345"
          },
          "maintenance": [
            {
              "date": "07-17-2017",
              "desc": "oil change"
            },
            {
              "date": "01-03-2018",
              "desc": "new tires"
            }
          ]
        },
        {
          "type": "truck",
          "model": "Dodge Ram",
          "insurance": {
            "company": "ABC Insurance",
            "policy_num": "12345"
          },
          "maintenance": [
            {
              "date": "08-27-2017",
              "desc": "new tires"
            },
            {
              "date": "01-08-2018",
              "desc": "oil change"
            }
          ]
        }
      ],
      "source": "internet"
    },
    {
      "personal": {
        "age": 24,
        "gender": "F",
        "name": {
          "first": "Jane",
          "last": "Roberts"
        }
      },
      "vehicles": [
        {
          "type": "car",
          "model": "Toyota Camry",
          "insurance": {
            "company": "Car Insurance",
            "policy_num": "98765"
          },
          "maintenance": [
            {
              "date": "05-11-2017",
              "desc": "tires rotated"
            },
            {
              "date": "11-03-2017",
              "desc": "oil change"
            }
          ]
        },
        {
          "type": "car",
          "model": "Honda Accord",
          "insurance": {
            "company": "Car Insurance",
            "policy_num": "98765"
          },
          "maintenance": [
            {
              "date": "10-07-2017",
              "desc": "new air filter"
            },
            {
              "date": "01-13-2018",
              "desc": "new brakes"
            }
          ]
        }
      ],
      "source": "phone"
    }
  ]
}

ドキュメントモデル



NoSQL データのトップレベルドキュメントビューを使用すると、トップレベルのフィールドやオブジェクトにすぐにアクセスできるようになり、階層化した配列の処理やパースにかかる時間やリソースを省くことができます。CData ドライバーは、JSON が格納されたディレクトリをトップレベルの繰り返し要素に基づいて表示し、すべてのネストされた配列を単一のカラムとして扱うように設定できます。このモードでは、ドライバーはJSON データを読み込むためにストリーミングを使用し、返されるデータをクエリごとに一度だけパースします。

以下で上記のサンプルドキュメントに基づいたサンプルクエリを作成し、その結果を見ていきましょう。クエリの結果、JSON パス「$.people」に基づいた単一の「people」テーブルが作成されます。

接続文字列

DataModel 接続プロパティに「Document」と指定し、JSONPath 接続プロパティに「$.people」と指定してします。

DataModel=Document;JSONPath='$.people';

メタデータ

以下のテーブルは、ドキュメントデータモデルを使用する場合のメタデータです。

People
カラム データ型
personal.age Integer
personal.gender String
personal.name.first String
personal.name.last String
source String
vehicles String

クエリ

このクエリでは、トップレベルのオブジェクト要素(people)と配列(vehicles)を結果に取り込みます。オブジェクトのフラット化はデフォルトで実行されるので、トップレベルのオブジェクト要素が利用できます。配列は、集約したJSON データとして返されます。

SELECT 
  [personal.age] AS age, 
  [personal.gender] AS gender, 
  [personal.name.first] AS name_first, 
  [personal.name.last] AS name_last, 
  [source], 
  [vehicles]
FROM 
  [people]

結果

データのドキュメントビューでは、「personal」オブジェクトが4カラムにフラット化され、「source」と「vehicles」エレメントが個別のカラムとして返され、結果として6カラムのテーブルが作成されます。

age gender name_first name_last source vehicles
20 M John Doe internet [{"type":"car","model":"Honda Civic","insurance":{"company":"ABC Insurance","policy_num":"12345"},"maintenance":[{"date":"07-17-2017","desc":"oil change"},{"date":"01-03-2018","desc":"new tires"}]},{"type":"truck","model":"Dodge Ram","insurance":{"company":"ABC Insurance","policy_num":"12345"},"maintenance":[{"date":"08-27-2017","desc":"new tires"},{"date":"01-08-2018","desc":"oil change"}]}]
24 F Jane Roberts phone [{"type":"car","model":"Toyota Camry","insurance":{"company":"Car Insurance","policy_num":"98765"},"maintenance":[{"date":"05-11-2017","desc":"tires rotated"},{"date":"11-03-2017","desc":"oil change"}]},{"type":"car","model":"Honda Accord","insurance":{"company":"Car Insurance","policy_num":"98765"},"maintenance":[{"date":"10-07-2017","desc":"new air filter"},{"date":"01-13-2018","desc":"new brakes"}]}]

メリットと考慮事項

ドキュメントモデルを使用すると、JSON ストアまたはサービス内のすべてのトップレベルデータと集約された配列データを1つのテーブルで表示できます。簡単なクエリを送信するだけで、トップレベルのデータを扱うことができます。どのようなクエリでもJSON データの読み取りとパースが1回のリクエストで行われるため、パフォーマンスが向上し、ストリーミング機能との互換性が向上します。考慮すべき点として、配列に格納されているデータの必要性と、JSON 配列を意味のある方法で処理するツールやアプリケーションの能力があります。

フラット化されたドキュメントモデル



JSON データ全体に単純にアクセスする必要があるユーザーにとっては、データを単一テーブルにフラット化することは最善のオプションです。データ内のJSON パスに基づいて、JSON データから単一のテーブルをパースするようにドライバーを構成することができます。このモードでは、ネストされたオブジェクト配列は別々のテーブルとして扱われますが、暗黙的に親テーブルに結合されます。ドライバーはストリーミングを使用し、クエリごとにJSON データを1回だけパースします。フラット化されたドキュメントモデルでは、ドット記法を使用してデータに対し暗黙的にJOIN ステートメントを実行し、JSON データ内のネストされたエレメントにドリルダウンすることができます。

以下は、上記のサンプルドキュメントを基に、JSON パス「$.people」、「$.people.vehicles」、「$.people.vehicles.maintenance」 に基づいて解析したサンプルクエリとその結果です(これは、「people」コレクションを「vehicles」コレクションと暗黙的に結合し、「vehicles」コレクションを「maintenance」コレクションと暗黙的に結合します)。

接続文字列

Data Model 接続プロパティを「FlattenedDocuments」に設定しJSON Path 接続プロパティを「$.people;$.people.vehicles;$.people.vehicles.maintenance;」に設定して、上記のクエリを実行してサンプル結果セットを表示します。

DataModel=FlattenedDocuments;JSONPath='$.people;$.people.vehicles;$.people.vehicles.maintenance;'

メタデータ

以下のテーブルは、フラット化されたドキュメントデータモデルの使用に基づくメタデータを示しています。

People
カラム データ型
people:_id String
personal.age Integer
personal.gender String
personal.name.first String
personal.name.last String
source String
vehicle:_id String
type String
model String
insurance.company String
insurance.policy_num String
maintenance:_id String
date Date
desc String

クエリ

このクエリでは、各「people」オブジェクトのネストされたエレメントをドリルできます。「vehicles」コレクションをJSON パスとして含んだため、「vehicle」のエレメントを明示的にクエリできます。

SELECT 
  [personal.age] AS age, 
  [personal.gender] AS gender, 
  [personal.name.first] AS name_first, 
  [personal.name.last] AS name_last, 
  [source], 
  [type], 
  [model], 
  [insurance.company] AS ins_company, 
  [insurance.policy_num] AS ins_policy_num,
  [date] AS maint_date, 
  [desc] AS maint_desc
FROM 
  [people]

結果

記述されたパスに基づいて水平および垂直フラット化を行うと、各「vehicle」オブジェクトはその親「people」オブジェクトに暗黙的に結合され、また各「maintenance」オブジェクトはその親「vehicle」オブジェクトに暗黙的に結合され、8行(2つの「vehicles」に対して各2つの「maintenance」オブジェクト、2つの「people」に対して各2つの「vehicles」)を持つテーブルを生成します。

age gender first_name last_name source type model ins_company ins_policy_num maint_date maint_desc
20 M John Doe internet car Honda Civic ABC Insurance 12345 2017-07-17 oil change
20 M John Doe internet car Honda Civic ABC Insurance 12345 2018-01-03 new tires
20 M John Doe internet truck Dodge Ram ABC Insurance 12345 2017-08-27 new tires
20 M John Doe internet truck Dodge Ram ABC Insurance 12345 2018-01-08 oil change
24 F Jane Roberts phone car Toyota Camry Car Insurance 98765 2017-05-11 tires rotated
24 F Jane Roberts phone car Toyota Camry Car Insurance 98765 2017-11-03 oil change
24 F Jane Roberts phone car Honda Accord Car Insurance 98765 2017-10-07 new air filter
24 F Jane Roberts phone car Honda Accord Car Insurance 98765 2018-01-13 new brakes

メリットと考慮事項

フラット化されたドキュメントモデルを使用すると、JSON ストアやサービス内のすべてのデータを1つのテーブルで表示できます。簡単なクエリを送信して、階層化されたデータにドリルダウンすることができます。どのようなクエリでも、JSON データの読み取りとパースが1回のリクエストで行われるため、パフォーマンスが向上し、ストリーミング機能との互換性が向上します。フラット化されたドキュメントを扱う場合、ユーザーは、使用しているツールやアプリケーションが、あらかじめ結合された単一のデータセットと、個別のエンティティのどちらに適しているかを考慮する必要があります。

リレーショナルモデル



CData ドライバーは、JSON ファイルまたはデータソースのデータのリレーショナルモデルを作成し、ネストされたオブジェクト配列を親テーブルへのリレーションシップを含む個々のテーブルとして扱うように設定できます。これは、リレーショナルデータモデルを想定している既存のBI、帳票、およびETL ツールでJSON データを処理する必要がある場合に特に役立ちます。解釈されるモデルは、テーブルとして表示したい各オブジェクト配列のデータ内にあるJSON パスに基づいています。リレーショナルモデルを構築する場合、JOIN クエリを実行するたびに、JSON ファイルまたはデータソースはクエリに含まれる各「テーブル」に対して一度クエリされます。

以下は、上記のサンプルドキュメントを基に、JSON パス「$.people」、「$.people.vehicles」、「$.people.vehicles.maintenance」に基づくリレーショナルモデルを使用したサンプルクエリとその結果です。

接続文字列

Data Model 接続プロパティを「Relational」に設定しJSON Path 接続プロパティを「$.people;$.people.vehicles;$.people.vehicles.maintenance;」に設定して、上記のクエリを実行してサンプル結果セットを表示します。

DataModel=Relational;JSONPath='$.people;$.people.vehicles;$.people.vehicles.maintenance;'

メタデータ

以下のテーブルは、リレーショナルデータモデルの使用に基づくメタデータを示しています。

People
カラム データ型
_id String
personal.age Integer
personal.gender String
personal.name.first String
personal.name.last String
source String

Vehicles
カラム データ型
_id String
insurance.company String
insurance.policy_num String
model String
type String

Maintenance
カラム データ型
_id String
date Date
desc String


クエリ

このクエリは、「people」、「vehicles」、「maintenance」テーブルを明示的に結合します。

SELECT 
  [people].[personal.age] AS age, 
  [people].[personal.gender] AS gender, 
  [people].[personal.name.first] AS first_name, 
  [people].[personal.name.last] AS last_name, 
  [people].[source], 
  [vehicles].[type], 
  [vehicles].[model], 
  [vehicles].[insurance.company] AS ins_company, 
  [vehicles].[insurance.policy_num] AS ins_policy_num, 
  [maintenance].[date] AS maint_date, 
  [maintenance].[desc] AS maint_desc
FROM 
  [people]
JOIN 
  [vehicles] 
ON 
  [people].[_id] = [vehicles].[people_id]
JOIN 
  [maintenance] 
ON 
  [vehicles].[_id] = [maintenance].[vehicles_id]

結果

リレーショナルモデルを使用すると、どの結合もクエリによって制御されます。この例では、各「maintenance」オブジェクトはその親「vehicle」オブジェクトに結合され、さらにその親「people」コントロールに結合されて8行のテーブルを作成します(2「people」それぞれに2「vehicles」で、それぞれに2「maintenance」エントリ)。

age gender first_name last_name source type model ins_company ins_policy_num maint_date maint_desc
20 M John Doe internet car Honda Civic ABC Insurance 12345 2017-07-17 oil change
20 M John Doe internet car Honda Civic ABC Insurance 12345 2018-01-03 new tires
20 M John Doe internet truck Dodge Ram ABC Insurance 12345 2017-08-27 new tires
20 M John Doe internet truck Dodge Ram ABC Insurance 12345 2018-01-08 oil change
24 F Jane Roberts phone car Toyota Camry Car Insurance 98765 2017-05-11 tires rotated
24 F Jane Roberts phone car Toyota Camry Car Insurance 98765 2017-11-03 oil change
24 F Jane Roberts phone car Honda Accord Car Insurance 98765 2017-10-07 new air filter
24 F Jane Roberts phone car Honda Accord Car Insurance 98765 2018-01-13 new brakes

メリットと考慮事項

リレーショナルモデルを使用すると、JSON データやサービス内の個別のエンティティに基づいてデータモデルを構築できるため、一部のBI、帳票、ETL ツールとの互換性が向上します。しかし、リレーショナルモデルを構築してクエリを実行するために、パフォーマンスを犠牲にすることになります。リレーショナルモデル の複数のテーブルのデータを扱う場合、ドライバーはクエリ内の各テーブルのデータをクエリし、パースします。例えば、上記のクエリは3つの別個のリクエストを必要とします。

関連記事



  •  CData NoSQL の概要 - NoSQLを扱う上で、CData Drivers のテクノロジーが他と一線を画す機能についてご紹介します。
  •  NoSQL ドライバー:性能比較 - NoSQL データソースから大規模なデータセットをクエリおよび処理する際に、ベンダー各社のドライバーがどのように動作するかを比較します。
  •  NoSQL ドライバー:機能比較 - 異なるベンダーのドライバーが、NoSQL データソースからの複雑なクエリや複雑なデータセットをどのように処理するかを比較します。