Firebase Realtime DB:按键值的数量对查询结果进行排序

时间:2020-02-03 11:43:57

标签: firebase firebase-realtime-database

我有一个带有用户的Firebase Web实时数据库,每个用户都有一个jobs属性,其值是一个对象:

{
  userid1:
    jobs:
      guid1: {},
      guid2: {},
  userid2:
    jobs:
      guid1: {},
      guid2: {},
}

我想查询以获取最多工作的n个用户。是否可以使用给定用户在其jobs属性中拥有的值数来对用户进行排序的排序技巧?

我特别不想存储每个用户拥有的作业数量的整数,因为我需要更新用户的jobs属性,作为原子更新的一部分,原子更新同时并原子地更新其他用户属性,而且我不认为交易(例如递增/递减计数器)可以成为这些原子交易的一部分。

这是我正在执行的原子更新的示例。注意当我运行以下更新时,我没有正在内存中修改的用户:

firebase.database().ref('/').update({
  [`/users/${user.guid}/pizza`]: true,
  [`/users/${user.guid}/jobs/${job.guid}/scheduled`]: true,
})

任何有关可用于此数据的模式的建议,将不胜感激!

1 个答案:

答案 0 :(得分:2)

实时数据库事务运行在JSON树中的单个节点上,因此很难将原子更新中的jobCounter节点更新集成到多个节点(即/users/${user.guid}/pizza/users/${user.guid}/jobs/${job.guid}/scheduled)。我们将需要在/users/${user.guid}级进行更新并计算计数器值,等等。

一种更简单的方法是,每当jobCounter节点之一发生变化(这意味着计数器发生变化)时,都可以使用Cloud Function更新用户的jobs节点。换句话说,如果添加或删除了新的作业节点,则更新计数器。如果仅修改现有节点,则不会更新计数器,因为作业数量没有变化。

exports.updateJobsCounter = functions.database.ref('/users/{userId}/jobs')
    .onWrite((change, context) => {

        if (!change.after.exists()) {

            //This is the case when no more jobs exist for this user

            const userJobsCounterRef = change.before.ref.parent.child('jobsCounter');
            return userJobsCounterRef.transaction(() => {
                return 0;
            });

        } else {

            if (!change.before.val()) {

                //This is the case when the first job is created

                const userJobsCounterRef = change.before.ref.parent.child('jobsCounter');
                return userJobsCounterRef.transaction(() => {
                    return 1;
                });

            } else {

                const valObjBefore = change.before.val();
                const valObjAfter = change.after.val();

                const nbrJobsBefore = Object.keys(valObjBefore).length;
                const nbrJobsAfter = Object.keys(valObjAfter).length;

                if (nbrJobsBefore !== nbrJobsAfter) {
                    //We update the jobsCounter node
                    const userJobsCounterRef = change.after.ref.parent.child('jobsCounter');
                    return userJobsCounterRef.transaction(() => {
                        return nbrJobsAfter;
                    });
                } else {
                    //No need to update the jobsCounter node
                    return null;
                }

            }

        }

    });