在Firebase中查找集合长度

时间:2018-11-14 10:37:24

标签: javascript node.js firebase firebase-realtime-database

我的Firebase实时数据库中有超过2万个对象。现在,我需要取出所有这些对象并对它们进行处理。问题是服务器每次执行时都会用尽内存。这是我当前的代码:

sendEmail.get('/:types/:message', cors(), async (req, res, next) => {
    console.log(5);
    const types = JSON.parse(req.params.types);
    console.log('types', types);
    let recipients = [];
    let mails = [];
    if (types.includes('students')) {
        console.log(1);
        const tmpUsers = await admin.database().ref('Users').orderByChild('student').equalTo(true).once('value').then(r => r.val()).catch(e => console.log(e));
        recipients = recipients.concat(tmpUsers);
    }
    if (types.includes('solvers')) {
        console.log(2);
        let tmpUsers = await admin.database().ref('Users').orderByChild('userType').equalTo('person').once('value').then(r => r.val()).catch(e => console.log(e));
        tmpUsers = tmpUsers.concat(arrayFromObject(await admin.database().ref('Users').orderByChild('userType').equalTo('company').once('value').then(r => r.val()).catch(e => console.log(e))));
        recipients = recipients.concat(tmpUsers);
    }
});

所以我有两个选择。通过startAtendAt流式传输或限制响应。但是要限制响应,我需要知道我到底有多少个对象。为此,我需要下载整个收藏集...您现在看到了我的问题。如何在不下载整个文档集的情况下了解我拥有多少文档?

2 个答案:

答案 0 :(得分:2)

您可以尝试结合使用limitToFirst/limitToLaststartAt / endAt来分页查询。

例如,您可以使用limitToFirst(1000)执行第一个查询,然后从此返回的列表中获取最后一个键,并将其与startAt(key)和另一个limitToFirst(1000)一起使用,重复进行直到您到达集合的结尾。

在node.js中,它可能看起来像这样( unested代码):

let recipients = [];

let tmpUsers = next();
recipients = filter(recipients, tmpUsers);

// startAt is inclusive, so when this reaches the last result there will only be 1
while (tmpUsers.length>1) {
    let lastKey = tmpUsers.slice(-1).pop().key;
    tmpUsers = next(lastKey);
    if (tmpUsers.length>1) { // Avoid duplicating last result
        recipients = filter(recipients, tmpUsers);
    }
}

async function next(startAt) {
    if (!startAt) {
        return await admin.database().ref('Users')
                .orderByKey()
                .limitToFirst(1000)
                .once('value').then(r => r.val()).catch(e => console.log(e));
    } else {
        return await admin.database().ref('Users')
                .orderByKey()
                .startAt(startAt)
                .limitToFirst(1000)
                .once('value').then(r => r.val()).catch(e => console.log(e));
    }
}

function filter(array1, array2) {
    // TODO: Filter the results here as we can't combine orderByChild/orderByKey
    return array1.concat(array2);
}

问题是您将无法使用数据库端过滤,因此您需要手动过滤结果,这可能会使情况变得更糟,具体取决于您需要保留多少项。一次recipients个变量。

另一种选择是分批处理(例如1000个),将它们从recipients数组中弹出以释放资源,然后移至下一个批处理。它确实完全取决于您需要对这些对象执行哪些操作,并且您需要权衡一次是否真的有必要一次性处理(并将其保存在内存中)整个结果集。

答案 1 :(得分:2)

您无需知道集合的大小即可分批处理它们。

您可以通过按键将它们排序(限制在1000个左右)来进行操作,然后在下一个批次中启动第一个批次的最后一个密钥。

如果您仍然想知道如何获取集合的大小,唯一的好方法是在单独的节点上维护集合的大小,并在更新集合时保持更新。