Firebase RealtimeDatabaseからCloud Firestoreへの移行

# Cloud Firestoreの発表
Firebaseの開発をしている、Googleは2017年10月にRealtimeDatabaseの次世代版「Cloud Firestore」を発表しました。
2017年12月現在のCloud Firestoreはベータ版ですが移行の方法を簡単ではありますが記しておこうと思います。

Cloud Firestoreそのものの理解を深めたい方は下記の記事を参考にするとよいでしょう。

- [Cloud Firestoreは進化したFirebase Realtime Database]
(https://qiita.com/1amageek/items/8179aebe871beb230194)
- [Realtime Database デベロッパーのための Cloud Firestore](https://developers-jp.googleblog.com/2017/10/cloud-firestore-for-rtdb-developers.html)
- [Realtime Database デベロッパーのための Cloud Firestore](https://developers-jp.googleblog.com/2017/10/cloud-firestore-for-rtdb-developers.html)

# RealtimeDatabase から Cloud Firestoreへの移行


![image.png](https://qiita-image-store.s3.amazonaws.com/0/62429/d31b2e39-aabb-19ed-880f-7c90fcc3f984.png)

プロジェクトに入り、Databaseを選択後「RealtimeDatabase」と選択されている箇所をクリックすると画像のように「Cloud Firestore」が選択肢として表示されます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/62429/b394a3f1-abb0-a0bd-23cd-59d2a431f9e9.png)

選択後は、画像のような画面が表示されますロックモード / テストモードというのは、権限のことです。
今回はテストモードを選択します。

**もちろんこの時点で、RealtimeDatabaseが使えなくなることはありません。**


## 接続 / イニシャライズ

RealtimeDatabaseのコード：

```js:RealtimeDatabase
var config = {
  apiKey: "apiKey",
  authDomain: "projectId.firebaseapp.com"
};
firebase.initializeApp(config);
var db = firebase.database();
```


Cloud Firestoreのコード：

```js:CloudFirestore
firebase.initializeApp({
  apiKey: '### FIREBASE API KEY ###',
  authDomain: '### FIREBASE AUTH DOMAIN ###',
  projectId: '### CLOUD FIRESTORE PROJECT ID ###'
});
var db = firebase.firestore();
```

### 解説
特に、大きな変更はないようです。
`database` を `firestore` に変更するのだけは忘れないようにしたいですね。

## データの追加

RealtimeDatabaseのコード：

```js:RealtimeDatabase
db.ref('hoge').set('fuga')
```


Cloud Firestoreのコード：

```js:CloudFirestore
db.collection('hoge').add({fuga: 'piyo'})
```

### 解説
ここは結構な違いです。
RealtimeDatabaseにはcollectionというものがなかったので、画像のようにプロジェクト直下に値を入れることができました。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/62429/86538e3d-fbd9-1df2-043a-e923a8c845a1.png)

しかし、Firestoreは画像のようにドキュメントが追加され、**IDを付与することが絶対になりました。**
addをすれば、IDは自動生成されます。
たぶんこれはインデックスなどの関係でこうなったのだと思います。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/62429/ffecc33d-4345-dbf2-3424-7905957c711d.png)

## 1回のみのデータ取得

初回時などに1回だけデータを取る方法です。

RealtimeDatabaseのコード：

```js:RealtimeDatabase
db.ref('hoge').once('value', (snapshot) => {
　　console.log(snapshot.val())
})
// fuga
```


Cloud Firestoreのコード：

```js:CloudFirestore
this.db.collection('hoge').get().then((querySnapshot) => {
  querySnapshot.forEach((doc) => {
    console.log(`${doc.id} => ${doc.data()['fuga']}`)
  })
})
// QgG6SvUZABnU5Z7FfOrZ => piyo
```

### 解説
そもそもデータ構造が違うので、正確な比較はできませんがCloud Firestoreはコレクションに対して、`get()` メソッドを叩くとデータが取れます。その後Promiseでデータを好きなようにします。
ドキュメントのIDとその中のデータが取得できます。

また、 `data.val()` ではなく `ddoc.data()` になりました。

## 変更がある場合のみ取得 / リアルタイムに取得

RealtimeDatabaseのコード：

```js:RealtimeDatabase
db.ref('hoge').on('child_changed', (data) => {
  console.log(data.val())
})
```

Cloud Firestoreのコード：

```js:CloudFirestore
// 特定のドキュメントに対して
db.collection('hoge').doc('ID').onSnapshot((doc) => {
  console.log(doc.data())
})
// コレクションに対して
db.collection('hoge').onSnapshot((snapshot) => {
  console.log(snapshot.docChanges)
})
```

### 解説
Realtimedatabaseでは、 `.on('child_changed'...` という感じでしたがCloudStoreからは `.onSnapshot((doc)...` という形になりました。
また、特定のドキュメントに対しての変更だけを検知することも可能です。
もちろんコレクションに対しての監視も可能です。
`dotChanges` の中には簡単に書くとこんな感じのデータが入ってます

```json
[
  { 
    "doc": "... //データが入ってます。",
    "newIndex": 0,
    "oldIndex": -1,
    "type": "added"
  }
]
```

注目してほしいのは、 `type` の部分です。ここには `added`の他にも `modified ` (変更） `removed` (削除)　があります。ここで変更があったデータはどんな変更だったのかなども検知することが可能です。

## データの更新

RealtimeDatabaseのコード：

```js:RealtimeDatabase
db.ref().update({'hoge': 'fugafuga'})
```

Cloud Firestoreのコード：

```js:CloudFirestore
db.collection('hoge').doc('ID').update({
  fuga: 'piyopiyo'
})
```

### 解説
特にないです。普通にわかる感じですよね

## データの削除

RealtimeDatabaseのコード：

```js:RealtimeDatabase
db.ref('hoge').remove()
```

Cloud Firestoreのコード：

```js:CloudFirestore
// 特定のドキュメントに対して
db.collection('hoge').doc('fuga').delete()

// 特定のフィールドに対して
var hogeRef = db.collection('hoge').doc('ID')
var hogeFuga = hogeRef.update({
    fuga: firebase.firestore.FieldValue.delete()
})
```

### 解説
特定のドキュメントに対しての削除は問題になりそうな箇所はありません。
特定のフィールドに対しての削除は、`update` を使って `firebase.firestore.FieldValue.delete()` メソッドを使用して、削除用の値に更新すると消えるようです。

特定のコレクションに対しては、削除するのには **すべてのドキュメントを削除するしかないです。**

# 最後に
今回の記事では、おもに基本的な機能を解説しましたがFirestoreにはクエリなどの機能が追加されています。

また、まだ英語ではありますが[Firestoreのドキュメント](https://firebase.google.com/docs/firestore/) もとても充実していてわかりやすいです。

ぜひ何か試してみたいと思ったら、ドキュメント見てみるのはいいことです。

あと思ったのは、RealtimeDatabaseのときにちゃんと書いてない人はCloudFirestoreへの移行大変そうですね:joy: