初めてのFirebaseを触ってみる(Database)

#はじめに
おはようございます。こんにちは。こんばんは。
今回はFirebase Databaseを解説していきます。
ではやっていきます。
※今回もwebベース(Vue.js)の解説です。
#Firebase Database
Firebaseが提供しているデータベースは２種類あります。
 
- Realtime Database
- Cloud Firestore

*Realtime Database は従来からある Firebase のデータベースです。リアルタイムのクライアント間同期が必要なモバイルアプリのための、効率的でレイテンシが低いソリューションです。
Cloud Firestore は、Firebase のモバイルアプリ開発用の新しい主力データベースです。直感的な新しいデータモデルで、Realtime Database をさらに効果的にしています。Cloud Firestore は、Realtime Database よりも多彩で高速なクエリと高性能なスケーリングが特長です。*

※どちらかと言うとしたのCloud Firestoreを使うことを推奨されていますが、本記事では両方解説します。長くなると思うので、見たい方を見ていただければ結構です。
因みにどちらもSQLを叩かないNoSQL（Not only SQL）です。
#共通の設定
どちらを使うにもデータベースを作る必要があるので作ります。
##管理画面
###Databaseをクリックし、「データベース作成」をクリック
![picture_pc_7c109045ec1ba9ce9119176de397da92.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116696/6f9f099f-b5c4-7518-1a58-8e8f5487004e.jpeg)


###テストモードにする
![picture_pc_8cf6a78f0db3c65da0f85b54b5eab466.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116696/f9cfddc8-3f55-0f32-b18a-e04c68aa58a4.jpeg)

