ウェブ上でデータリストを操作する

データベース参照を取得する

データベースでデータの読み書きを行うには、firebase.database.Reference のインスタンスが必要です。

ウェブ向けのモジュラー API

import { getDatabase } from "firebase/database";

const database = getDatabase();

名前空間が指定されたウェブ API

var database = firebase.database();

リストの読み取りと書き込み

リストへのデータの追加

マルチユーザー アプリケーションでリストにデータを追加するには push() メソッドを使用します。指定した Firebase 参照に新しい子が追加されるたびに、push() メソッドは一意のキーを生成します。この自動生成されたキーをリスト内の新しい各要素に使用することで、書き込みの競合を伴わずに複数のクライアントが同時に子を同じ場所に追加できます。push() によって生成される一意のキーはタイムスタンプに基づいているため、リストのアイテムは自動的に時系列で並べ替えられます。

push() メソッドによって返された新しいデータを参照することで、子の自動生成されたキーの値を取得することも、子のデータを設定することもできます。push() 参照の .key プロパティには自動生成されたキーが含まれています。

この自動生成されたキーを使用すると、データ構造を平坦化しやすくなります。詳細については、データのファンアウトのをご覧ください。

たとえば、次のように push() を使用すると、ソーシャル アプリケーションの投稿リストに新しい投稿を追加できます。

ウェブ モジュラー API

import { getDatabase, ref, push, set } from "firebase/database";

// Create a new post reference with an auto-generated id
const db = getDatabase();
const postListRef = ref(db, 'posts');
const newPostRef = push(postListRef);
set(newPostRef, {
    // ...
});

名前空間が指定されたウェブ API

// Create a new post reference with an auto-generated id
var postListRef = firebase.database().ref('posts');
var newPostRef = postListRef.push();
newPostRef.set({
    // ...
});

child イベントのリッスン

push() メソッドによる新しい子の追加や、update() メソッドによる子の更新など、ノードの子に対して発生した特定のオペレーションに応じて child イベントがトリガーされます。

イベント 一般的な使用方法
child_added アイテムのリストを取得します。また、アイテムのリストへの追加をリッスンします。このイベントは既存の子ごとに 1 回トリガーされます。さらに、指定されたパスに新しい子が追加されると、そのたびに再びトリガーされます。リスナーには、新しい子のデータを含んでいるスナップショットが渡されます。
child_changed リスト内のアイテムに対する変更がないかリッスンします。子ノードが修正されるたびにこのイベントがトリガーされます。この変更には、子ノードの子孫に対する変更も含まれます。イベント リスナーに渡されるスナップショットには、子の更新済みデータが含まれています。
child_removed リストから削除されたアイテムがないかリッスンします。このイベントは直接の子が削除されるとトリガーされます。コールバック ブロックに渡されるスナップショットには、削除された子のデータが含まれています。
child_moved 並べ替えリスト内の項目順の変更をリッスンします。child_moved イベントは常に、(現在の order-by メソッドに基づいて)アイテムの並べ替えが変更される原因となった child_changed イベントに後続します。

それぞれのイベントを組み合わせると、データベース内の特定のノードの変更内容をリッスンするうえで役立つことがあります。たとえば、ソーシャル ブログアプリでは、以下に示すようにこれらのメソッドを組み合わせて使用し、投稿のコメントでのアクティビティをモニタリングできます。

ウェブ モジュラー API

import { getDatabase, ref, onChildAdded, onChildChanged, onChildRemoved } from "firebase/database";

const db = getDatabase();
const commentsRef = ref(db, 'post-comments/' + postId);
onChildAdded(commentsRef, (data) => {
  addCommentElement(postElement, data.key, data.val().text, data.val().author);
});

onChildChanged(commentsRef, (data) => {
  setCommentValues(postElement, data.key, data.val().text, data.val().author);
});

onChildRemoved(commentsRef, (data) => {
  deleteComment(postElement, data.key);
});

名前空間が指定されたウェブ API

var commentsRef = firebase.database().ref('post-comments/' + postId);
commentsRef.on('child_added', (data) => {
  addCommentElement(postElement, data.key, data.val().text, data.val().author);
});

commentsRef.on('child_changed', (data) => {
  setCommentValues(postElement, data.key, data.val().text, data.val().author);
});

commentsRef.on('child_removed', (data) => {
  deleteComment(postElement, data.key);
});

value イベントのリッスン

データのリストを読み取るための推奨される方法は child イベントのリッスンですが、リスト参照で value イベントをリッスンするのが有効な場合もあります。

