コピペで使うFirebase【Cloud Firestore編】

# 概要
firestoreを使う際にコピペで動かしたいのでそのまとめです。  

**公式ドキュメント**

[Cloud Firestore](https://firebase.google.com/docs/firestore?hl=ja)
[初期設定](https://firebase.google.com/docs/firestore/quickstart?hl=ja)
[データの追加](https://firebase.google.com/docs/firestore/manage-data/add-data?hl=ja)
[データの取得](https://firebase.google.com/docs/firestore/query-data/get-data?hl=ja)
[クエリー](https://firebase.google.com/docs/firestore/query-data/queries?hl=ja)
[リアルタイム更新](https://firebase.google.com/docs/firestore/query-data/listen?hl=ja)

<table>
<tr>
  <th colspan=3>FireStoreのメソッド一覧</th>
</tr>
<tr>
  <th>メソッド</th>
  <th>動作</th>
  <th>備考</th>
</tr>
<tr>
  <td>add</td>
  <td>追加</td>
  <td>ドキュメント名はランダムな文字列になる</td>
</tr>
<tr>
  <td>set</td>
  <td>追加</td>
  <td>ドキュメント名を指定できる</td>
</tr>
<tr>
  <td>set</td>
  <td>更新</td>
  <td>ドキュメントを更新(上書き)、フィールド追加</td>
</tr>
<tr>
  <td>update</td>
  <td>更新</td>
  <td>ドキュメントのフィールドを更新</td>
</tr>
<tr>
  <td>get</td>
  <td>取得</td>
  <td>ドキュメントを取得</td>
</tr>
<tr>
  <td>delete</td>
  <td>削除</td>
  <td>ドキュメントを削除</td>
</tr>
<tr>
  <td>onSnapshot </td>
  <td>取得</td>
  <td>変更の監視・データ取得</td>
</tr>
</table>

## 応用
- [ドキュメント名をidフィールドにももたせる場合](#ドキュメント名をidフィールドにももたせる場合)
- [前方一致検索](#前方一致検索)
- [範囲期間内のもの](#範囲期間内のもの)
- [ページネーション(別記事)](https://qiita.com/koffee0522/items/c025e3d7d888c4a9a6e3)


# 初期設定
インストール

```bash
$ npm install firebase --save
```

firestore初期化

```js
import firebase from "firebase/app"
import "firebase/firestore"

const firebaseConfig = {
    /* firebase config */
}

// 初期化は一度だけ
if (!firebase.apps.length) {
    firebase.initializeApp(firebaseConfig);
}
```

firestore参照

```js
const db = firebase.firestore();
```

# ドキュメント追加
## add
add() はドキュメント名にランダムなid(文字列)が入る。

```js
db.collection("user").add({ name: "taro" }).then(docRef => {
    // success
}).catch(error => {
    // error
})
```
保存されたデータ

```DB
user: {
    <ランダムなid>: {
        name: taro
    }
}
```
## set
set() はドキュメント名を指定できる。  
※ doc() を引数なしで使うとadd()と同じくドキュメント名にランダムなid(文字列)が入る。

```js
db.collection("user").doc("1").set({ name: "taro" }).then(docRef => {
    // success
}).catch(error => {
    // error
})
```
保存されたデータ

```DB
user: {
    1: {
        name: taro
    }
}
```

# ドキュメントの更新
## update
update() はdoc()に指定したドキュメントのフィールドを更新する  
※更新するフィールドがない場合はエラーとなる

```js
db.collection("user").doc("1").update({ name: "taro san" }).then(() => {
    // success
}).catch(error => {
    // error
})
```
更新前

```DB
user: {
    1: {
        name: taro
    }
}
```
更新後

```DB
user: {
    1: {
        name: taro san
    }
}
```
## set
set() を使うと対象のドキュメント自体を更新します

```js
db.collection("user").doc("1").set({ firstName: "taro", lastName: "aso" }).then(() => {
    // success
}).catch(error => {
    // error
})
```
更新前

```DB
user: {
    1: {
        name: "taro"
    }
}
```
更新後

```DB
user: {
    1: {
        firstName: "taro",
        lastName: "aso"
    }
}
```
### mergeオプション
set() ではmergeオプションをつけるとフィールドの追加もできます

```js
db.collection("user").doc("1").set({ firstName: "taro", lastName: "aso" }, {merge: true}).then(() => {
    // success
}).catch(error => {
    // error
})
```
更新前

```DB
user: {
    1: {
        name: "taro"
    }
}
```
更新後

```DB
user: {
    1: {
        name: "taro",
        firstName: "taro",
        lastName: "aso"
    }
}
```
# ドキュメントの取得
## get
get() はドキュメントを取得します。  
Firestoreには以下のデータが保存されているとします

```DB
user: {
    1: {
        name: "taro"
    },
    2: {
        name: "ken"
    }
}
```
コレクション内の全てのドキュメントを取得する場合

```js
/* 全てのドキュメントを配列に代入 */
const users = [];
db.collection("user").get().then((docs) => {
    // success
    if (docs.exists) {
        docs.fotEach(doc => {
            users.push(doc.data())
        })
    }
    console.log(users)
}).catch(error => {
    // error
})
```
取得データ

```bash
[{　name: "taro"　}, { name: "ken" }]　// type Array(2)
```
指定のドキュメントを取得する場合

```js
db.collection("user").get("2").then((doc) => {
    // success
    console.log(doc.data())
}).catch(error => {
    // error
})
```
取得データ

```　
{　name: "ken"　}
```
### where
指定のフィールドでフィルタリングしドキュメントを取得する場合

```js
db.collection("user").where('name', '==' 'taro').get().then((docs) => {
    // success
    console.log(doc.data())
}).catch(error => {
    // error
})
```
取得データ

```
{　name: "taro"　}
```

# ドキュメントの削除
## delete
delete() は対象のドキュメントを削除します。

```js
db.collection("user").doc("1").delete().then(() => {
    // success
}).catch(error => {
    // error
})
```

# リアルタイム更新
## onSnapshot
onSnapshot() は対象のドキュメントまたはコレクション内の複数のドキュメントの更新を検知しデータを取得します。

```js
// ドキュメント
db.collection("user").doc("1").onSnapshot((doc) => {
    // doc => 対象のドキュメント
}, (error) => {
    // ...
})

// コレクション
db.collection("user").onSnapshot((docs) => {
    // docs => コレクション内の複数のドキュメント
}, (error) => {
    // ...
})
```

# 応用
## ドキュメント名をidフィールドにももたせる場合

```js
db.collection("user").add({ id: "", name: "taro" }).then(docRef => {
    db.collection("user").doc(docRef.id).update({
        id: docRef.id
    }).then(() => {
        // success
    }).catch(error => {
    // error
    })
}).catch(error => {
    // error
})
```
保存されたデータ

```DB
user: {
    <ランダムなid>: {
        id: <ランダムなid>
        name: taro
    }
}
```

## 前方一致検索
```js
const word = "ke"
db
  .collection("user")
  .orderBy('name')
  .startAt(word)
  .endAt(word + '\uf8ff')
  .get()
  .then((docs) => {
    // success
  }).catch(error => {
    // error
  })
```
取得データ

```
[{　name: "ken"　}, {　name: "kenta"　}]
```

## 範囲期間内のもの
日付は`firebase.firestore.Timestamp`型である必要がある

```js
const startDate = new Date(1992, 5, 1)
const startDate = new Date(2000, 6, 1)
db
  .collection("user")
  .orderBy('birthdate', 'desc')
  .where('birthdate', '>=', startDate)
  .where('birthdate', '<=', endDate)
  .get()
  .then((docs) => {
    // success
  }).catch(error => {
    // error
  })
```
取得データ

```
[{　name: "ken" }]
```

# メモ
2021/07/26 随時更新中
追加予定
- 排他処理(同時更新制御)
- バッチ処理