TableauのMongoDB BI コネクタを使ってドキュメント指向DBがどのように見えるか試してみる

# はじめに

ドキュメント指向データベースをBIツールで分析する場合、どのようなテーブルになるのか確認してみます。

BIツールはTableauを使い、ドキュメント指向データベースはMongoDB Atlasを使ってみます。
MongoDB Atlasを選定した理由は、Tableauで接続できる「BI Connector」が手軽に使えるからです。

なお私はMongoDBもTableauも全くの素人です。
用語の使い方に間違いがあるかもしれません。（ご指摘いただけると嬉しいです）

## 環境
以下のような構成図になっています。
主目的はBI Connectorの利用なのでMongoDBに接続していますが、ついでに以下もためしました。

* [MongoDB Atlas]と[AWS]とのVPCピアリング
* [Tableau]から[Amazon DocumentDB]への接続

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/216cea5f-de4e-e7ce-9115-05fbb557ed8f.png)


バージョンは以下のとおりです。

| 対象 | バージョン |
|:-:|:-:|
| Tableau Desktop  |  2020.3.0 |
| MongoDB Atlas |  4.2.10 |

なお、
Tableauは「MongoDB BI Connector」というデータソースがあります。
MongoDB Atlasには「BI Connector」というサービスがあります。
まぎらわしいので以後は後者のほうを 「Atlas BI Connector」と呼ぶことにします。


# 接続先の用意

## MongoDB Atlas

### アカウントのセットアップ

とりあえずアカウントを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/33071d99-ed15-6c73-a405-e3c5ec526cbc.png)

最初は無料版を選択してみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/4697057a-5433-250d-8338-4f24a8799a49.png)

### 無料版クラスタの作成

AWS上に作成します。無料版なので東京リージョンは選べないため、バージニア北部を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/bcf65dd6-dc71-9f3e-1d74-7b53a0bdee1a.png)

スクロールしM0クラスタを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/0f3e3980-2dfd-6b9a-65fa-ba6c12948431.png)

ウィザードが次に何をすべきか指示してくれます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/2c1911f5-9818-43b3-00a6-5adcc24b380d.png)

### DBユーザの作成
DBユーザを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/a5f1d6db-6267-a434-5fe2-d1bdd7ed4e0c.png)


とりあえずパスワード認証で、権限は「Read and write to any database」にしてみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/6ed0312f-e2a6-af23-2911-9d91d0a7235e.png)


### ホワイトリストの登録
接続元のIPを登録（自宅と、ピアリング先のAWSのVPCのセグメントを登録しました）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/0dda8f8f-cab9-c1f0-ea08-1207a08e3dba.png)

### サンプルデータの読み込み
サンプルデータがあるようなので、三点リーダーから取り込んでみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/666873c9-acb8-6610-1ab7-1474604b58dc.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/825ac02b-cabe-77c1-2754-80da1d7002d8.png)


Clusterを選択してCollectionsを選択するとデータが確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/7a6fabd4-a14f-bf70-0f41-190f1159b60f.png)

### クレジットカードの登録
今回は BI Connectorを使うのが目的です。BI Connectorはそれ自体が有料です。さらに利用には有料版のクラスタ(M10以上)が必要です。
有料版を使うため、クレジットカードを登録します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/13dbd6c5-5b95-b1eb-5daf-209636489c0f.png)

### 有料版クラスタの作成
クラスター作成画面より、有料のM10を選択してみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/e0ebbb0f-e02c-714a-0c42-1d8f789459e2.png)

### Atlas BI Connector の有効化
下にスクロールし、「Advanced Settings」からBI コネクタを有効化してみます。
あまり課金ロジックを理解していませんが、数日間利用したところ、1日あたり $3.64 かかっていました。おそらくBIコネクタを有効化している日数ではなく、使用した日の日数分だけ課金されているような気がします。実際にコネクションを張らなかった日は課金されていないように思えました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/13f476b3-4373-8684-d8c0-62ab30b98086.png)

作成後は、この有料版クラスタにもサンプルデータを読み込ませておきます（省略します）

### 接続文字列の確認
Atlas BI Connectorは通常のクラスタ接続先と異なるようです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/aea23e64-8a04-8ccc-5d44-2b00a19348e4.png)

Atlas BI Connectorの接続文字列を見てみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/72e2595b-38cf-3a94-5a6a-a7b490bd1d0c.png)