##Realtime Databaseの使い方
[https://firebase.google.com/docs/database?hl=ja](https://firebase.google.com/docs/database?hl=ja)
ルールの箇所を図のように「resd」、「write」を``true``にしてください。
※今回は練習ようなのでセキュリティを考えていない。
![picture_pc_eb48d8988a6fb3f6afd538f4eb5f6f0b.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116696/33534a50-6384-d78b-76bf-9a04cd53ae7d.jpeg)

※firebaseを使うファイルには必ず以下の構文を``<script>``タグの中
に記述してください。

```js
import firebase from '@/plugins/firebase'
```
↑これはなんなんだ？って方は[こちら](https://qiita.com/watataku8911/items/94274031a4e49c9cc9a0)。
###データの追加
```js
const database = firebase.database();
const room = "chat_room";
database.ref(room).push({
    title: "タイトル",
    body: "本文",
});
```
データベースでデータの読み書きを行う為には、firebase.database.Referenceのインスタンスが必要です。次のコードを使用します。

```js
const database = firebase.database();
```

let room ="chat_room";で、指定したroomにデータを記録しています。この値を変更することにより、データの格納先を変えることができます。

```js
const room = "chat_room";
```

データベースへのデータを書き込むメソッドはpush()の他にset()、update()、transaction()があります。
※ほとんどの場合push()を使えば良いと思うw

```js
database.ref(room).push({
    //DBにつっこみたい値
});
```
###データ参照

```js
const database = firebase.database();
const room = "chat_room";
database.ref(room)
.on("value", (data)=> {
    if (data) {
        const rootList = data.val();
        const key = data.key;
        let list = [];
        // データオブジェクトを配列に変更する
        if(rootList != null) {
            Object.keys(rootList).forEach((val, key) => {
                rootList[val].id = val;
                list.push(rootList[val]);
            })
        }
        
    }
});
```
上のコードで全件参照ができます。
####TIPS
全件参照はこれでいいとしても場合によればSQLで言うとwhere ○○ = ???としたくなる時があります。
ここではそれっぽい方法を紹介していきましょう。
例えばこのようなデータがあるとします。

```
{
  "users" : {
    "aaa" : {
      "age" : 21,
      "name" : "sato"
    },
    "bbb" : {
      "age" : 18,
      "name" : "suzuki"
    },
    "ccc" : {
      "age" : 20,
      "name" : "tanaka"
    },
    "ddd" : {
      "age" : 17,
      "name" : "takahashi"
    },
    "eee" : {
      "age" : 25,
      "name" : "ito"
    }
  }
}
```
Firebaseのコードはこんな感じでRealtime Databaseを使用しています。
#####WHERE ID = ‘aaa’
シンプルにuserのkeyで取得する場合です。

```js

firebase.database().ref('users/aaa')
.once('value',function(snapshot) {console.log(snapshot.val())})

> {age: 21, name: "sato"}
```
#####WHERE name = ‘sato’
nameのvalueで検索する場合です。
orderByChildを使用すると検索対象のkeyにindexを設定しないと警告が表示されます。
‘sato’に一致した値が全て返されます。

```js

firebase.database().ref('/users')
.orderByChild('name').startAt('sato').endAt('sato')
.once('value',function(snapshot) {console.log(snapshot.val())})

> {aaa: {…}}
```
#####WHERE age BETWEEN 18 AND 21
範囲で指定して検索してみます。

```js
firebase.database().ref('/users')
.orderByChild('age').startAt(18).endAt(21)
.once('value',function(snapshot) {console.log(snapshot.val())})

> {aaa: {…}, bbb: {…}, ccc: {…}}
```
#####LIMIT 2
取得数を制限します。
この例では年齢順に2件まで取得しています。
昇順降順と書いていますがハッシュで返されるので順番ではないです。

limitToFirstは該当条件の先頭から取得します。(昇順)
bbbは18才、dddは17才

```js
.database().ref('/users').orderByChild('age')
.startAt(1).limitToFirst(2)
.once('value',function(snapshot) {console.log(snapshot.val())})

> {bbb: {…}, ddd: {…}}
```
limitToLastは該当条件の後方から取得します。(降順)
aaaは21才、eeeは25才

```js
firebase.database().ref('/users').orderByChild('age')
.startAt(1).limitToLast(2)
.once('value',function(snapshot) {console.log(snapshot.val())})

> {aaa: {…}, eee: {…}}
```
#####WHERE name like ‘ta%’

```js
firebase.database().ref('/users')
.orderByChild('name').startAt('ta').endAt('ta\uf8ff')
.once('value',function(snapshot) {console.log(snapshot.val())})

> {ccc: {…}, ddd: {…}}
```
###データ編集

```js
const database = firebase.database();
const room = "chat_room";
database.ref(room).child("※1キー").update({
    //変更したいもの
});
```
<font color="red">※1:push()でDBに突っ込むとRDBで言う主キーに当たるIDが自動的に割り当てられる為それを使う</font>

###データ削除

```js
const database = firebase.database();
const room = "chat_room";

database.ref(room).child("※1キー").remove();
```

##Cloud Firestoreの使い方
[https://firebase.google.com/docs/firestore?hl=ja](https://firebase.google.com/docs/firestore?hl=ja)
Firebase Realtime Databaseの次世代版。Realtime Databaseはデーターベース制約でいろいろとパフォーマンス等考慮したデータ構造にしないといけなかったが、Cloud Firestoreは自由度が高まったため、データベース制約をあまり意識せずにデータ格納できるようになった。
###用語解説
Cloud FirestoreではRealtime Databaseでは出てこない用語があるので初めにそちらを紹介します。

- コレクション：複数の「ドキュメント」をグルーピングした物をコレクションと呼びます。MySQLでいうテーブルのようなもの
- ドキュメント：データの単位はRDBのような「レコード」ではなくドキュメントと呼ばれます。

**イメージ:**
<img width="292" alt="Cloud_Firestore_ _ _Firebase.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116696/d9901833-f712-2cb5-b808-4dec7625163b.png">

*ドキュメントの中にコレクション入れその中にドキュメントが…といったことも可能です。非常に柔軟なデータ構造を作成することができますが最初から縦横無尽に使いこなす必要ないので、簡単な構造から徐々に慣れていくのが良いかと思います。*

##設定
こちらもルールの箇所を``true``にしときます。
※今回は練習ようなのでセキュリティを考えていない。
<img width="1075" alt="marknote_–_Database_–_Firebase_console.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116696/c29b8405-875c-23c6-19f3-cf6dc3cefe54.png">



##データ追加

```js
const db = firebase.firestore();
db.collection("users").add({
  //DBに突っ込みたいもの
  name: "マイメロ",
  age: 27
})
.then((doc) => {
  console.log(`追加に成功しました`);
})
.catch((error) => {
  console.log(error);
});
```
コレクションに対して``add()``メソッドを実行すると、コレクション内にドキュメントが作成されます。指定したコレクションが存在しない場合は自動的に新規作成されます。
実行するとこのようにWebブラウザのコンソール上で確認できます。コレクション内でドキュメントを識別するためのIDは自動的に生成されます。
<img width="695" alt="_Firebase__Firestoreで読み書きする__Web編_.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116696/557e96da-66eb-d2c1-9db0-6759517ea3c3.png">


##データ参照
###全件取得する
コレクション内のドキュメントを全件取得するには``get()``メソッドを利用します。

```js
const db = firebase.firestore();
db.collection("users").get().then((query) => {
  var buff = [];
  query.forEach((doc) => {
    var data = doc.data();
    buff.push([doc.id, data.name, data.age]);
  });
  console.log(buff);
})
.catch((error)=>{
  console.log(`データの取得に失敗しました`);
});
```
###特定の条件のデータを取得する
コレクション内のデータを検索するには``where()``メソッド。第2引数に条件を指定します。 以下のコードで年齢が27歳のデータのみが抽出されます。

```js
const db = firebase.firestore();
db.collection("users").where("age", "==", 27)
.get()
.then((querySnapshot) => {
 querySnapshot.forEach( (doc) => {
   console.log(doc.id, " => ", doc.data());
 });
})
.catch( (error) => {
 console.log(`データの取得に失敗しました (${error})`);
});
```
利用できる条件演算子は以下の通り。

- 等価演算子（==）
- 範囲比較（<、<=、>、>=）

※``where()``をメソッドチェーンでつなぐことでAND条件による絞り込みも可能です。
注意点として等価演算子と範囲比較を組み合わせる場合にはカスタムインデックスと呼ばれるFirestoreの機能の利用が求められています。
なお、FirestoreではOR条件の検索はサポートされていないようです。
したがってこのようにアプリ側で必要な条件分のデータを抽出し結合する必要があります。

```js
let result = [];

const db = firebase.firestore();
db.collection("users").where("age", "==", 27).get().then( (querySnapshot)=>{
  querySnapshot.forEach( (doc) => {
     result.push([doc.id, doc.data()]);
  });
});

db.collection("users").where("name", "==","マイメロ").get().then( (querySnapshot)=>{
  querySnapshot.forEach( (doc) => {
     result.push([doc.id, doc.data()]);
  });
});

console.log(result);
```
###ソートする
コレクションに対して``orderBy()``メソッドを利用します。第1引数にはソートしたい項目を指定します
デフォルトでは降順となりますが、第2引数に文字列descを渡すと昇順になります。

```js
const db = firebase.firestore();
db.collection("users").orderBy("age")
.get()
.then((querySnapshot) => {
 querySnapshot.forEach( (doc) => {
   console.log(doc.id, " => ", doc.data());
 });
})
.catch( (error) => {
  console.log(`データの取得に失敗しました`);
});
```
昇順にしたい場合は以下のようにします。

```js
const db = firebase.firestore();
db.collection("users").orderBy("age", "desc")
```

###取得件数を制限する
コレクションに``limit()``メソッドを実行すれば最大取得件数を指定することができます。以下の場合は3件だけ取得します。

```js
const db = firebase.firestore();
db.collection("users").limit(3)
.get()
.then((querySnapshot) => {
 querySnapshot.forEach( (doc) => {
   console.log(doc.id, " => ", doc.data());
 });
})
.catch( (error) => {
  console.log(`データの取得に失敗しました`);
});
```
###データ更新

```js
const db = firebase.firestore();
db.collection("users").doc("w1bHp4ODzSo9Hlr6mhpY").update({
  name: "ポムポムプリン"
})
.then(()=>{
  console.log("更新に成功しました");
})
.catch((error)=>{
  console.log(`更新に失敗しました (${error})`);
});
```

###データ削除

```js
const db = firebase.firestore();
db.collection("users").doc("xxxxxxxx").delete().then(() => {
  console.log("削除しました");
})
.catch((error) => {
  console.log(`削除に失敗しました (${error})`);
});
```


以上。
長くなりましたが両方解説して見ました。解説が浅いところとか、間違い等があると思いますのでその時はアドバイス等お願いします。
次回はfirebase Storageを解説していきます。

最後まで読んでいただきありがとうございました。
[Twitter](https://twitter.com/watataku9)やってます。良ければチェックして見てください。:point_up::point_up::point_up:

#リンク
- [初めてのFirebaseを触ってみる(導入編)](https://qiita.com/watataku/items/94274031a4e49c9cc9a0)
- [初めてのFirebaseを触ってみる(Authentication)](https://qiita.com/watataku/items/9f134b44ead087abe68b)