Android 上でのデータのリストの操作

このドキュメントでは、Firebase でデータのリストを操作する方法について説明します。Firebase データの読み取りと書き込みの基本については、Android でのデータの読み取りと書き込みをご覧ください。

DatabaseReference を取得する

データベースに対してデータを読み書きするには、次に示す DatabaseReference のインスタンスが必要です。

private DatabaseReference mDatabase;
// ...
mDatabase = FirebaseDatabase.getInstance().getReference();

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

データのリストへの追加

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

push() メソッドによって返された新しいデータを参照することで、子の自動生成されたキーの値を取得することも、子のデータを設定することもできます。push() 参照の getKey() を呼び出すと、自動生成されたキーの値が返されます。

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

child イベントのリッスン

リストを操作するときには、単一のオブジェクトに使用される value イベントではなく、child イベントをアプリケーションでリッスンする必要があります。

push() メソッドによる新しい子の追加や、updateChildren() メソッドによる子の更新など、ノードの子に対して発生した特定の操作に応じて、child イベントがトリガーされます。それぞれのイベントを組み合わせると、データベース内の特定のノードの変更内容をリッスンするうえで役立つことがあります。

DatabaseReference で child イベントをリッスンするには、ChildEventListener をアタッチします。

リスナー イベント コールバック 一般的な使用方法
ChildEventListener onChildAdded() アイテムのリストを取得したり、アイテムのリストへの追加をリッスンしたりします。 このコールバックは既存の子ごとに 1 回トリガーされます。さらに、指定されたパスに新しい子が追加されると、そのたびに再びトリガーされます。リスナーに渡される DataSnapshot に、新しい子のデータが含まれています。
onChildChanged() リスト内のアイテムが変更されたかどうかリッスンします。このイベントは子ノードが変更されるたびに発生します。これには、子ノードの子孫での変更も含まれます。イベント リスナーに渡される DataSnapshot には、子の更新済みデータが含まれています。
onChildRemoved() リストからアイテムが削除されるかどうかリッスンします。イベントのコールバックに渡される DataSnapshot には、削除された子のデータが含まれています。
onChildMoved() 並べ替えリストの項目順変更をリッスンします。 このイベントは、更新により子の再並べ替えが発生して onChildChanged() コールバックがトリガーされるたびにトリガーされます。 orderByChild または orderByValue によるデータの並べ替えに使用されます。

たとえば、ソーシャル ブログ アプリでは、以下に示すようにこれらのメソッドを組み合わせて使用し、投稿のコメントでのアクティビティをモニタリングできます。

ChildEventListener childEventListener = new ChildEventListener() {
    @Override
    public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) {
        Log.d(TAG, "onChildAdded:" + dataSnapshot.getKey());

        // A new comment has been added, add it to the displayed list
        Comment comment = dataSnapshot.getValue(Comment.class);

        // ...
    }

    @Override
    public void onChildChanged(DataSnapshot dataSnapshot, String previousChildName) {
        Log.d(TAG, "onChildChanged:" + dataSnapshot.getKey());

        // A comment has changed, use the key to determine if we are displaying this
        // comment and if so displayed the changed comment.
        Comment newComment = dataSnapshot.getValue(Comment.class);
        String commentKey = dataSnapshot.getKey();

        // ...
    }

    @Override
    public void onChildRemoved(DataSnapshot dataSnapshot) {
        Log.d(TAG, "onChildRemoved:" + dataSnapshot.getKey());

        // A comment has changed, use the key to determine if we are displaying this
        // comment and if so remove it.
        String commentKey = dataSnapshot.getKey();

        // ...
    }

    @Override
    public void onChildMoved(DataSnapshot dataSnapshot, String previousChildName) {
        Log.d(TAG, "onChildMoved:" + dataSnapshot.getKey());

        // A comment has changed position, use the key to determine if we are
        // displaying this comment and if so move it.
        Comment movedComment = dataSnapshot.getValue(Comment.class);
        String commentKey = dataSnapshot.getKey();

        // ...
    }

    @Override
    public void onCancelled(DatabaseError databaseError) {
        Log.w(TAG, "postComments:onCancelled", databaseError.toException());
        Toast.makeText(mContext, "Failed to load comments.",
                Toast.LENGTH_SHORT).show();
    }
};
ref.addChildEventListener(childEventListener);

value イベントのリッスン

データのリストの読み取りには ChildEventListener を使用することが推奨されますが、リスト参照に ValueEventListener をアタッチするのが有効な場合もあります。

データのリストに ValueEventListener をアタッチすると、データのリスト全体が単一の DataSnapshot として返されるため、これをループして個別の子にアクセスすることができます。

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

// My top posts by number of stars
myTopPostsQuery.addValueEventListener(new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {
        for (DataSnapshot postSnapshot: dataSnapshot.getChildren()) {
            // TODO: handle the post
        }
    }

    @Override
    public void onCancelled(DatabaseError databaseError) {
        // Getting Post failed, log a message
        Log.w(TAG, "loadPost:onCancelled", databaseError.toException());
        // ...
    }
});

このパターンは、追加の onChildAdded イベントをリッスンする代わりに、単一の操作でリストのすべての子をフェッチする場合に便利です。

リスナーのデタッチ

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

1 つのデータの場所にリスナーが複数回追加されると、そのリスナーはイベントごとに複数回呼び出されるので、そのリスナーを完全に削除するには同じ回数だけデタッチする必要があります。

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

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

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

データを並べ替える

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

メソッド 使用方法
orderByChild() 指定した子キーの値で結果を並べ替えます。
orderByKey() 子キーで結果を並べ替えます。
orderByValue() 子の値で結果を並べ替えます。

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

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

// My top posts by number of stars
String myUserId = getUid();
Query myTopPostsQuery = databaseReference.child("user-posts").child(myUserId)
        .orderByChild("starCount");
myTopPostsQuery.addChildEventListener(new ChildEventListener() {
    // TODO: implement the ChildEventListener methods as documented above
    // ...
});

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

orderByChild() メソッドの呼び出しでは、結果を並べ替えるための子キーを指定します。ここでは、投稿はそれぞれの投稿の子 "starCount" の値で並べ替えられます。他の種類のデータを並べ替える方法の詳細については、クエリデータが並べ替えられる仕組みをご覧ください。

データのフィルタリング

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

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

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

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

// My top posts by number of stars
myTopPostsQuery.addValueEventListener(new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {
        for (DataSnapshot postSnapshot: dataSnapshot.getChildren()) {
            // TODO: handle the post
        }
    }

    @Override
    public void onCancelled(DatabaseError databaseError) {
        // Getting Post failed, log a message
        Log.w(TAG, "loadPost:onCancelled", databaseError.toException());
        // ...
    }
});

結果の個数を制限する

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

アイテムの変更に応じて、クエリに加わったアイテムに対する onChildAdded() コールバックと、クエリから外れたアイテムに対する onChildRemoved() コールバックを受け取り、合計数が 100 に保たれます。

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

// Last 100 posts, these are automatically the 100 most recent
// due to sorting by push() keys
Query recentPostsQuery = databaseReference.child("posts")
        .limitToFirst(100);

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

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

startAt()endAt()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() の場合と同じです。

次のステップ

フィードバックを送信...

ご不明な点がありましたら、Google のサポートページをご覧ください。