[Firebase] Firestoreで読み書きする (Web編)

今回は最近ベータが取れて正式なサービスに昇格したFirestoreを使ってみたいと思います。まずは基本的なCRUDからやってみます。

Firestoreの準備

プロジェクトの作成

詳しくは過去記事を参照してください。 blog.katsubemakito.net

Databaseの作成

Firebaseのコンソールにログインしプロジェクトのトップページに移動します。 左メニューのDatabaseをクリックすると、右ペインでFirestoreにするか、以前の技術であるRealtime Databaseにするか選択を迫られますが、ここではFirestoreを選択します。

今回は学習が目的だったので一旦「テストモード」を選択しました。誰でもデータの取得や更新ができるモードです。この設定は後で変更できます。

最終的に以下のような画面になれば成功です。ここで直接データの参照はもちろん追加や変更なども行うことができます。

FirestoreとRealtimeDatabaseの違い

詳しくは以下のページを参照してください。 firebase.google.com

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

Firestoreのデータモデル

これも丸投げですみませんw 詳しくは以下のページを参照ください。 RDBや単純なKVSしか利用したことがないと最初は中々とっつきにくいですが、慣れですかね。 firebase.google.com

すごく雑に説明すると以下のようなイメージです。

ドキュメント
データの単位はRDBのような「レコード」ではなくドキュメントと呼ばれます。ドキュメントは単一のJSONデータをイメージしてください。JSONなのでフラッタな階層にもできますし、ネストすることも可能です。
コレクション
複数の「ドキュメント」をグルーピングした物をコレクションと呼びます。コレクションに格納されるドキュメントは異なるそれぞれがフィールドを持つことも許容されます。
リファレンス
ドキュメントやコレクションは一意に保存されています。この保存先を指し示す値をリファレンスと呼びます。

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

FirestoreでCRUDする

前提

まずFirebaseのコンソールに表示される内容をそのままコピペしここではconfig.jsという名前で保存しました。この設定内容はアカウント毎に異なりますので必ずご自身のものをご利用ください。

// コンソールの内容をそのままコピペ
var config = {
  apiKey: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  authDomain: "test-f76bc.firebaseapp.com",
  databaseURL: "https://test-f76bc.firebaseio.com",
  projectId: "test-f76bc",
  storageBucket: "test-f76bc.appspot.com",
  messagingSenderId: "1111111111111111"
};
firebase.initializeApp(config);

以下のようなHTML内にFirebaseを操作するためのJavaScriptを記述していきます。もちろん外部のファイルに書いてもかまいません。あくまで参考程度に。

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Hello FireStore</title>
</head>
<body>

<script src="https://www.gstatic.com/firebasejs/5.8.1/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/5.8.1/firebase-firestore.js"></script>
<script src="/js/config.js"></script>
<script>
  // Firestoreのオブジェクト取得
  var db = firebase.firestore();

  // このあたりにこれから掲載するサンプルコードなどを記述。
</script>
</body>
</html>

Create - データ作成

コレクションに対してadd()メソッドを実行すると、コレクション内にドキュメントが作成されます。指定したコレクションが存在しない場合は自動的に新規作成されます。RDBで言うCREATE TABLEのような定義は不要です。

db.collection("users").add({
  name: "マイメロ",
  age: 27
})
.then((doc) => {
  console.log(`追加に成功しました (${doc.id})`);
})
.catch((error) => {
  console.log(`追加に失敗しました (${error})`);
});

実行するとこのようにWebブラウザのコンソール上で確認できます。コレクション内でドキュメントを識別するためのIDは自動的に生成されます。

マイメロの年齢は調べたのですがよくわからなかったので仮の値を指定しました。ご了承ください。

Read - データ参照

単一のドキュメントを取得する

先ほど作成したドキュメントを取得してみます。doc()メソッドにドキュメントを特定するためのIDを指定してあげると実データではなくリファレンスが返ってきます。このリファレンスに対して操作を行います。

var docRef = db.collection("users").doc("ebpuQO9VsUEpEa4INxFb");

docRef.get().then((doc)=>{
  if (doc.exists) {
    console.log( doc.data() );
  }
  else {
    console.log("404");
  }
})
.catch( (error) => {
    console.log(`データを取得できませんでした (${error})`);
});

全件取得する

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

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(`データの取得に失敗しました (${error})`);
});

特定の条件のデータを取得する

コレクション内のデータを検索するにはwhere()メソッド。第2引数に条件を指定します。 以下のコードで年齢が27歳のデータのみが抽出されます。

db.collection("users").where("age", "==", 27)
  .get()
  .then((querySnapshot) => {
     querySnapshot.forEach( (doc) => {
       console.log(doc.id, " => ", doc.data());
     });
  })
  .catch( (error) => {
     console.log(`データの取得に失敗しました (${error})`);
  });

利用できる条件演算子は以下の通り。

  • 等価演算子(==)
  • 範囲比較(<、<=、>、>=)
AND条件

また以下のようにwhere()をメソッドチェーンでつなぐことでAND条件による絞り込みも可能です。注意点として等価演算子と範囲比較を組み合わせる場合にはカスタムインデックスと呼ばれるFirestoreの機能の利用が求められています。

db.collection("users")
  .where("age", "==", 27)
  .where("name", "==", "マイメロ")
OR条件

FirestoreではOR条件の検索はサポートされていないようです。したがってアプリ側で必要な条件分のデータを抽出し結合する必要があります。

var result = [];

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を渡すと昇順になります。

db.collection("users").orderBy("age")
  .get()
  .then((querySnapshot) => {
     querySnapshot.forEach( (doc) => {
       console.log(doc.id, " => ", doc.data());
     });
  })
  .catch( (error) => {
     console.log(`データの取得に失敗しました (${error})`);
  });

昇順にしたい場合は以下のようにします。

db.collection("users").orderBy("age", "desc")

取得件数を制限する

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

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

Update - データ更新

特定のドキュメントを更新するためにはドキュメントに対してset()メソッドを利用します。doc()メソッドにはドキュメントを特定するためのIDを指定します。

db.collection("users").doc("xxxxxxxx").set({
  name: "ポムポムプリン",
  age: 32
})
.then(()=>{
  console.log("更新に成功しました");
})
.catch((error)=>{
  console.log(`更新に失敗しました (${error})`);
});

例によってポムポムプリンが32歳かは不明です。ご了承ください。

Delete - データ削除

削除はドキュメントに対してdelete()メソッドを実行します。例えば全件削除するためには、削除したい件数分ループさせる必要があるようです。

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

サンプル

実行結果

このサンプルのデータは他の閲覧者の方と共有されていますので、同時に他の方が操作していると意図しない挙動になる場合があります。

ソースコード

参考ページ

firebase.google.com