我需要定期从Firebase中删除旧数据。我找到了这个解决方案,但一直未能使其发挥作用。
终端告诉我functions[deleteOldItems(us-central1)]: Successful update operation.
,但数据没有被删除。
这就是我所做的:
const functions = require('firebase-functions');
exports.deleteOldItems = functions.database.ref('/nodeThatContainsDataToBeDeleted')
.onWrite((change, context) => {
var ref = change.after.ref.parent; // reference to the items
var now = Date.now();
var cutoff = now - 2 * 60 * 60 * 1000;
var oldItemsQuery = ref.orderByChild('timestamp').endAt(cutoff);
return oldItemsQuery.once('value', function(snapshot) {
// create a map with all children that need to be removed
var updates = {};
snapshot.forEach(function(child) {
updates[child.key] = null
});
// execute all updates in one go and return the result to end the function
return ref.update(updates);
});
});
timestamp
是639915248.124176
(来自Swift(
数据库结构:
root : {
"nodeThatContainsDataToBeDeleted" : {
"-r5tyfX9FGC0glhgf78Ia" : {
"company" : "CompanyCo",
"name" : "Sam",
"phone" : "1212",
"imageURL" : "https://firebasestorage.googleapis.com/imageurl",
"timestamp" : 6.39915248124176E8
}
},
}
当您在后台触发的云函数(在您的情况下是once()
和update()
方法(中执行异步操作时,您必须返回一个Promise,这样云函数就会等待这个Promise解析以终止。更多信息在三个官方视频标题";学习JavaScript承诺"(第2部分和第3部分特别关注背景触发的云功能,但之前确实值得看第1部分(。
通过执行oldItemsQuery.once('value', function(snapshot) {...})
,您实际上使用了once()
方法的回调版本。
为了正确地链接不同的promise并在回调中返回此链,您需要使用promise版本,如下所示:
exports.deleteOldItems = functions.database.ref('/nodeThatContainsDataToBeDeleted')
.onWrite((change, context) => {
var ref = change.after.ref.parent; // reference to the items
var now = Date.now();
var cutoff = now - 2 * 60 * 60 * 1000;
var oldItemsQuery = ref.orderByChild('timestamp').endAt(cutoff);
return oldItemsQuery.once('value')
.then(snapshot => {
// create a map with all children that need to be removed
var updates = {};
snapshot.forEach(function (child) {
updates[child.key] = null
});
// execute all updates in one go and return the result to end the function
return ref.update(updates);
});
});
这个答案建立在@RenaudTarnec的答案之上。
自2015年以来,JavaScript和Firebase SDK已经取得了长足的进步,因此在复制/使用StackOverflow上的旧代码示例时要小心这一点。您链接的解决方案还链接到functions-samples
repo,其中的示例代码一直保持最新的现代功能。
正如Renaud所述,您需要切换到只使用Promise
版本的once()
侦听器,而不是使用回调。虽然您的代码确实按预期返回了Promise
,但当前回调没有正确插入到Promise
链中,这意味着您的云函数没有等待回调中的代码完成。链接的例子是在2015年写的,这曾经是有效的,但现在已经不是这样了。
这些线路:
return oldItemsQuery.once('value', function(snapshot) {
/* ... use snapshot ... */
});
应该是:
return oldItemsQuery.once('value')
.then((snapshot) => {
/* ... use snapshot ... */
});
或:
const snapshot = await oldItemsQuery.once('value');
/* ... use snapshot ... */
接下来,您已经将onWrite
侦听器配置为侦听/nodeThatContainsDataToBeDeleted
上的更改。虽然这是有效的,但效率非常低。在/nodeThatContainsDataToBeDeleted
(如/nodeThatContainsDataToBeDeleted/somePushId/someBoolean = true
(下的某个时刻,对于数据库中的每一个小更改,函数都会下载该节点下嵌套的所有数据。对于这个节点上的几个条目,这是无关紧要的,但当您接近数千个条目时,您可能会读取许多兆字节的数据。
如果查看您链接的示例,侦听器将附加到/path/to/items/{pushId}
(单个写入条目(,而不是/path/to/items
(所有条目(。{pushId}
过去是一个命名的通配符,它捕获路径的最后一部分(如-r5tyfX9FGC0glhgf78Ia
(。
此行:
functions.database.ref('/nodeThatContainsDataToBeDeleted') // fetches all entries
应该是:
functions.database.ref('/nodeThatContainsDataToBeDeleted/{pushId}') // fetches only the entry that changed
注意:一个"bug";这个函数的作用是重新触发自己。对于它删除的每个条目,deleteOldItems
将再次被激发。您可能希望使用.onCreate()
或.onUpdate()
而不是.onWrite()
-有关更多信息,请参阅文档。
结合这些变化给出(基于撰写本文时的最新示例代码(:
// Copyright 2017 Google Inc., Apache 2.0
// Sourced at https://github.com/firebase/functions-samples/blob/master/delete-old-child-nodes/functions/index.js
// Cut off time. Child nodes older than this will be deleted.
const CUT_OFF_TIME = 2 * 60 * 60 * 1000; // 2 Hours in milliseconds.
/**
* This database triggered function will check for child nodes that are older than the
* cut-off time. Each child needs to have a `timestamp` attribute.
*/
exports.deleteOldItems = functions.database.ref('/nodeThatContainsDataToBeDeleted/{pushId}').onWrite(async (change) => {
const ref = change.after.ref.parent; // reference to the parent
const now = Date.now();
const cutoff = now - CUT_OFF_TIME;
const oldItemsQuery = ref.orderByChild('timestamp').endAt(cutoff);
const snapshot = await oldItemsQuery.once('value');
// create a map with all children that need to be removed
const updates = {};
snapshot.forEach(child => {
updates[child.key] = null;
});
// execute all updates in one go and return the result to end the function
return ref.update(updates);
});