データベース参照を取得する
データベースでデータの読み書きを行うには、firebase.database.Reference
のインスタンスが必要です。
var database = firebase.database();
データの読み取りと書き込み
このドキュメントでは、データの取得に関する基本と、Firebase データの並べ替えとフィルタリングの方法について説明します。
Firebase データは、非同期リスナーを firebase.database.Reference
にアタッチして取得します。リスナーはデータの初期状態で 1 回トリガーされます。さらに、データが変更されると、そのたびに再トリガーされます。
基本的な書き込みオペレーション
基本的な書き込みオペレーションには、set()
を使用してデータを特定の参照に保存できます。そのパスにある既存のデータが置換されます。たとえば、ソーシャル ブログアプリでは、次のように set()
でユーザーを追加できます。
function writeUserData(userId, name, email, imageUrl) { firebase.database().ref('users/' + userId).set({ username: name, email: email, profile_picture : imageUrl }); }
set()
を使用すると、特定の場所にあるデータ(子ノードも含む)が上書きされます。
value イベントのリッスン
パスにあるデータを読み取り、変更をリッスンするには、firebase.database.Reference
の on()
メソッドまたは once()
メソッドを使用して、イベントを監視します。
イベント | 一般的な使用方法 |
---|---|
value |
パスのコンテンツ全体に対する変更の読み取りとリッスンを行います。 |
value
イベントを使用して、特定のパスにあるコンテンツの静的スナップショットを、イベントの発生時に存在していたとおりに読み取ることができます。このメソッドはリスナーがアタッチされたときに 1 回トリガーされます。さらに、データ(子も含む)が変更されると、そのたびに再びトリガーされます。イベントのコールバックには、その場所にあるすべてのデータ(子のデータも含む)を含んでいるスナップショットが渡されます。データが存在しない場合、スナップショットから返されるのは、exists()
を呼び出した場合は false
、val()
を呼び出した場合は null
です。
次の例は、データベースから投稿のスターの数を取得するソーシャル ブログ アプリケーションを示しています。
var starCountRef = firebase.database().ref('posts/' + postId + '/starCount'); starCountRef.on('value', (snapshot) =>{ const data = snapshot.val(); updateStarCount(postElement, data); });
リスナーは snapshot
を受信します。これには、イベントのときにデータベース内の指定された場所にあったデータが含まれています。snapshot
のデータは val()
メソッドを使用して取得できます。
データの 1 回読み取り
変更を想定していない UI 要素を初期化するときなど、変更がないかリッスンせずにデータのスナップショットがほしい場合があります。once()
メソッドを使用して、このシナリオを簡素化できます。このメソッドは 1 回トリガーされ、その後再びトリガーされることはありません。
これは 1 回読み込む必要があるだけで頻繁な変更やアクティブなリッスンを行うことは想定していないデータに対して有用です。たとえば、前述の例のブログアプリでは、このメソッドを使用して、ユーザーが新しい投稿を作成し始めたときにユーザーのプロフィールを読み込んでいます。
var userId = firebase.auth().currentUser.uid; return firebase.database().ref('/users/' + userId).once('value').then((snapshot) => { var username = (snapshot.val() && snapshot.val().username) || 'Anonymous'; // ... });
データを更新または削除する
特定のフィールドを更新する
他の子ノードを上書きすることなく、ノードの特定の複数の子に同時に書き込むには、update()
メソッドを使用します。
update()
の呼び出し時に、キーのパスを指定して下位レベルの子の値を更新できます。スケーラビリティを向上させるためにデータが複数の場所に保存されている場合、データのファンアウトを使用してそのデータのすべてのインスタンスを更新できます。
たとえば、次のようなコードを使用して、ソーシャル ブログアプリで、投稿を作成し、その投稿で最近のアクティビティのフィードと投稿ユーザーのアクティビティのフィードを同時に更新するとします。
function writeNewPost(uid, username, picture, title, body) { // A post entry. var postData = { author: username, uid: uid, body: body, title: title, starCount: 0, authorPic: picture }; // Get a key for a new Post. var newPostKey = firebase.database().ref().child('posts').push().key; // Write the new post's data simultaneously in the posts list and the user's post list. var updates = {}; updates['/posts/' + newPostKey] = postData; updates['/user-posts/' + uid + '/' + newPostKey] = postData; return firebase.database().ref().update(updates); }
この例では、push()
を使用して、/posts/$postid
にある全ユーザーの投稿が格納されているノード内に投稿を作成すると同時に、キーを取得しています。その後、このキーを使用して、/user-posts/$userid/$postid
にあるユーザーの投稿に別のエントリを作成できます。
これらのパスを使用すると、上記の例で両方の場所に新しい投稿を作成したように、update()
を 1 回呼び出すだけで JSON ツリー内の複数の場所に対して更新を同時に実行できます。この方法による同時更新はアトミック(不可分)です。つまり、すべての更新が成功するか、すべての更新が失敗するかのどちらかです。
完了コールバックの追加
データがいつ commit(確定)されたのかを把握したい場合は、完了コールバックを追加できます。set()
と update()
はどちらも、完了コールバックをオプションとして取ります。このコールバックは、書き込みがデータベースに commit されたときに呼び出されます。呼び出しが失敗した場合は、失敗した理由を示すエラー オブジェクトがコールバックに渡されます。
firebase.database().ref('users/' + userId).set({ username: name, email: email, profile_picture : imageUrl }, (error) => { if (error) { // The write failed... } else { // Data saved successfully! } });
データの削除
データを削除する最も簡単な方法は、そのデータの場所への参照の remove()
を呼び出すことです。
また、他の書き込みオペレーション(set()
や update()
など)の値として null
を指定する方法でも削除できます。この方法と update()
を併用すると、API を 1 回呼び出すだけで複数の子を削除できます。
Promise
の受信
Firebase Realtime Database サーバーにデータが commit されたときにそれを知るには、Promise
を使用します。set()
と update()
のどちらも、書き込みがデータベースに commit されたタイミングの把握に使用できる Promise
を返すことができます。
リスナーのデタッチ
コールバックを削除するには、Firebase データベース参照の off()
メソッドを呼び出します。
単一のリスナーは、パラメータとしてリスナーを off()
に渡すと削除できます。off()
を引数なしで呼び出すと、その場所にあるすべてのリスナーが削除されます。
親リスナーの off()
を呼び出しても、その子ノードに登録されているリスナーが自動的に削除されるわけではありません。また、コールバックを削除するために子リスナーの off()
も呼び出す必要があります。
トランザクションとしてのデータの保存
増分カウンタなど、同時変更によって破損する可能性があるデータを操作する場合は、トランザクション オペレーションを使用します。このオペレーションには、update 関数とオプションの完了コールバックを与えることができます。update 関数はデータの現在の状態を引数として取り、書き込みたい新しい状態を返します。新しい値が正常に書き込まれる前に別のクライアントがその場所に書き込んだ場合、update 関数が現在の新しい値で再度呼び出されて、書き込みが再試行されます。
たとえば、このソーシャル ブログアプリの例では、次のようにして、投稿にスターを付ける / 投稿のスターを取り消す操作をユーザーに許可し、投稿で得られたスターの数を追跡できます。
function toggleStar(postRef, uid) { postRef.transaction((post) => { if (post) { if (post.stars && post.stars[uid]) { post.starCount--; post.stars[uid] = null; } else { post.starCount++; if (!post.stars) { post.stars = {}; } post.stars[uid] = true; } } return post; }); }
トランザクションを使用することで、複数のユーザーが同じ投稿にスターを同時に付けた場合や、クライアントのデータが古くなった場合でも、スターの数が不正確になることを防ぎます。トランザクションが拒否された場合、サーバーは現在の値をクライアントに返します。クライアントは更新された値でトランザクションを再度実行します。トランザクションが受け入れられるか、トランザクションを中止するまでこの処理が繰り返されます。
オフラインでデータを書き込む
クライアントでネットワーク接続が切断された場合でも、アプリは引き続き適切に機能します。
Firebase データベースに接続しているクライアントはそれぞれ、アクティブ データの内部バージョンを独自に保持しています。データが書き込まれると、まず、このローカル バージョンに書き込まれます。次に Firebase クライアントは、「ベスト エフォート」ベースでそのデータをリモート データベース サーバーや他のクライアントと同期します。
その結果、データベースへの書き込みが発生すると、実際にサーバーへデータが書き込まれるよりも早く、ローカル イベントが直ちにトリガーされます。つまり、ネットワークのレイテンシや接続に関係なく、アプリは応答性の高い状態を維持します。
接続が再確立されると、アプリは適切な一連のイベントを受け取り、クライアントが現在のサーバー状態と同期されます。この処理のためにカスタムコードを記述する必要はありません。