Javascript 在firebase中查找集合长度

Javascript 在firebase中查找集合长度,javascript,node.js,firebase,firebase-realtime-database,Javascript,Node.js,Firebase,Firebase Realtime Database,我的Firebase实时数据库中有超过20000个对象。我现在需要取出所有这些物体,对它们进行处理。问题是每次我这样做时,服务器都会耗尽内存。这是我当前的代码: sendEmail.get('/:types/:message', cors(), async (req, res, next) => { console.log(5); const types = JSON.parse(req.params.types); console.log('types', typ

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

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);
    }
});

所以我有两个选择。使用
startAt
endAt
流式传输或限制响应。但是为了限制反应,我需要知道我到底有多少个对象。要做到这一点,我需要下载整个收藏。。。你现在明白我的问题了。如果不下载整个集合,我如何知道我有多少文档?

您可以尝试通过组合和
startAt
/
endAt
对查询分页

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

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

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


另一种选择是分批处理它们(例如1000个),从
收件人
数组中弹出它们以释放资源,然后移动到下一批。它完全取决于您需要对对象执行什么操作,并且您需要权衡是否确实需要一次性处理(并保存在内存中)整个结果集。

您可以尝试通过组合和
startAt
/
endAt
对查询分页

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

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

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


另一种选择是分批处理它们(例如1000个),从
收件人
数组中弹出它们以释放资源,然后移动到下一批。它完全取决于您需要对对象执行什么操作,并且您需要权衡是否确实需要一次性处理(并保留在内存中)整个结果集。

您不需要知道集合的大小就可以批量处理它们

您可以通过按键对它们进行排序,限制为1000左右,然后在下一批开始第一批的最后一个键


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

您不需要知道集合的大小就可以批量处理它们

您可以通过按键对它们进行排序,限制为1000左右,然后在下一批开始第一批的最后一个键


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

诀窍是与
startAt
/
endAt
结合使用。例如,您可以使用
limitToFirst(100)
执行第一个查询,然后从返回的列表中获取最后一个键,并将其与:
startAt(key)
和另一个
limitToFirst(100)
一起使用。在讨论这一点的文档中有一个简单的例子。我会看看我是否能画一个node.js的例子。我希望能有一个例子。我想我明白你的意思了,但我不确定我是否完全理解,我会试着做一个。再仔细考虑一下:如果是云函数在处理大型数据集时超时,你可以改为云函数,我已经试过了。2GB是不够的。我自己也很震惊,因为我已经写了一个例子,所以我不确定分页是否会有帮助。无论如何,我会发布我的答案,但您可能必须将数据处理拆分为多个函数调用。诀窍是与
startAt
/
endAt
结合使用。例如,您可以使用
limitToFirst(100)
执行第一个查询,然后从返回的列表中获取最后一个键,并将其与:
startAt(key)
和另一个
limitToFirst(100)
一起使用。在讨论这一点的文档中有一个简单的例子。我会看看我是否能画一个node.js的例子。我希望能有一个例子。我想我明白你的意思了,但我不确定我是否完全理解,我会试着做一个。再仔细考虑一下:如果是云函数在处理大型数据集时超时,你可以改为云函数,我已经试过了。2GB是不够的。我自己也很震惊,因为我已经写了一个例子,所以我不确定分页是否会有帮助。无论如何,我会发布我的答案,但您可能必须将数据处理拆分为多个函数调用。它将一直运行,直到数据库只返回1个或更少的结果(
while(tmpUsers.length>1)
),这意味着它已到达列表的末尾。这是因为它是包容性的,所以返回1个结果意味着它已经结束。至少,如果我的代码有效的话,这就是我的想法!哎哟。这是有道理的。我来看看。很好的回答@Grimthorr!它将一直运行,直到数据库只返回1个或更少的结果(
while(tmpUsers.len
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);
}