ホスト名に「biconnector」という文字列が追加されていますね。それぞれコピーしておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/c46c818c-3b40-aee6-9139-63dd8773c7c3.png)


### VPC ピアリング
主目的ではありませんが、AWSとのピアリングも試しておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/43e915c4-207e-d632-5198-d076c98550c7.png)

AWSのアカウントID、VPC ID、とそのCIDRを指定します。Atlas側のCIDRは自動入力されていました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/12d6e287-91e9-a030-c27d-1238b5b036cc.png)

AWS側にピアリング申請がきているので承認しておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/e9d0d2b0-7a3c-b64c-3855-9c633031c800.png)

接続したいサブネットのルートテーブルにもVPCピアリングを通しておきます。先程確認したAtlas側のCIDRをいれます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/8976ada6-5b5b-a3a4-1a55-a57f9067f988.png)

VPC側でDNSホスト名とDNS解決が有効になっていることを確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/376be092-e3cd-c666-41dd-a2dfe8dc8b90.png)


AWS側からクラスタのドメイン名にpingを通すと、プライベートIPで応答が帰ってきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/e0a62c0b-8e1e-0232-995a-40dc90f07888.png)

## TableauからMongoDB Atlasへ接続

AWSのEC2上にインストールしてあるTableau Desktopから操作します。
tableauからMongoDB BIコネクターを選択すると、まずはドライバダウンロードを求められます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/8c2dc418-e1db-5c8c-5ad4-40b7c976f19a.png)

リンクをクリックするとブラウザが開きます。Windows用のmysql odbcドライバ「mysql-connector-odbc-8.0.21-winx64.msi」を落とします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/c3d3288a-3a39-4637-4f58-d64106d7ef09.png)

インストールしようとすると「this application requires visual studio 2019 x64 redistributable」と警告が出たため、「VC_redist.x64.exe」をインストールし再起動します。再起動したらmysql odbcドライバを改めて入れ(たような気がし)ます。
https://support.microsoft.com/ja-jp/help/2977003/the-latest-supported-visual-c-downloads
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/5c758159-c6df-f718-a4d0-261091c3ed90.png)

tableauから接続してみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/bed8c271-da33-051c-428c-7d50b65b5eec.png)


接続は成功するも、システムDBしか見えません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/6cf0325c-1d79-9d04-ba9c-b5e6779f16b7.png)

MongoDB Atlasのほうで、接続用ユーザの権限を「Read and write to any database」から「Atlas admin」に変更してみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/d447bfbd-4073-a27d-ebb6-9bccd9f7c576.png)

反映まで念のため数分待ち、再度接続してみます。今度はデータベース名も指定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/242d8f96-9cb8-0b4c-325c-5e23004184d4.png)

正常に接続でき、データベースや表もちゃんと見えました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/3a93b1a2-0574-feaf-f90b-7918cfeafcb7.png)

# BI Connectorでどのように見えるか確認する

## テーブル構造の確認

"sample_training"データベースの"companies"コレクションの1データをMongoDB Atlasから見てみます。
以下のような構造をしています。例として image は Object型になっていることが確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/bbb07c00-c686-c789-5e5b-609ce461ad80.png)


tableauからみると、companiesコレクションはオブジェクト単位で分離していることがわかります。
オブジェクトのネストはアンダーバーで表現されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/44b82de0-6690-3433-14d4-4e3cbb240908.png)

imageの実際のJSONデータを抜粋します。
$numberIntで型定義されている配列と、ファイルパスの文字列というデータになっています。

```
    "image": {
        "available_sizes": [
            [
                [
                    {
                        "$numberInt": "150"
                    },
                    {
                        "$numberInt": "75"
                    }
                ],
                "assets/images/resized/0000/3604/3604v14-max-150x150.jpg"
            ],
            [
                [
                    {
                        "$numberInt": "250"
                    },
                    {
                        "$numberInt": "125"
                    }
                ],
                "assets/images/resized/0000/3604/3604v14-max-250x250.jpg"
            ],
```

上記のような構造のデータは、tableau側ではこうなります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/b142201f-0e21-0d6d-ec9f-0e3e0e5e3ee0.png)

今度はオブジェクトのネストが２階層になっているテーブルの元データを見てみます。

```
    "funding_rounds": [
        {
            "id": {
                "$numberInt": "888"
            },
            "round_code": "a",
            "source_url": "http://seattlepi.nwsource.com/business/246734_wiki02.html",
            "source_description": "",
            "raised_amount": {
                "$numberInt": "5250000"
            },
            "raised_currency_code": "USD",
            "funded_year": {
                "$numberInt": "2005"
            },
            "funded_month": {
                "$numberInt": "10"
            },
            "funded_day": {
                "$numberInt": "1"
            },
            "investments": [
                {
                    "company": null,
                    "financial_org": {
                        "name": "Frazier Technology Ventures",
                        "permalink": "frazier-technology-ventures"
                    },
                    "person": null
                },
                {
                    "company": null,
                    "financial_org": {
                        "name": "Trinity Ventures",
                        "permalink": "trinity-ventures"
                    },
                    "person": null
                }
            ]
        },
```

１階層目。自テーブルのidxカラムが自動追加されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/b1851e58-1702-5098-9835-78da9544380f.png)

２階層目。自テーブルのidxカラムと、親テーブルのidxカラムが自動追加されています。このidxカラムの組み合わせにより、データを一意的に識別できるようになっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/57e88f62-b839-599c-c508-a3e95d6295f9.png)

## Tableauのシートに表示してみる

Tableauのデータソースから、２階層にネストしたテーブルをリレーションシップで組み合わせてみます。
まずはrootの companies と companies_acquisitionsをドラッグアンドドロップします。
同名のカラムがあるため、カラム指定せずに自動で紐付けてくれます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/550b7f79-bedf-5bf3-d53b-6965101ed651.png)

rootの companies と companies_funding_roundsも同じように自動紐付けされます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/bc38b16f-16b2-549e-2bcf-96e4416fb0d8.png)

companies_funding_rounds と companies_funding_rounds_investmentsは自動紐付けされないようです。同名カラムが複数ある(_IdとFunding Rounds Idx)からですかね？
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/e7a4de71-f7f0-4fa5-e5b0-f521d93aa91c.png)

手動で子にとっての親を指すインデックスカラム(Funding Rounds Idx)を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/cddafb2d-6af2-aa0c-0626-114fd35830d2.png)

リレーションシップができました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/765bb00a-00b5-5e56-12dd-e0b96385d5f2.png)

シートに移ります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/b7fa0717-a864-15ce-d759-ab50a2e2ed32.png)

業種カテゴリと従業員数でグラフを作ってみました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/4c828efe-98f9-99b1-bbef-d96fedf0ecbc.png)

# 所感
* オブジェクト単位でテーブルが分割されるものの、自動で追加されるインデックスカラムによって結びつけができるという点は便利です。ドキュメントDBを表現するにはこういうアプローチしかなさそうですし。
* ただ元データ側は、やはりドキュメント指向なためか文字列型が多く、Tableauで分析するにはメジャーが少ない印象です。
* このサンプルデータはキレイなのでなんとか棒グラフを作ることはできましたが、データによってはTableauの「テキスト表」でしか表現できないケースもあるようです。
  * データ入力時のバリデーションはアプリの正常動作に必要不可欠な機能だったりしますが、ことドキュメント指向DBに関していえば、BIで分析するためにも重要な機能であると思いました。厳密なバリデーションをかければBIはしやすくなりますが、アプリの柔軟性が落ちます。特定のサブスキーマだけ柔軟性を持たせたいというような場合はどうすればいいのでしょうか。。
  * [2020.10.16追記]IIJさんの以下のブログが、あいまいな構造のデータを扱う場合に非常に参考になります。
     * https://eng-blog.iij.ad.jp/archives/6813
     * JSON Schemaなど各種のSchema Validationの得手不得手や、サブスキーマの検索手法（速さ）も含め比較されています。
          * JTD（JSON Type Definition）がよさそうですがRFC標準化前の状態のようですね。
           * IETFの読み込みを重視しているという文化も含めて、さすがの記事です。
* ですのでtableauで表示をするまえに、どのような分析をしたいか、その分析をするためにどのようなデータ型で定義するか、を検討すべきであるという当たり前の事実を再確認しました。
  * データパイプラインの後工程（TableauやTableau Prep）で吸収するという手もあるかもしれませんが、あまり試していません。
* 必要であれば事前にETL処理などを行うべきですし、ETLを行えば何もBIツールから直接ドキュメント指向データベースに接続する必要もない気がします。


