Android
Kotlin
Firebase
CloudFirestore

Client Side Joinってなに?って方は下記の記事をご覧ください。
かなりわかりやすいです。

https://qiita.com/1amageek/items/afc1c0ceb15ffc2372fd
https://qiita.com/1amageek/items/64bf85ec2cf1613cf507

基本的な考え方🖋

基本的には表示する一覧のIDだけ持っておいて画面に描写するときだけ表示する分回数分詳細な情報を取りにいくことになります。

スクリーンショット 2018-06-03 9.44.05.png

↑の図でいうと「詳細情報」と書かれている回数(4回)Firestoreに問い合わせてデータを取りに行く手法になります。

RDBなどではこんな実装したら怒られそうですが、Firestoreではこの考え方が主流だそうです。

実装💻

例をあげて説明していきます。

ユーザーが所属しているグループ一覧を表示する取得する場合、データ構造はSubCollectionを使って下記のようにします。

/user/{userId}/group/{groupId}
/group/{groupId}/user/{userId}

({userId}と{groupId}はFirestoreで自動生成されたIDです)

このデータ構造でユーザーが所属しているグループ一覧をリスト表示したいとします。

1、ユーザーが所属しているグループIDを取得しにいく

まずはユーザーが所属しているグループIDを取得しましょう。

        val db = FirebaseFirestore.getInstance()
        db.collection("user").document(userId)
                .collection("group")
                .get()
                .addOnSuccessListener{
                  // 成功したとき
                  if (it.isEmpty) return@OnSuccessListener
                  val groupIdList = ArrayList<String>()
                  val dataList = it.documents

                  dataList.forEach {
                    groupIdList.add(it.id)
                  }
                }
                .addOnFailureListener{
                  // 失敗したとき
                }

(userId はこのアプリを使っているユーザーのIDのことです)

所属しているIDを一覧で取得して ArrayList<String> で格納しています。
この実装では取得順序が保証されていませんが、リストに表示するグループの順序を決めたい場合はここで並び替えるか、 Query を使うなどしてください

取得したID一覧をRecyclerViewのセルを生成するAdapterに渡す

ここで取得したIDはセルを生成するAdapterに渡しましょう。

class GroupAdapter(private val activity: GroupActivity, private val groupIdList: ArrayList<String>) : RecyclerView.Adapter<GroupAdapter.ViewHolder>() {

    private val inflater = LayoutInflater.from(activity)

    override fun onCreateViewHolder(viewGroup: ViewGroup, i: Int): ViewHolder {
        return ViewHolder(inflater.inflate(R.layout.listview_group_item, viewGroup, false))
    }

    override fun onBindViewHolder(viewHolder: ViewHolder, i: Int) {
        val groupId = groupIdList[i]

        activity.showProgress()

        val db = FirebaseFirestore.getInstance()
        db.collection("group").document(groupId).get().addOnSuccessListener {

            if (it.exists()){
                // セルの描写タイミングでデータを取得し、画面にアタッチする
                val title = it["title"] as String
                viewHolder.groupTitle.text = title

                if (it["description"] != null){
                    val description = it["description"] as String
                    viewHolder.groupDescription.text = description
                }

                if (it["icon_url"] != null){
                    val iconUrl = it["icon_url"] as String
                    Picasso.with(activity).load(iconUrl).transform(CircleTransform()).fit().centerCrop().into(viewHolder.iconView)
                }
            }

        }.addOnFailureListener {
           // 失敗したとき
        })
    }

    override fun getItemCount(): Int {
        return groupIdList.size
    }


    class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {

        val groupTitle: TextView = view.groupTitle
        val groupDescription: TextView = view.groupDescription
        val iconView: ImageView = view.iconView
    }
}

これで完了です。

実際描写に遅くないの??😢

自分も最初はそう思ってましたが、実際やってみるとそこまで遅くないです。
Firebase素晴らしい。
ぜひ試してみてください。

ClientSideJoin.mov.gif

宣伝🎫

以前に「Androidで使うFirebase入門」というタイトルで記事を書きましたのでよろしければこちらもどうぞ

https://note.mu/shogoyamada/n/nb2645e007bad

ツイッター↓
https://twitter.com/yshogo87