Android Firestore 查询分页 FirebaseFirestoreException: INVALID_ARGUMENT

Android Firestore query paging FirebaseFirestoreException: INVALID_ARGUMENT

我有一个函数可以从我的 firestore 数据库中获取帖子,我正在按照文档按最后一个索引文档对查询进行分页,但是由于某些原因在第一次获取之后尝试 运行 我收到的下一个查询com.google.firebase.firestore.FirebaseFirestoreException: INVALID_ARGUMENT: Order by clause cannot contain duplicate fields uploadTimeStamp

我也登录并看到查询似乎没问题,lastVisible 实际上是 运行 第一次查询后文档列表中的索引 4,但不知何故在第二个查询中我首先收到所有又发了 5 个帖子..

知道我做错了什么吗?


suspend fun getMyPosts(userID: String): Flow<MutableList<DocumentSnapshot>> = flow {
        Log.d(TAG, "getMyPosts() called -> userID is $userID")
        var data: QuerySnapshot? = null
        try {
            data = dbRef.collection("posts")
                .whereEqualTo("userID", userID)
                .orderBy("uploadTimeStamp", Query.Direction.DESCENDING)
                .limit(5)
                .get()
                .await()
            Log.d(TAG, "emitting ${data.documents.size} posts: \n")
            data.documents.forEach { doc ->
                Log.d(
                    TAG,
                    "post number ${data.documents.indexOf(doc)}, timestamp: ${doc.get("uploadTimeStamp")}"
                )
            }

            emit(data.documents)
            while (!data!!.isEmpty && data.size() == 5) {
                Log.d(TAG, "getting more posts")
                val lastVisible = data.documents[data.size() - 1]
                Log.d(TAG, "last index is ${data.size() - 1}")
                Log.d(TAG, "lastVisible is ${lastVisible.get("uploadTimeStamp")}")
                data = data.query
                    .whereEqualTo("userID", userID)
                    .orderBy("uploadTimeStamp", Query.Direction.DESCENDING)
                    .startAfter(lastVisible)
                    .limit(5)
                    .get()
                    .await()
                if (!data.isEmpty) {
                    Log.d(TAG, "emitting ${data.documents}")
                    emit(data.documents)
                }
            }
        } catch (e: Exception) {
            Log.d(TAG, "Failed to get MyPosts,\n $e")
            data!!.documents.forEach { doc ->
                Log.d(
                    TAG,
                    "post number ${data.documents.indexOf(doc)}, timestamp: ${doc.get("uploadTimeStamp")}"
                )
            }
        }
    }

这是我 运行 getMyPosts() 时的 logcat 所以你可以看到我以某种方式再次获得了前 5 个文档:


2021-06-03 12:31:33.349 4990-5249/avedot.app D/AppRepository: getMyPosts() called -> userID is ncTe77npPHPJyQvJJdRUAY1fEBB3
2021-06-03 12:31:34.048 4990-5249/avedot.app D/AppRepository: emitting 5 posts: 
2021-06-03 12:31:34.048 4990-5249/avedot.app D/AppRepository: post number 0, timestamp: 1622708658810
2021-06-03 12:31:34.048 4990-5249/avedot.app D/AppRepository: post number 1, timestamp: 1622708316148
2021-06-03 12:31:34.049 4990-5249/avedot.app D/AppRepository: post number 2, timestamp: 1622665878322
2021-06-03 12:31:34.049 4990-5249/avedot.app D/AppRepository: post number 3, timestamp: 1622665769466
2021-06-03 12:31:34.049 4990-5249/avedot.app D/AppRepository: post number 4, timestamp: 1622661053130
2021-06-03 12:31:34.082 4990-5249/avedot.app D/AppRepository: getImages() called
2021-06-03 12:31:34.083 4990-5249/avedot.app D/AppRepository: [false, false, true]
2021-06-03 12:31:35.345 4990-5249/avedot.app D/AppRepository: [false, true, true]
2021-06-03 12:31:36.068 4990-5249/avedot.app D/AppRepository: [true, true, true]
2021-06-03 12:31:37.084 4990-5249/avedot.app D/AppRepository: [false, false, true]
2021-06-03 12:31:37.294 4990-5249/avedot.app D/AppRepository: [false, false, true]
2021-06-03 12:31:37.500 4990-5249/avedot.app D/AppRepository: getting more posts
2021-06-03 12:31:37.500 4990-5249/avedot.app D/AppRepository: last index is 4
2021-06-03 12:31:37.501 4990-5249/avedot.app D/AppRepository: lastVisible is 1622661053130
2021-06-03 12:31:37.666 4990-5249/avedot.app D/AppRepository: Failed to get MyPosts,
     com.google.firebase.firestore.FirebaseFirestoreException: INVALID_ARGUMENT: Order by clause cannot contain duplicate fields uploadTimeStamp
2021-06-03 12:31:37.666 4990-5249/avedot.app D/AppRepository: post number 0, timestamp: 1622708658810
2021-06-03 12:31:37.666 4990-5249/avedot.app D/AppRepository: post number 1, timestamp: 1622708316148
2021-06-03 12:31:37.667 4990-5249/avedot.app D/AppRepository: post number 2, timestamp: 1622665878322
2021-06-03 12:31:37.667 4990-5249/avedot.app D/AppRepository: post number 3, timestamp: 1622665769466
2021-06-03 12:31:37.667 4990-5249/avedot.app D/AppRepository: post number 4, timestamp: 1622661053130

这是我的 firestore 文档结构

如果我们稍微简化您的代码,您正在做:

var data: QuerySnapshot? = null
try {
    data = dbRef.collection("posts")
        .whereEqualTo("userID", userID)
        .orderBy("uploadTimeStamp", Query.Direction.DESCENDING)
        .limit(5)
        .get()
        .await()
    ...
    while (!data!!.isEmpty && data.size() == 5) {
        val lastVisible = data.documents[data.size() - 1]
        data = data.query
            .whereEqualTo("userID", userID)
            .orderBy("uploadTimeStamp", Query.Direction.DESCENDING)
            .startAfter(lastVisible)
            .limit(5)
            .get()
            .await()

因此,您通过在其上再次调用 whereEqualToorderBy ,从其原始值构建 data,这是错误消息抱怨什么。

最简单的解决方法是不重复使用数据的现有值,因此将 data = data.query 更改为 data = dbRef.collection("posts")

但您似乎在尝试构建器模式,这实际上是一种更好的方法。在这种情况下,您需要确保您没有重建整个查询,而只是添加与分页部分相关的子句:

data = data.query
    .startAfter(lastVisible)
    .get()
    .await()

所以这里我们只是添加 startAfter 子句,因为所有其他条件在您首次构建它时已经存在于 data.query 中。