データのリストに value オブザーバーをアタッチすると、データのリスト全体が単一のスナップショットとして返されるため、ループして個別の子にアクセスできます。

クエリで一致が 1 つしかない場合でも、スナップショットはリストになります(ただし、アイテムは 1 つしか含まれていません)。アイテムにアクセスするには、結果をループする必要があります。

ウェブ モジュラー API

import { getDatabase, ref, onValue } from "firebase/database";

const db = getDatabase();
const dbRef = ref(db, '/a/b/c');

onValue(dbRef, (snapshot) => {
  snapshot.forEach((childSnapshot) => {
    const childKey = childSnapshot.key;
    const childData = childSnapshot.val();
    // ...
  });
}, {
  onlyOnce: true
});

名前空間が指定されたウェブ API

ref.once('value', (snapshot) => {
  snapshot.forEach((childSnapshot) => {
    var childKey = childSnapshot.key;
    var childData = childSnapshot.val();
    // ...
  });
});

このパターンは、child が追加された他のイベントをリッスンするのではなく、単一のオペレーションでリストのすべての子を取得する際に便利です。

データの並べ替えとフィルタリング

Realtime Database の Query クラスを使用して、キー、値、または子の値で並べ替えられたデータを取得できます。また、並べ替えられた結果を、特定の件数またはある範囲のキーや値に限定するようフィルタリングできます。

データを並べ替える

並べ替えられたデータを取得するには、最初にいずれかの order-by メソッドを指定して、結果の並べ替え方法を決定します。

メソッド 用途
orderByChild() 指定した子キーまたはネストされた子のパスの値によって結果を並べ替えます。
orderByKey() 子キーによって結果を並べ替えます。
orderByValue() 子の値によって結果を並べ替えます。

同時に使用できる order-by メソッドは 1 つだけです。同じクエリで order-by メソッドを複数回呼び出すとエラーがスローされます。

次の例では、あるユーザーの上位の投稿をスターの数で並べ替えたリストを取得しています。

ウェブ モジュラー API

import { getDatabase, ref, query, orderByChild } from "firebase/database";
import { getAuth } from "firebase/auth";

const db = getDatabase();
const auth = getAuth();

const myUserId = auth.currentUser.uid;
const topUserPostsRef = query(ref(db, 'user-posts/' + myUserId), orderByChild('starCount'));

名前空間が指定されたウェブ API

var myUserId = firebase.auth().currentUser.uid;
var topUserPostsRef = firebase.database().ref('user-posts/' + myUserId).orderByChild('starCount');

ここでは、子リスナーと組み合わせて、ユーザー ID に基づいてデータベース内のパスからクライアントをユーザーの投稿と同期させるクエリを定義しています。ユーザー ID は、それぞれの投稿が受けたスターの数で並べ替えられています。このように ID をインデックス キーとして使用する手法はデータのファンアウトと呼ばれています。詳細については、データベースの構造化をご覧ください。

orderByChild() メソッドの呼び出しでは、結果を並べ替えるための子キーを指定します。この場合、投稿はそれぞれの子 "starCount" の値によって並べ替えられます。次のようなデータの場合、クエリはネストされた子によって並べ替えることもできます。

"posts": {
  "ts-functions": {
    "metrics": {
      "views" : 1200000,
      "likes" : 251000,
      "shares": 1200,
    },
    "title" : "Why you should use TypeScript for writing Cloud Functions",
    "author": "Doug",
  },
  "android-arch-3": {
    "metrics": {
      "views" : 900000,
      "likes" : 117000,
      "shares": 144,
    },
    "title" : "Using Android Architecture Components with Firebase Realtime Database (Part 3)",
    "author": "Doug",
  }
},

この場合、orderByChild() の呼び出しで、ネストされた子への相対パスを指定することにより、metrics キーの下にネストされた値によってリスト要素を並べ替えることができます。

ウェブ モジュラー API

import { getDatabase, ref, query, orderByChild } from "firebase/database";

const db = getDatabase();
const mostViewedPosts = query(ref(db, 'posts'), orderByChild('metrics/views'));

名前空間が指定されたウェブ API

var mostViewedPosts = firebase.database().ref('posts').orderByChild('metrics/views');

他の種類のデータを並べ替える方法の詳細については、クエリデータが並べ替えられる仕組みをご覧ください。

データのフィルタリング

データをフィルタリングするために、クエリの構築時に、制限メソッドまたは範囲メソッドのいずれかを order-by メソッドと組み合わせることができます。

