You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
DatabasePagingSource (and therefore also FirebaseRecyclerPagingAdapter) not working when firebase query contains orderByChild clause.
I have verified that the problem is not in FirebaseUI itself but in method
privateQuerystartAt(Nodenode, Stringkey)
in Query class in package com.google.firebase.database;
according code javadocs for the method:
* Createsaqueryconstrainedtoonlyreturnchildnodeswithavaluegreaterthanorequalto
* thegivenvalue, usingthegiven {@codeorderBy} directiveorpriorityasdefault, and
* additionallyonlychildnodeswithakeygreaterthanorequaltothegivenkey.
but I have verified that if the query is using an orderByChild clause, the key parameter in startAt is ignored
Because of this I am getting crashes cause by exception
java.lang.IllegalStateException: The same value, VWGuwwgOg3tg_15vtqkJf32bhICi-zI_yt---, was passed as the nextKey in two
sequential Pages loaded from a PagingSource. Re-using load keys in
PagingSource is often an error, and must be explicitly enabled by
overriding PagingSource.keyReuseSupported.
because, since the key parameter is ignored, we reread each time the same data, each time the method
public Single<LoadResult<String, DataSnapshot>> loadSingle(@NonNull LoadParams<String> params)
is called
Steps to reproduce:
see above
Proposed Fix
I have written code to fix the issue. note that I am working with a fork of the original FirebaseUI-Android, so I have the original code converted to Kotlin, but you can get the idea if you look at it. In brief what I do is keep not only the key of the last read document but also the value of the childkey, and use them together when I need to query for the next batch of documents
Code With Bug Fix:
classDatabasePagingSource(privatevalmQuery:Query) : PagingSource<Any, DataSnapshot>() {
@SuppressLint("RestrictedApi")
fungetChildValue(snapshot:DataSnapshot, index:PathIndex):Any? {
val keypath=index.queryDefinition
val data=snapshot.child(keypath)
if(!data.exists()) returnnullreturn data.value
}
fun Query.startAt_childvalue(startvalue:Any?,keyvalue:String?):Query {
returnwhen(startvalue) {
isString-> startAt(startvalue,keyvalue)
isBoolean-> startAt(startvalue,keyvalue)
isDouble-> startAt(startvalue,keyvalue)
isLong-> startAt(startvalue.toDouble(),keyvalue)
else->this
}
}
/** * reason of SuppressLint: DatabaseError.fromStatus() is not meant to be public.*/
@SuppressLint("RestrictedApi")
overridesuspendfunload(params:LoadParams<Any>): LoadResult<Any, DataSnapshot> {
//change mQuery.startAt at value if child index//if not null then what we have here is orderByChild queryvar querychildpathindex:PathIndex?= mQuery.spec.index as?PathIndexval pkey=params.key asPair<Any?,String>?val task:Task<DataSnapshot> =if (params.key ==null) {
mQuery.limitToFirst(params.loadSize).get()
} else {
if (querychildpathindex !=null) //orderByChild query mode
mQuery.startAt_childvalue(pkey?.first, pkey?.second).limitToFirst(params.loadSize +1).get()
else
mQuery.startAt(null,pkey?.second).limitToFirst(params.loadSize +1).get()
}
try {
val dataSnapshot = task.await()
if (dataSnapshot.exists()) {
//Make List of DataSnapshotval data:MutableList<DataSnapshot> =ArrayList()
var lastKey:Pair<Any?,String>?=nullif (params.key ==null) {
for (snapshot in dataSnapshot.children) {
data.add(snapshot)
}
} else {
val iterator:Iterator<DataSnapshot> = dataSnapshot.children.iterator()
//Skip First Item that corresponds to lastKey read in previous batchif (iterator.hasNext()) {
iterator.next()
}
while (iterator.hasNext()) {
val snapshot = iterator.next()
data.add(snapshot)
}
}
//Detect End of Dataif (!data.isEmpty()) {
//Get Last Keyval lastkey_c = getLastPageChildKey(data,querychildpathindex)
val lastkey_k = getLastPageKey(data)
lastKey =if (lastkey_c ==null&& lastkey_k ==null)
nullelseif (lastkey_k ==null) Pair(lastkey_c, "") elsePair(lastkey_c, lastkey_k)
}
return toLoadResult(data, lastKey)
} else {
val details =DETAILS_DATABASE_NOT_FOUND+ mQuery.toString()
throwDatabaseError.fromStatus(
STATUS_DATABASE_NOT_FOUND,
MESSAGE_DATABASE_NOT_FOUND,
details
).toException()
}
} catch (e:ExecutionException) {
returnLoadResult.Error<Any, DataSnapshot>(e)
}
}
privatefuntoLoadResult(
snapshots:List<DataSnapshot>,
nextPage:Pair<Any?,String>?
): LoadResult<Any, DataSnapshot> {
returnLoadResult.Page(
snapshots,
null, // Only paging forward.
nextPage,
LoadResult.Page.COUNT_UNDEFINED,
LoadResult.Page.COUNT_UNDEFINED
)
}
privatefungetLastPageChildKey(data:List<DataSnapshot>,index:PathIndex?): Any? {
if(index==null) returnnullreturnif (data.isEmpty()) {
null
} else {
getChildValue(data[data.size -1],index)
}
}
privatefungetLastPageKey(data:List<DataSnapshot>): String? {
returnif (data.isEmpty()) {
null
} else {
data[data.size -1].key
}
}
overridefungetRefreshKey(state:PagingState<Any, DataSnapshot>): Pair<Any?, String>? {
returnnull
}
companionobject {
privateconstvalSTATUS_DATABASE_NOT_FOUND="DATA_NOT_FOUND"privateconstvalMESSAGE_DATABASE_NOT_FOUND="Data not found at given child path!"privateconstvalDETAILS_DATABASE_NOT_FOUND="No data was returned for the given query: "
}
}
The text was updated successfully, but these errors were encountered:
@beyondeye thank you so much for the well detailed explanation and the code solution. Maybe you could send a PR implementing your proposed solution?
I understand you might be discouraged because your solution uses Kotlin and our codebase is in Java - if that's the case, I'll try to come up with a PR myself.
Step 2: Describe your environment
Step 3: Describe the problem:
DatabasePagingSource (and therefore also FirebaseRecyclerPagingAdapter) not working when firebase query contains
orderByChild
clause.I have verified that the problem is not in FirebaseUI itself but in method
in
Query
class in packagecom.google.firebase.database
;according code javadocs for the method:
but I have verified that if the query is using an
orderByChild
clause, thekey
parameter instartAt
is ignoredBecause of this I am getting crashes cause by exception
because, since the
key
parameter is ignored, we reread each time the same data, each time the methodis called
Steps to reproduce:
Proposed Fix
I have written code to fix the issue. note that I am working with a fork of the original FirebaseUI-Android, so I have the original code converted to Kotlin, but you can get the idea if you look at it. In brief what I do is keep not only the key of the last read document but also the value of the childkey, and use them together when I need to query for the next batch of documents
Code With Bug Fix:
The text was updated successfully, but these errors were encountered: