NoSQL触ったことない人のためのFirestoreハンズオン

# Firestoreとは

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199678/7d4bf4dc-7886-9327-f7d0-8ae07d28331b.png" width="300">

https://firebase.google.com/docs/firestore?hl=ja
> Cloud Firestore は、Firebase と Google Cloud Platform からのモバイル、ウェブ、サーバー開発に対応した、柔軟でスケーラブルなデータベースです。Firebase Realtime Database と同様に、リアルタイム リスナーを介してクライアント アプリ間でデータを同期し、モバイルとウェブのオフライン サポートを提供します。これにより、ネットワークの遅延やインターネット接続に関係なく機能するレスポンシブ アプリを構築できます。Cloud Firestore は、その他の Firebase および Google Cloud Platform プロダクト（Cloud Functions など）とのシームレスな統合も実現します。

mBaaS「Firebase」で提供しているNoSQLサービスです。
FirebaseではMariaDBやMySQL、PostgreSQLといったRDBMSを提供していないため、Firebaseに閉じた開発をするのであれば、データベースとしてNoSQLであるFirestoreを使うことになります。

# NoSQLとは

すごく雑に言うと
**RDBMSに比べ、スケーラブルだけど、良くも悪くも緩い**のがNoSQL。

NoSQL触ったことがなく噂で「早いらしい」と聞いたことがあると、隣の芝生が青く見えるので
「じゃあ今後作るアプリケーションは全てNoSQLでいこう」となるかもしれませんが、そんな単純ではないです。

NoSQLのサービスにもよりますが、基本的に「整合性取れなくてもいいので、水平分散して運用できる」仕組みです。
そのため、

- トランザクションが使えないサービスが多い（Firestoreは使える）
- スキーマの定義ができない
- not null制約やdefault句等も無い
- GROUP BYやCOUNTといった集計もできない
- ORやLIKE検索ができない
- !=句の絞り込みができない
- 一括でUPDATE、DELETEができない

といったように、RDBMS脳で考えるとそんなこともできないの？！となります。
なのでRDBMSの代替品としてではなく、ケースによって使い分けることが大切です。

# 早速触ってみる

習うより慣れろ、ということであれこれ解説するより実際触ってみましょう。
以前のFirebase Authenticationハンズオンから大部分コピペです。

# ハンズオン

必要なもの

- Ruby
    - localhostで実行するため、Webサーバが必要になる。Webサーバを立ち上げられるなら他言語でもOK
- Googleアカウント

なお、今回は必要最低限の知識と最小工数を目指すため、フロントエンドも素のHTML5とJavaScriptでいきます。

ステップは全部で5つです。

1. Firebaseプロジェクトの作成
2. Firebase Webアプリケーションの作成
3. Firestoreの設定
4. Webコンソールでデータの追加をいろいろ試してみる
5. Webフロントエンドで取得する

## 1. Firebaseプロジェクトの作成

- https://console.firebase.google.com/
- 「プロジェクトを追加」選択
- プロジェクトの名前は、判別できれば何でもOK
    - なお、プロジェクト名はグローバルでユニークなものになるため、`test`のように既に作られているプロジェクト名を指定すると末尾にランダムな文字列が付く
- GoogleアナリティクスはOFFでOK

これだけでプロジェクトが作られる。

## 2. Firebase Webアプリケーションの作成

ダッシュボードの以下アイコンから、Webアプリの追加を行う
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199678/66adce58-b169-8ee5-fe86-fef037bdf84a.png" width="360">

- アプリ名は、これも認識できれば自由に付けてOK
- Hostingは今回は使わない
- Firebase Admin SDKのソースも一旦無視して続行

## 3. Firestoreの設定

- 左メニュー「開発」から、Cloud Firestoreを選択
- 「データベースの作成」ボタン押下
- 「テストモードで開始する」を選択
- asia-northeast1が東京、asia-northeast2が大阪なのでお好きな方を。
    - ロケーション参考：https://firebase.google.com/docs/firestore/locations?hl=ja