メソッド 用途
limitToFirst() 並べ替えられた結果リストの先頭から返すアイテムの最大数を設定します。
limitToLast() 並べ替えられた結果リストの末尾から返すアイテムの最大数を設定します。
startAt() 選択した order-by メソッドに応じて、指定したキーまたは値以上のアイテムを返します。
startAfter() 選択した order-by メソッドに応じて、指定したキーまたは値より大きいアイテムを返します。
endAt() 選択した order-by メソッドに応じて、指定したキーまたは値以下のアイテムを返します。
endBefore() 選択した order-by メソッドに応じて、指定したキーまたは値より小さいアイテムを返します。
equalTo() 選択した order-by メソッドに応じて、指定したキーまたは値に等しいアイテムを返します。

order-by メソッドとは異なり、複数の制限関数または範囲関数を組み合わせることができます。たとえば、startAt() メソッドと endAt() メソッドを組み合わせて、結果を特定の範囲の値に制限できます。

結果の個数を制限する

limitToFirst() メソッドと limitToLast() メソッドを使用して、特定のイベントに対して同期する子の最大数を設定できます。たとえば、limitToFirst() を使用して上限 100 を設定した場合は、初期状態で最大 100 個の child_added イベントのみを受け取ります。Firebase データベースに保存されているアイテムが 100 個よりも少ない場合、それぞれのアイテムに対する child_added イベントが発生します。

アイテムの変更に応じて、クエリに加わったアイテムに対する child_added イベントと、クエリから外れたアイテムに対する child_removed イベントを受け取り、合計数が 100 に保たれます。

次の例では、サンプルのブログアプリで全ユーザーの最新 100 件の投稿のリストを取得するクエリを定義しています。

ウェブ モジュラー API

import { getDatabase, ref, query, limitToLast } from "firebase/database";

const db = getDatabase();
const recentPostsRef = query(ref(db, 'posts'), limitToLast(100));

名前空間が指定されたウェブ API

var recentPostsRef = firebase.database().ref('posts').limitToLast(100);

この例では、クエリを定義しているだけです。データを実際に同期するには、リスナーのアタッチが必要です。

キーまたは値によるフィルタリング

startAt()startAfter()endAt()endBefore()equalTo() を使用して、クエリ用に任意の始点、終点、同値点を選択できます。これは、データをページ分けするときや、特定の値を持つ子があるアイテムを検索するときに役立ちます。

クエリデータが並べ替えられる仕組み

このセクションでは、Query クラスのそれぞれの order-by メソッドでデータがどのように並べ替えられるのかを説明します。

orderByChild

orderByChild() を使用すると、指定した子キーを含むデータは次のように並べ替えられます。

  1. 指定した子キーに null 値を持つ子が最初に来ます。
  2. 指定した子キーの値が false である子が次に来ます。複数の子が値 false を持つ場合、キーで辞書順に並べ替えられます。
  3. 指定した子キーの値が true である子が次に来ます。複数の子が値 true を持つ場合、キーで辞書順に並べ替えられます。
  4. 数値を持つ子が次に来ます。ただし、昇順に並べ替えられます。指定した子ノードに同じ数値を持つ複数の子がある場合、キーで並べ替えられます。
  5. 文字列は数値の後に来て、辞書順で昇順に並べ替えられます。指定した子ノードに同じ値を持つ複数の子がある場合、キーで辞書順に並べ替えられます。
  6. オブジェクトが最後に来て、キー名の辞書順で昇順に並べ替えられます。

orderByKey

orderByKey() を使用してデータを並べ替えると、キー名で昇順にデータが返されます。

  1. 32 ビット整数として解析できるキーを持つ子が最初に来ます。ただし、昇順に並べ替えられます。
  2. 文字列値をキーとして持つ子が次に来ます。ただし、辞書順で昇順に並べ替えられます。

orderByValue

orderByValue() を使用すると、子がその値で並べ替えられます。並べ替えの条件は、指定した子キーの値の代わりにノードの値が使用されるという点を除いて、orderByChild() の場合と同じです。

リスナーのデタッチ

コールバックを削除するには、Firebase データベース参照で off() メソッドを呼び出します。

単一のリスナーは、パラメータとしてリスナーを off() に渡すと削除できます。off() を引数なしで呼び出すと、その場所にあるすべてのリスナーが削除されます。

親リスナーで off() を呼び出しても、その子ノードに登録されているリスナーが自動的に削除されるわけではありません。また、コールバックを削除するために子リスナーで off() も呼び出す必要があります。

次のステップ