FirestoreのCRUD操作の書き方まとめ

JavaScriptでのFirestoreのCRUD処理をまとめます。
コールバックは省略します。

リアルタイムリスナーについては以下を参照してください。
[Firestoreのリアルタイムリスナーの書き方
](https://qiita.com/sano1202/items/4e378f86c1bb97196115)


# コレクションのリファレンスを取得
FireStoreではコレクションという仕組みがあります。
RDBでいうところのテーブルです。
コレクションのリファレンスを取得することで、コレクション単位の操作ができます。

```js
const itemsRef = db.collection('items');
```

# ドキュメントのリファレンスを取得
FireStoreではコレクションの中に複数のドキュメントがあります。
RDBでいうところのレコードです。
ドキュメントのリファレンスを取得することで、ドキュメント単位の操作ができます。

```js
const itemRef =  db.collection('items').doc('[DocumentId]'); 
```
docメソッドのの引数を空にすると、新規で生成したドキュメントへのリファレンスを返します。
その際のドキュメントID（主キー）は自動で生成されます。
また、ドキュメントのリファレンスは以下のように記述することも可能です。

```js
const itemRef =  db.doc('items/[DocumentId]'); 
```
リファレンスの取得では通信は発生せず、処理も同期的に行われます。

# 登録
登録をするにはaddメソッド、またはsetメソッドを使用します。

```js
const item = {
    name: "name",
    price: 100
};
 
db.collection('items').add(item);
```
addメソッドはドキュメントIDを自動で割り振ります。
setメソッドを実行するにはドキュメントのリファレンスを取得する必要があります。

```js
const item = {
    name: "name",
    price: 100
};
 
db.collection('items').doc('[DocumentId]').set(item);
```
setメソッドはドキュメントのリファレンスによって処理が異なります。

- docメソッドに存在しないDocumentIdを指定・・・新規登録
- docメソッドに既存のDocumentIdを指定・・・更新
- docメソッドに空を指定・・・addと同じ

# 更新
更新をするにはupdateメソッド、またはsetメソッドを使用します。

```js
// priceを200に更新
db.doc('items/[DocumentId]').update({price:200});
```
存在しないDocumentIdを指定した場合はエラーになります。
RDBのように複数件更新はできません。
**setによる更新では指定項目以外は空となります。**

# 削除
削除をするにはdeleteメソッドを使用します。

```js
db.doc('items/[DocumentId]').delete();
```
更新同様、存在しないDocumentIdを指定した場合はエラーになります。
更新同様、複数件を対象にすることはできません。

# 単一取得
単一取得をするにはドキュメントのリファレンスを指定し、getメソッドを使用します。

```js
// 同期的に取得したい場合はasync-await
const snapshot = await db.collection('items').doc('[DocumentId]').get();

// dataメソッドで、snapshotからオブジェクトを取り出す
const item = snapshot.data();
console.log(item.name);
```
# 全件取得
全件取得をするにはコレクションのリファレンスを指定し、getメソッドを使用します。

```js
const snapshot = await db.collection('items').get();

let items = [];

// 単一取得でない場合、オブジェクトはforEachで取り出す
snapshot.forEach(function(doc) {
    items.push(doc);
});

console.log(items[0].name);
```
# 条件付き取得

whereメソッドを使用してフィルタリングできます。

## 等価
等価条件を指定できます。

```js
const snapshot = db.collection('items')
                   .where('name', '==', 'a')
                   .get();
```
## in

in条件を指定できます。

```js
 const snapshot = db.collection('items')
                    .where('name' , 'in', ['item-a', 'item-b', 'item-c'])
                    .get();
```
## array-contains

array-containsで包含条件を指定できます。

```js
// nameに'a'を含む
const snapshot = db.collection('items')
                   .where('name' , 'array-contains', 'a')
                   .get();
 ```
## array-contains-any

array-contains-anyで複数の包含条件を指定できます。

```js
// nameに'a'または、'b'または'c'を含む
const snapshot = db.collection('items')
                   .where('name' , 'array-contains-any', ['a', 'b', 'c'])
                   .get();
```
# 複数条件
いくつか制限があり注意が必要です。

## 等価
等価はwhereメソッドを重ねることで複数指定することができます。

```js
const snapshot = db.collection('items')
                   .where('name', '==', 'a')
                   .where('price', '==', 100)
                   .get();
```
## 比較

単一のフィールドであれば比較も制限がありません。

```js
const snapshot = db.collection('items')
                   .where('price', '>=', 100)
                   .where('price', '<', 300)
                   .get();
```

ただし、複数フィールドの比較は無効です。

```js
// 無効
const snapshot = db.collection('items')
                   .where('price', '>=', 100)
                   .where('quantity', '<', 10)
                   .get();
```
> 複数比較をしなくて良いようなモデリングが必要になります。

また、等価 + 比較をする場合は、インデックスの追加が必要です。

```js
// 有効、ただしインデックスを生成する必要がある
const snapshot = db.collection('items')
                   .where('price', '==', 100)
                   .where('quantity', '<', 10)
                   .get();
```
> インデックスを生成せずに実行すると、エラーメッセージにURLが出力されます。
> アクセスすると自動的に必要なインデックスを作成します。

他にもinやarray-contains系は複数回使えないなどの制約があります。

# ソート
ソートを行うにはorderByメソッドを使用します。

```js
const snapshot = db.collection('items')
                   .where('price', '>=', 100)
                   .orderBy('price', 'desc') // 省略時は昇順
                   .get();
```
ソートも比較と同時に使用する場合は同じフィールドに対してのみ可能です。