これだけで、Firestoreの設定が完了しました。

## 4. Webコンソールでデータの追加をいろいろ試してみる

Webコンソールでいろいろ試してみてください。

- 「コレクションを開始」でusersと入れ、次へ
- ドキュメントIDは自動IDクリックでOK
- フィールドにtitle, タイプはstring, 値にhogeといれて保存
    - 厳密さを抜きにして用語をRDBでざっくり翻訳すると、
    - コレクションはRDBで言うテーブル
    - ドキュメントはRDBで言うレコード
    - フィールドはRDBで言うカラム
    - タイプはRDBで言うデータ型

- 適当に複数ドキュメントやコレクションを作ってみましょう。
    - 次の3つを試してみましょう
    - 1. 別のドキュメントで、今度はフィールドtitleを作らないで違うフィールド名で作成
    - 2. ドキュメントの下にbookingsというコレクションを作成し、ドキュメントを作成
    - 3. 定義したフィールドやドキュメント、コレクションを削除する
- RDBと異なる点はどういったところか？をいろいろ実感できると良いです
    - ドキュメントの下にコレクションを作ることができる（レコードの中にテーブルを入れ子にできるようなもの）
    - フィールドをドキュメントごとに自由に追加・削除できる（逆に言えばスキーマを定義できない）
    - あたりはRDBと大きく異なる点。


- 遊び終えたらフロントエンドからusersコレクションを取得するため、usersの中に「title」というフィールドを持つドキュメントを3つほど保存しておきます。

## 5. Webフロントエンドで取得する

- ローカルで適当なディレクトリを作成し、index.htmlを作成
- 以下ソースコードを貼り付け。

```html:index.html
<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="utf-8">
        <script src="https://www.gstatic.com/firebasejs/8.0.2/firebase-app.js"></script>
        <script src="https://www.gstatic.com/firebasejs/8.0.2/firebase-firestore.js"></script>
        <script>
        // Your web app's Firebase configuration
        var firebaseConfig = {
            apiKey: "XXXXXXXX",
            authDomain: "XXXXXXXX",
            databaseURL: "XXXXXXXX",
            projectId: "XXXXXXXX",
            storageBucket: "XXXXXXXX",
            messagingSenderId: "XXXXXXXX",
            appId: "XXXXXXXX"
        };
        firebase.initializeApp(firebaseConfig);
        </script>
    </head>
    <body>
        <ul id="users"></ul>
        <script>
        var db = firebase.firestore();
        var ul = document.getElementById("users");
        db.collection("users")
          .get()
          .then(function(querySnapshot) {
            querySnapshot.forEach(function(doc) {
              var li = document.createElement('li');
              li.textContent = doc.data().title;
              ul.appendChild(li);
              console.log(doc.id, " => ", doc.data());
            });
          })
          .catch(function(error) {
            console.log("Error getting documents: ", error);
          });
        </script>
    </body>
</html>
```

変更が必要な箇所

```js
        var firebaseConfig = {
            apiKey: "XXXXXXXX",
            authDomain: "XXXXXXXX",
            databaseURL: "XXXXXXXX",
            projectId: "XXXXXXXX",
            storageBucket: "XXXXXXXX",
            messagingSenderId: "XXXXXXXX",
            appId: "XXXXXXXX"
        };
```

この部分はFirebaseプロジェクトからコピーして置換してください。
![key.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199678/d4cc0190-2f14-e077-98e7-b6b78c348a08.png)

あとはWebサーバを立ち上げるだけ。

index.htmlを保存したディレクトリに移動し、

```bash
$ ruby -run -e httpd . -p 8000
```

Webブラウザでlocalhost:8000にアクセスします。

こんな感じに表示されればOK。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199678/4228cc88-9fd1-67b2-5cad-38065df2270a.png" width="300">

# 最後に

今回は実験目的で「テストモード」実行をしましたが、本番運用する時はしっかりセキュリティルールへの理解と設定が必要です。
今回は時間の関係で割愛したので、本番運用を目指す方は、必ず学習しましょう。