Firebase & Retrieving Elements

2019-06-23 19:59发布

问题:

I'm trying to read x amount of elements from Firebase, but I have a feeling I'm misunderstanding something...

DataSnapshot returns the correct child count, however when I try to loop through the children, the loop never executes.

Note: Code in Kotlin

fun list(count: Int, callback: ListCallback) {
    val playersRef = firebase.child("players")
    val queryRef = playersRef.orderByChild("rank").limitToFirst(count)
    queryRef.addListenerForSingleValueEvent(object : ValueEventListener {

        override fun onCancelled(error: FirebaseError?) {
            Log.e("firebase", error!!.message)
        }

        override fun onDataChange(snapshot: DataSnapshot?) {
            val children = snapshot!!.children
            // This returns the correct child count...
            Log.i("firebase", children.count().toString())
            val list = ArrayList<Entry>()
            // However, this loop never executes
            children.forEach {
                val e = Entry()
                e.name = it.child("name").value as String
                e.rank = it.child("rank").value as Long
                e.wins = it.child("wins").value as Long
                e.losses = it.child("losses").value as Long
                Log.i("firebase", e.toString())
                list.add(e)
            }
            callback.onList(list)
        }
    })
}

回答1:

This works for me:

val firebase: Firebase = Firebase("https://stackoverflow.firebaseio.com/34378547")

fun main(args: Array<String>) {
    list(3)
    Thread.sleep(5000)
}

fun list(count: Int) {
    val playersRef = firebase.child("players")
    val queryRef = playersRef.orderByChild("rank").limitToFirst(count)
    queryRef.addListenerForSingleValueEvent(object : ValueEventListener {

        override fun onCancelled(error: FirebaseError?) {
            println(error!!.message)
        }

        override fun onDataChange(snapshot: DataSnapshot?) {
            val children = snapshot!!.children
            // This returns the correct child count...
            println("count: "+snapshot.children.count().toString())
            children.forEach {
                println(it.toString())
            }
        }
    })
}

Output:

count: 2
DataSnapshot { key = -K6-Zs5P1FJLk4zSgNZn, value = {wins=13, name=fluxi, rank=1, losses=1} }
DataSnapshot { key = -K6-ZtdotHkkBzs5on9X, value = {wins=10, name=puf, rank=2, losses=42} }

Update

In the comments there was some discussion about why snapshot.children.count() works, while children.count() doesn't. The problem is caused by two facts:

  1. Firebase's DataSnapshot.getChildren() returns an Iterable, which can be iterated forward only (as is the contract of an Iterable).
  2. The Kotlin count() loops over the Iterable to count its items.

So after Kotlin's count() is done, the Iterable is at the end of the sequence. The subsequent for loop has nothing to loop over anymore. In my snippet I call snapshot.children separately to get a separate iterator to get the count.

Knowing how Kotlin implements count() it is better to use Firebase's built-in childrenCount:

println("count: "+snapshot.childrenCount)