首页 > 解决方案 > 函数返回未定义的、预期的 Promise 或值,并且无法使用云函数从 Firebase 数据库中删除旧数据

问题描述

我正在尝试删除数据库上超过 12 小时的多个节点。我正在使用 pub/sub 函数来触发此事件。我不知道我的代码是否实际上循环遍历所有节点,因为我没有使用特定的onWrite数据库onCreate触发器。这是数据库的图像示例

在此处输入图像描述

这是发布/订阅代码

 exports.deletejob = functions.pubsub.topic('Oldtask').onPublish(() => {

           deleteOldItem();
})

和 deleteOldItem 函数

function deleteOldItem(){
const CUT_OFF_TIME =  12 * 60 * 1000; // 12 Hours in milliseconds.
//var ref = admin.database().ref(`/articles/${id}`);
const ref = admin.database().ref(`/articles`);
 const updates = {};
ref.orderByChild('id').limitToLast(100).on('value', function (response) {
    var index = 0;

   response.forEach(function (child) {
    var element = child.val();

     const datetime = element.timestamp;

         const now = Date.now();

         const cutoff = now - datetime;

if (CUT_OFF_TIME < cutoff){

     updates[element.key] = null;
}

  });
//This is supposed to be the returened promise
 return ref.child(response.key).update(updates);

});

如果我做错了什么,我很想知道。发布/订阅是由已在谷歌云调度程序上设置的 JobScheduler 触发的

标签: javascriptfirebase-realtime-databasegoogle-cloud-platformgoogle-cloud-functionsgoogle-cloud-scheduler

解决方案


您的代码中有几个问题给您带来了麻烦。

  • 对承诺的处理不正确。特别是,您的顶级函数实际上从未返回过承诺,它只是调用了deleteOldItems().
  • 您应该使用 promise 形式once()而不是on()使用回调调用,因为在这种情况下您不想安装侦听器,您只需要一次结果,并且希望将其作为 promise 链的一部分进行处理。
  • 要删除节点,您应该调用remove()对该节点的引用。它还会生成一个承诺供您在此处使用。
  • 您没有正确计算以毫秒为单位的 12 小时,您以毫秒为单位计算了 12 分钟 :)

这就是我想出的。它使用 http 函数而不是 pubsub 函数以及为我的测试添加日志语句,但是您需要的修改应该是微不足道/显而易见的(只需更改原型并删除之后的响应deleteOldItems,但请确保您继续返回)的结果deleteOldItems()

const functions = require('firebase-functions');
const admin = require('firebase-admin');

function deleteOldItems() {
  const CUT_OFF_TIME =  12 * 60 * 60 * 1000; // 12 Hours in milliseconds.
  const ref = admin.database().ref('/articles');
  return ref.orderByChild('id').limitToLast(100).once('value')
    .then((response) => {
      const updatePromises = [];
      const now = Date.now();

      response.forEach((child) => {
        const datetime = child.val().timestamp;
        const cutoff = now - datetime;

        console.log(`processing ${datetime} my cutoff is ${CUT_OFF_TIME} and ${cutoff}`);

        if (CUT_OFF_TIME < cutoff){
          updatePromises.push(child.ref.remove())
        }
      });

      return Promise.all(updatePromises);
    });
}

exports.doIt = functions.https.onRequest((request, response) => {
    return deleteOldItems().then(() => { return response.send('ok') });
}

虽然我尚未对其进行测试,但我很确定这将包含在您对云调度程序的原始函数调用中:

exports.deletejob = functions.pubsub.topic('Oldtask').onPublish(() => {
    return deleteOldItems();
})

当然,这仍然比您需要的复杂,因为 order byid在这里并没有真正为您带来任何好处。相反,为什么不只使用查询返回截止时间之前最早的项目(例如,正是您要删除的项目)?我也切换到limitToFirst确保最早的条目被丢弃,这看起来更自然并确保公平:

function deleteOldItems() {
  const cutOffTime =  Date.now() - (12 * 60 * 60 * 1000); // 12 Hours earlier in milliseconds.
  const ref = admin.database().ref('/articles');
  return ref.orderByChild('timestamp').endAt(cutOffTime).limitToFirst(100).once('value')
    .then((response) => {
      const updatePromises = [];

      response.forEach((child) => {
          updatePromises.push(child.ref.remove())
      });

      return Promise.all(updatePromises);
    });
}

如果您对多个项目执行此操作,当然,您可能希望在时间戳字段上添加索引,以便范围查询更有效。


推荐阅读