在我的firestore数据库中,有12个以上的文档。我可以通过调用以下函数on button click
正确地获取前三个文档。但是在secondclick
上,尽管documentReference正确传递,但它没有检索任何数据。querySnapshot大小即将为0。这可能是问题所在。
以下是声明
private val db: FirebaseFirestore = FirebaseFirestore.getInstance()
private val colRef: CollectionReference = db.collection("Notebook")
private var lastResult: DocumentReference? = null
private lateinit var query: Query
以下是onButtonClick
代码:private fun loadNoteNew() {
@Suppress("SENSELESS_COMPARISON", "LiftReturnOrAssignment")
if (lastResult == null) {
query = colRef.orderBy("priority")
.limit(3)
} else {
Log.i(TAG, "Start ${lastResult!!.id}")
query = colRef.orderBy("priority")
.startAfter(lastResult)
.limit(3)
}
Log.i(TAG, "before get")
query.get()
.addOnSuccessListener { querySnapshot ->
var data = ""
Log.i(TAG, "querySnapshot Size : ${querySnapshot.size()}")
if (lastResult != null) {
Log.i(TAG, "querySnapshot ID : ${lastResult!!.id}")
}
for (snapshot in querySnapshot) {
val note = snapshot.toObject(Note::class.java)
note.id = snapshot.id
val title = note.title
val desc = note.description
val priority = note.priority
data += "${note.id} \nTitle =$title \nDescription = $desc\nPriority : $priority\n\n"
}
if (querySnapshot.size() > 0) {
data += "---------------\n\n"
textView_loadData.append(data)
lastResult = querySnapshot.documents[querySnapshot.size() - 1].reference
Log.i(TAG, lastResult!!.id)
}
}
}
下面给出的是首次点击的logcatI/FireStoreExample: before get
I/FireStoreExample: querySnapshot Size : 3
I/FireStoreExample: P9hIw4Ai7w4IHP6H3ew3
下面是第二次点击的日志I/FireStoreExample: Start P9hIw4Ai7w4IHP6H3ew3
I/FireStoreExample: before get
I/FireStoreExample: querySnapshot Size : 0
I/FireStoreExample: querySnapshot ID : P9hIw4Ai7w4IHP6H3ew3
请帮我找出问题所在。谢谢
最佳答案
由于对使用 startAt 和 startAfter 方法的查询分页的语义有误解,第二个查询结果为空。
假设Notebook集合包含N个文档。当您进行第一个查询时,您会要求按优先级字段排序的前三个文档,因此该查询将返回文档1..3。然后,在第二次单击时,您期望查询返回下一个3个结果,因此实际上您期望的是文档4..6。这里的关键点是 startAt 和 start After都是根据有序字段的值而不是最后检索的文档进行分页。总体而言,startAt和startAfter的语义大致如下。
orderby(X).startAt(Y)=>返回X字段大于或等于Y的文档
orderby(X).startAfter(Y)=>返回X字段严格大于Y的文档
考虑到这一点,让我们检查一下执行第二个查询时代码的实际作用:
// At the end of the first query...
lastResult = querySnapshot.documents[querySnapshot.size() - 1].reference
// Second query
query = colRef.orderBy("priority")
.startAfter(lastResult)
.limit(3)
在上面的代码中,您要请求“优先级”字段大于文档参考“P9hIw4Ai7w4IHP6H3ew3”的文档,实际上没有大于该文档的文档,因此结果集为空。这都是api reference。还有另一件事要注意。由于这些方法会根据字段值进行过滤,因此光标的位置可能不明确。例如,如果您有4个优先级为3的文档,并且在设置
startAfter(3)
时已经检索了前三个文档,那么您将丢失一个文档。同样,如果要创建startAt(3)
,您将取回相同的三个文档。 documentation中也指出了这一点。总而言之,您可以通过两种选择使此工作按预期进行:在另一个字段中添加另一个orderby,以便文档可以由组合唯一标识,从而防止任何游标歧义,并且可以保证在使用 startAfter 之后使用。下一个代码片段基于doc示例和您的代码。
// first query
query = colRef.orderBy("priority")
.orderBy("AnotherField")
.limit(3)
// Save last document
lastResult = querySnapshot.documents[querySnapshot.size() - 1]
// Second and next queries
query = colRef.orderBy("priority")
.orderBy("AnotherField")
.startAfter(lastResult)
.limit(3)
最后,请记住,查询所有文档(如果文档数量不多)可能会更简单,然后将优化推迟到性能成为问题为止。