IoTデータをMongoDBとMetabaseで可視化するためのTips

## はじめに

はじめまして。Qiita初投稿です！！

センサーで収集した温度・湿度・気圧のデータをクラウドに飛ばして可視化したいと思いました。クラウドはAWS IoT CoreやAzure IoT Hubを使うのが手っ取り早いのですが、お金をかけないでやるために、Databaseと可視化ツール（BIツール）もOSSを使い自前で構築してみました。

### 使ったOSS
 - Database : MongoDB（v4.2.2）
 - BI Tool : Metabase（v0.33.5.1）

両方ともメジャーなOSSですので、使い方の解説はあちこちにありますが、MongoDB + Metabaseの組み合わせを解説した記事があまりなく、**ドキュメントに書いてないハマりどころ**がいくつかありましたのでそのあたりを解説します。

## 全体構成

詳細は割愛しますが、以下のようにVM環境もOpenStackを使い、自宅サーバ上で構築しています。

![fig-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/162338/a19f6b70-edd0-7cd1-7678-8992082bcf0e.png)

### データの流れ

 1. BME280（温度・湿度・気圧）+ ESP32からWi-Fiでデータを送信
 2. IoT-Hub & データブローカーの[OM2M](https://www.eclipse.org/om2m/)がデータを受信
 3. 新しいデータが届くと、OM2Mがアプリ（ADN-AE）に通知（Notification）
 4. アプリでセンサデータを抽出してMongoDBに格納
 5. BI Tool（Metabase）でデータを可視化
 6. ついでに、M5Stackを使って最新データをモニターできるようにしてみた

## MongoDBのTips

### データの記述方法

以下のように、JSONを使いデータをキー・バリュー形式でフォーマットします。

```
{
    "Location" : "Outside", 
    "RSSI" : -55, 
    "TemperatureC" : 10.79, 
    "Humidity" : 79.7, 
    "PressureHpa" : 988.53, 
    "Battery" : 2.78, 
    "CreateDate" : "2019-12-26T21:29:59Z", 
}
```

 - JSONの「キー」はMongoDBの検索キーにもなりますので、簡潔で分かりやすい名前を付けます
 - 他の人が見ても分かるように名前に単位情報（TempretureC → 摂氏だよ）を入れると親切
 - データの収集時刻（CreateDate）はUTC時間をISO8601形式でフォーマット → UTCで格納しておけば、Metabaseがブラウザの設定に合わせたローカルタイムに変換して表示します
 - 収集時間に加えてセンサーの設置場所も入れておくとよい

### MongoDBに日付を認識させる

NoSQL型のDBは、RDBのようにスキーマーを作って各データの属性を定義する必要がないのですが、MongoDBとMetabaseを連携するためには、日付だけは**”このデータは日付だよ”という意味付け**を行う必要があります。こうしておかないと、Metabaseから日付の範囲を指定したクエリを行った際に、「can’t convert from BSON type String to Date」といったエラーが発生し、データの取得ができませんでした。

そこで、MongoDBに日付データを格納する際には、日付をISODateオブジェクトにする必要があります。そのために、以下のようにサーバー上のアプリで、"CreateDate"からISODateを生成・追加してMongoDBに格納します。

```
{
    "Location" : "Outside", 
    "RSSI" : -55, 
    "TemperatureC" : 10.79, 
    "Humidity" : 79.7, 
    "PressureHpa" : 988.53, 
    "Battery" : 2.78, 
    "CreateDate" : "2019-12-26T21:29:59Z", 
    "isodate" : ISODate("2019-12-26T21:29:59Z")
}
```

ISODateの生成・追加は、JavaScriptでは以下のように記述できます

```Javascript
var isoDate =  new Date(jsonData.CreateDate);
jsonData.isodate = isoDate;
```

 - JSONデータから"CreateDate"フィールドを取り出し、この値を引数にしてDateを生成 → これでISODateになる
 - JSONデータに追加する

## MetabaseのTips

### Database nameなどの登録

MongoDBと連携するために、MetabaseのDatabase Admin画面で各種の登録を行います。その際の注意点は以下の通りです。

 - Database nameはMongoDBに書き込みを行った際に使用したDB名（コレクション名ではない）
 - Database usernameに加えて、Authentication Databaseを指定する → これを指定しないと認証エラーになり接続できません

<img width="806" alt="スクリーンショット 2019-12-27 10.04.18.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/162338/075dc589-885e-9679-d4e7-646352335eb3.png">

MetabaseAdmin → DataModelでデーター毎の属性を設定できますが、特に設定する必要はありません。温度・湿度などは数値データ、isodateは日付情報として認識してくれます。

### グラフのソース情報になるテーブルのカラムをいじってはいけない

これが一番ハマった点でした。

データをグラフ化する際は、以下のようにデータの一覧画面から、**Visualization**ボタンをクリックしてブラフの種類やX-Y軸に表示するカラムの指定を行います。この一覧のカラムは、MongoDBから取り出したキー情報をアルファベット順に並べているのですが、カラムを非表示にしたり表示順序を入れ替えることができます。

<img width="1337" alt="スクリーンショット 2019-12-27 10.22.06.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/162338/b1eacb7b-714a-5bee-584a-e41885302660.png">

最初はこの一覧を見やすくするために、IDを非表示にして日付を左側に持ってきるなどの、表示のカスタマイズを行っていました。表示をカスタマイズした状態からグラフを生成すると、X軸に日付を指定した際に以下の問題がありました。

 - 日付は表示されるが、時刻を表示してくれない
 - バージョンによっては、X軸にIsoDateを指定させてくれない

すなわち、**グラフのソース情報になるテーブルのカラムをいじってはいけない**ということです。こんなことドキュメントには書いてない！！（多分・・）です。

### データ数が増えた際の注意点

Metabaseは、DBの登録件数が2000レコードを超えると、デフォルトでは最初の2000レコードを表示するようになります。そのためデータ量が増えた場合は、グラフを描画する際にフィルター条件を設定し「直近の１ヶ月分」など、クエリー範囲を指定する必要があります。

### ダッシュボードのサンプル

収集データをグラフ化してダッシュボートに表示した画面の例を以下に示します。結構見栄えがよいグラフを表示してくれます。

<img width="1305" alt="スクリーンショット 2019-12-27 10.34.06.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/162338/662ed3ef-a938-19fb-7aa2-0aacb6ae02bb.png">