在Firebase实时数据库中未实现分页

3
以下是我用来实现从 Firebase 实时数据库检索数据的分页代码。基本上,我正在尝试根据页码获取前 n 个内容,然后从第一个查询中检索到的数据中获取最后 n 个内容。
function getUserSnapshotOrVerifyUserId(username, idToken, cb) {
    if (username == null || username.length == 0 || idToken == null || idToken.length == 0)
        return cb({
            status: "error",
            errorMessage: "Missing params."
        }, null);
    
    admin.auth().verifyIdToken(idToken).then(decodedToken => {
        let uid = decodedToken.uid;

        admin.database().ref().child("users").orderByChild("username").equalTo(username).once('value', snapshot => {
            if (!snapshot.exists())
                return cb({
                    status: "error",
                    message: "invalid-profile"
                });

            snapshot.forEach(child => {
                const id = child.val().id;
                if (id !== uid)
                    return cb({
                        status: "error",
                        message: "Invalid ID"
                    });

                admin.database().ref("users/" + id).once("value", snapshot => {
                    if (!snapshot.exists())
                        return cb({
                            status: "error",
                            errorMessage: "user not found."
                        });
                    
                    return cb(null, id, snapshot);
                });
            });
        });
    }).catch(err => cb({
        status: "error",
        message: err
    }));
}

exports.getUserContentTestPagination = functions.https.onRequest((req, res) => {
    corsHandler(req, res, async () => {
        try {
            const username = req.body.username || req.query.username;
            const idToken = req.body.idToken;

            const limit = 2;
            const page = req.body.page || 1;

            const limitToFirst = page * limit;
            const limitToLast = limit;


            getUserSnapshotOrVerifyUserId(username, idToken, async (err, id) => {
                if(err) return res.json(err);

                const uploadsRef = admin.database().ref('uploads').orderByChild('createdBy').equalTo(id)

                const firstnquery = uploadsRef.limitToFirst(limitToFirst);
                const lastnquery = firstnquery.limitToLast(limitToLast);

                lastnquery.once("value", snapshot => {
                    res.json({
                        snapshot
                    })
                })
                
            })
        } catch (err) {
            res.json({
                status: "error",
                message: err
            })
        }
    });
});

我调用了一个函数timeout,但是当我使用 firstnquery 获取前 n 个数据时,它会如预期地返回前 n 个数据。所以问题出在 lastnquery 上。希望得到任何帮助。

更新 1:

exports.getUserContentTestPagination = functions.https.onRequest((req, res) => {
    corsHandler(req, res, async () => {
        try {
            const username = req.body.username || req.query.username;
            const idToken = req.body.idToken;

            const limit = 2;
            const page = req.body.page || 1;
            
            let lastKnownKeyValue = null;

            getUserSnapshotOrVerifyUserId(username, idToken, async (err, id) => {
                if(err) return res.json(err);

                const uploadsRef = admin.database().ref('uploads');
                const pageQuery = uploadsRef.orderByChild('createdBy').equalTo(id).limitToFirst(limit);
                
                pageQuery.once('value', snapshot => {
                    snapshot.forEach(childSnapshot => {
                        lastKnownKeyValue = childSnapshot.key;
                    });

                    if(page === 1){
                        res.json({
                            childSnapshot
                        })
                    } else {
                        const nextQuery = uploadsRef.orderByChild('createdBy').equalTo(id).startAt(lastKnownKeyValue).limitToFirst(limit);

                        nextQuery.once("value", nextSnapshot => {
                            nextSnapshot.forEach(nextChildSnapshot => {
                                res.json({
                                    nextChildSnapshot
                                })
                            })
                        })
                    }

                });

            })
        } catch (err) {
            res.json({
                status: "error",
                message: err
            })
        }
    });
});
1个回答

1

在查询中同时使用limitToFirstlimitToLast非常不常见。事实上,我很惊讶这不会引发错误:

const firstnquery = uploadsRef.limitToFirst(limitToFirst);
const lastnquery = firstnquery.limitToLast(limitToLast);

Firebase查询基于游标。这意味着要获取下一页的数据,您必须知道上一页上的最后一项。这与大多数数据库不同,它们基于偏移量工作。Firebase不支持基于偏移量的查询,因此您需要知道createdBy的值和上一页最后一项的键。
有了这个,您可以使用以下内容获取下一页的项目:
admin.database().ref('uploads')
     .orderByChild('createdBy')
     .startAt(idOfLastItemOfPreviousPage, keyOfLastItemOfPreviousPage)
     .limitToFist(pageSize + 1)

我强烈建议查看在实时数据库上实现分页的其他问题,因为那里也有一些很好的示例和解释。


是的,我按照您在此链接上的答案进行了操作:https://dev59.com/1pPfa4cB1Zd3GeqPDG9y,并提出了一个解决方案。然而,我得到了“起始点已经被设置(由另一个调用startAt或equalTo)。”的错误提示,因为我显然同时使用了`equalTo()`和`startAt()`。我不能不使用equalTo,因为我必须使用传递给`equalTo()`的用户ID来过滤特定用户上传的内容。我最终采用了“显示更多”而非分页的解决方案。感谢您的建议。 - Cedric Hadjian
你不能在单个查询中同时使用 startAt()equalTo(),这就是为什么你在我的答案中看到我传递了两个参数给 .startAt(idOfLastItemOfPreviousPage, keyOfLastItemOfPreviousPage)。第一个参数是要开始的 createdBy 的值,第二个参数是要开始的键,以防有多个节点具有相同的 createdBy 值。 - Frank van Puffelen
我还是不理解,如果上传文件没有按createdBy的id进行排序,它怎么知道我只需要那个特定用户的上传文件?将createdBy的值传递给startAt()会如何让它知道我需要该特定用户的上传文件? - Cedric Hadjian
“idOfLastItemOfPreviousPage”是与您在代码中使用的“equalTo(id)”相同的值(这就是我从那里复制名称的原因),但用于上一页的最后一项。如果该“id”值是用户ID(在这种情况下,我建议以后使用更好的变量名),那么它将是“userIdOfLastItemOfPreviousPage”。 - Frank van Puffelen
我明白了,谢谢。我现在正在尝试实现您建议的内容。还有一个问题,如何向用户显示最新的结果?我做了一些研究,显然没有按createdBy日期降序排序的方法。也许现在有某种更新?当我实现“加载更多”时,我该如何解决这个问题? - Cedric Hadjian
好的,非常感谢你的帮助。我认为现在我已经有了足够的基础,可以继续前进了。 - Cedric Hadjian

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接