スマホアプリ開発を加速する,Firebaseを使ってみよう

第3回 データの読み出しをマスターする

この記事を読むのに必要な時間:およそ 11 分

前回の連載では,Firebaseに簡単なデータを保存し,それを実際にAndroidクライアントから読み出してみました。今回は,より詳細なデータの取得方法と,取得するデータのソート,取得するデータ数のリミットや範囲といった複雑なクエリについて解説します。

今回は例として,リアルタイムチャットアプリケーションを作成すると想定して以下の様なデータを用意し,さまざまな方法で読み出してみたいと思います。

  • /messages以下に,チャットのメッセージ情報を{"sender":"送信者", "body":"本文"}の一覧形式で格納
  • /counts以下に,どのユーザが何通メッセージを受信したかという情報を{"ユーザ名":数}という形式で格納

イベントとリスナ

前回「イベント駆動型プログラミング」の節でご説明したように,Firebaseではデータが欲しいタイミングでプログラマが能動的に取りに行く「Pull型」ではなく,データが追加されたり変更されたタイミングで通知される「Push型」でデータを取得します。

このような「データが追加された」「データが変更された」といった契機のことをイベントと呼びます。

プログラマは,コードブロックに「受け取ったデータをどのように扱いたいか」を記述しておき,その処理を実行して欲しいイベントに関連付けます。このコードブロックはリスナと呼ばれ,イベントにコードブロックを紐付けることを「リスナを登録する」と表現します。

今回作成するリアルタイムチャットアプリケーションでは,⁠新着メッセージが到着した」というのがイベントで,⁠新着メッセージを受け取ったらメッセージ一覧に追加し,画面に新着メッセージを表示する」という処理がリスナです。

イベントが発生するたびにリスナに書いてある処理は実行され,ユーザがわざわざ画面をリロードしなくても「新着メッセージが到着するたびにメッセージを表示する」といったリアルタイムなアプリケーションを開発することがFirebaseでは可能なのです。

Firebaseのイベント一覧

Firebaseでは処理開始の契機として利用できるイベントが全部で5つあります。

イベントタイミング
Value イベントデータの新規追加/変更のタイミングで呼び出される
Child Added イベントリストのようなデータ構造で,子要素が新規追加されたタイミングで呼び出される
Child Changed イベントリストのようなデータ構造で,子要素が変更されたタイミングで呼び出される
Child Removed イベントリストのようなデータ構造で,子要素が削除されたタイミングで呼び出される
Child Moved イベントリストのようなデータ構造で,子要素が移動されたタイミングで呼び出される

それぞれについて詳しく解説していきます。

サンプルデータ

今回作成するリアルタイムチャットアプリケーションのために,Webコンソールを使って/messagesに以下のようなデータを用意してください。

{
  "messages" : {
    "01" : {
      "body" : "Hi, there!",
      "sender" : "John"
    },
    "02" : {
      "body" : "What's up?",
      "sender" : "Steve"
    },
    "03" : {
      "body" : "hey hey hey!",
      "sender" : "Bill"
    }
  }
}

Webコンソールの使い方は前回の連載を参考にしてください。

Firebaseにおける配列やリストの扱い

FirebaseではデータをJSONツリーとして保持することを連載第1回で述べました。

では,なぜ今回のリアルタイムチャットアプリケーションの例では,メッセージ一覧を以下のような配列形式で保持しなかったのでしょう?

{
  "messages" : [
    {
      "body" : "Hi, there!",
      "sender" : "John"
    },
    {
      "body" : "What's up?",
      "sender" : "Steve"
    },
    {
      "body" : "hey hey hey!",
      "sender" : "Bill"
    }
  ]
}

これにはFirebaseの制約事項が関係しています。端的に言うと,Firebaseでは配列はネイティブではサポートされていません。

['hello', 'world']のようなデータを保存すると,Firebaseでは内部的に{0: 'hello', 1: 'world'}のように,整数をキーにしたオブジェクトとしてデータを保持します。

この理由は,Firebaseでは,データが非同期に追加/削除/変更されるケースが頻繁に発生するためだと公式サイトでは説明されています。 つまり,配列の添字というものは一時的なもので,要素の数が変わると添字も振り直されるし,ある値に対する添字は常に一意には定まりません。 データの読み書きが常に並列で行われるFirebaseでは,このような不確定要素の多い添字でデータの更新を行うことが困難であるため,一度定まったら常に一定であるキー:値の形式で保持することにしているようです。

ただし,Firebaseのクライアントライブラリはプログラマの負担を減らすため,クライアントライブラリからは何も考えず['hello', 'world']のような配列形式で保存すれば内部的に{0: 'hello', 1: 'world'}として保持され,読み出す時も同様に{0: 'hello', 1: 'world'}['hello', 'world']であるかのように読み出すことができます。

しかしながらこれにも「すべてのキーが整数であり,半分以上の値が空でない場合に限る」という制限がありますので,詳しく知りたい方はBest Practices: Arrays in Firebaseという記事を読んでみてください。

著者プロフィール

白山文彦(しろやまふみひこ)

サーバサイド,インフラ,Androidなど何でもやるプログラマ。

コメント

コメントの記入