# おまけ(TableauからAmazon DocumentDBへの接続試行など)

### 直接アプローチ

#### Tableauの「MongoDB BI Connector」からAmazon DocumentDBへの接続
ダメ元でやってみたらやはりだめでした。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/216cea5f-de4e-e7ce-9115-05fbb557ed8f.png)

Tableauの「MongoDB BI Connector」は、Tableau側に DocumentDBのマッピング機能があるわけではなく、あくまで接続機能でしかないようです。マッピング機能は MongoDB側の BI Connector(Atlas BI Connector)にあります。

接続先のサーバをDocumentDBにしてみると、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/ac1489ef-f742-708d-deba-db8b4863403f.png)

エラーになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/f995d65b-1347-d4e9-2d48-62770d158700.png)

#### Tableauのカスタムコネクタ(JDBC/ODBC)の利用
* 有償コネクタ(未検証)
 * MongoDBへのコネクタは世間にいろいろありますが、試していません。
     * https://www.progress.com/jdbc/mongodb
     * http://unityjdbc.com/mongojdbc/mongo_jdbc.php

* 無償コネクタ
 * 以下のようなものを見つけてGradleでビルドし、JDBCドライバを作り、TDCファイルを置いてみたりしましたが接続できず諦めました。
     * https://github.com/mongodb/mongo-jdbc-driver

#### ODBCドライバから Atlas BI Connectorへ接続
DocumentDBへのアクセスではなくMongoDB Atlasへの接続です。これはうまくいきました。接続後、Collectionは検索しないと表示されませんでしたが、それ以外は問題なし。
機能的にはMongoDB側のAtlas BI Connectorがあればクライアント側の機能はさほど問われないということなんでしょう。

WindowsにODBCドライバをインストールし、
https://github.com/mongodb/mongo-odbc-driver/releases/
mongodb-connector-odbc-1.4.2-win-64-bit.msi


Windowsの標準機能からODBCドライバを作ります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/854d9172-3660-147d-d663-757993dc3460.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/08add261-ad80-304d-5126-3cf888621191.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/b84e6747-634f-76b2-88ed-2c6a2b78f3dc.png)

tableauからはODBCで接続できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/4a6c5ad9-3363-6cdb-f0e2-8a8c7d58d81d.png)

PowerBIからはODBCを指定して、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/97b12577-ca62-fa03-a6c8-f3958ad30a45.png)

さきほどのコネクタを指定し、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/6d679d7d-f103-bc64-2e63-4b041c6530e2.png)

改めてユーザ名とパスワードをいれると、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/66810919-0df5-0f2d-1a9c-956337f7f4ad.png)

接続ができました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/8008dbb9-5d2e-65f3-7977-39807c8537be.png)

#### ODBCドライバから DocumentDBへ接続
MongoDBのODBCドライバの接続先をDocumentDBに変え、Tableauからそれを指定し接続してみましたが、接続できずテスト接続でフリーズしました。アクセス元がBIツールなので仕方ないです。これができればAtlas BI Connectorの存在理由はありませんし。

### 間接アプローチ

#### Athenaを経由しAmazon DocumentDBへの接続
AthenaはDocumentDBへの接続機能があるようなので、Athenaを経由すればTableauから見えないか？という案

 * https://docs.aws.amazon.com/ja_jp/athena/latest/ug/athena-prebuilt-data-connectors-docdb.html

そもそもTableau側のAthenaコネクタがS3しか想定しない作りになっているので、NG。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/6d0a03bf-9aa7-33ba-e879-1d90234c37ff.png)

Tableauからはだめそうですが、AthenaのFederated Queryそのものについては別の機会で記事にしてみようと思います。

#### GlueジョブでDocumentDBにETL処理を行うパターン
詳細は省きますが、DocumentDBへの接続はできました。以下のようなデータが、

```
{
    "_id": "01001",
    "city": "AGAWAM",
    "loc": [
        -72.622739,
        42.070206
    ],
    "pop": 15338,
    "state": "MA"
}

```

GlueのDynamicFrameによって以下のように型認識されています。

```
|-- _id: string
|-- city: string
|-- loc: array
|    |-- element: double
|-- pop: int
|-- state: string
```

型はちゃんと認識できているので、ETL処理を行うことはできると思います。

以上です。