在Cloud Firestore中,为什么不能单个批量生产"删除集合(可以使用实时数据库完成)?

时间:2017-10-11 16:05:09

标签: firebase google-cloud-firestore

使用Firebase实时数据库,我们只需在父节点上调用remove ()就可以删除一个包含单个命令的项目列表(该节点已被删除,所有节点也都是子节点)。

但根据Firestore(https://firebase.google.com/docs/firestore/manage-data/delete-data#collections)的文档:
为了删除一个集合,我们必须编写一个批处理来循环遍历所有文档并逐个删除它们。

这根本没有效率。是因为Firestore是测试版还是在一次调用中删除整个节点(Collection)在结构上是不可能的?

3 个答案:

答案 0 :(得分:22)

RTDB能够做到这一点,因为每个数据库都是单个区域的本地数据库。为了提供序列化视图,当您调用remove()时,数据库会停止所有其他工作,直到删除完成。

这种行为导致了几次明显的中断:如果remove()调用必须删除大量数据,则所有其他活动在完成之前都会被有效锁定。因此,即使对于想要删除大量数据的RTDB用户,我们也建议以组(CLInode.js)递归查找和删除文档。

另一方面,Firestore基于更传统的Google风格的存储基础架构,其中分配了不同的密钥范围dynamically to different servers(存储实际上不受BigTable支持,但适用相同的原则)。这意味着删除数据不再是单个区域操作,并且有效地使删除显示为事务性变得非常昂贵。 Firestore交易目前仅限于100名参与者,这意味着任何非平凡的交​​易批量删除都是不可能的。

我们正在调查如何最好地展示执行批量删除的API,而不会采取有希望的事务行为。可以直接想象如何从移动客户端执行此操作,但正如您所看到的,如果我们所做的只是为您嵌入循环和批量删除,这将不会有效。我们也不想让REST客户成为二等公民。

Firestore是一种新产品,还有很多事情要做。不幸的是,这还没有削减。虽然这是我们希望最终解决的问题,但我无法提供任何时间表。

与此同时,控制台和firebase command-line都提供了一种非交易方式,例如:用于测试自动化。

感谢您对Firestore的理解和感谢!

答案 1 :(得分:6)

我很高兴从Realtime Database重构我的Firestore应用程序,享受更短的代码和更简单的语法,直到我重构了delete()函数!要删除带有子集的文档:

  • 创建一系列承诺。
  • get()一个子集合,没有进一步的子集合。
  • 通过forEach()函数迭代读取子集合中的每个文档。
  • 删除每个文档,然后将删除命令推送到promises数组中。
  • 继续下一个子集并重复此操作。
  • 使用Promise.all(arrayOfPromises)等待所有子集合都被删除。
  • 然后删除顶级文档。

对于多层集合和文档,您需要将其作为一个函数,然后从另一个函数调用它以获得下一个更高层等。

您可以在控制台中看到这一点。要手动删除集合和文档,请删除最右侧的文档,然后删除最右侧的集合,依此类推。

这是我在AngularJS中的代码。仅当在子集合之前未删除顶级集合时,它才有效。

$scope.deleteClip = function(docId) {
if (docId === undefined) {
docId = $scope.movieOrTvShow + '_' + $scope.clipInMovieModel;
}
$scope.languageVideos = longLanguageFactory.toController($scope.language) + 'Videos';
var promises = [];
firebase.firestore().collection($scope.languageVideos).doc($scope.movieOrTvShow).collection('Video Clips').doc(docId).collection('SentenceTranslations').get()
.then(function(translations) {
  translations.forEach(function(doc) {
    console.log(doc.id);
    promises.push(firebase.firestore().collection($scope.languageVideos).doc($scope.movieOrTvShow).collection('Video Clips').doc(docId).collection('SentenceTranslations').doc(doc.id).delete());
  });
});
firebase.firestore().collection($scope.languageVideos).doc($scope.movieOrTvShow).collection('Video Clips').doc(docId).collection('SentenceExplanations').get()
.then(function(explanations) {
  explanations.forEach(function(doc) {
    console.log(doc.id);
    promises.push(firebase.firestore().collection($scope.languageVideos).doc($scope.movieOrTvShow).collection('Video Clips').doc(docId).collection('SentenceExplanations').doc(doc.id).delete());
  });
});
Promise.all(promises).then(function() {
  console.log("All subcollections deleted.");
  firebase.firestore().collection($scope.languageVideos).doc($scope.movieOrTvShow).collection('Video Clips').doc(docId).delete()
  .then(function() {
    console.log("Collection deleted.");
    $scope.clipInMovieModel = null;
    $scope.$apply();
  })
  .catch(function(error) {
    console.log("Remove failed: " + error.message);
  });
})
.catch(function(error){
  console.log("Error deleting subcollections: " + error);
});
};

所有这些都是实时数据库中的一行。

答案 2 :(得分:4)

这是删除集合中所有文档的最快方法: 在python delete collection looppython batch method

之间混合
def delete_collection(coll_ref, batch_size, counter):
    batch = db.batch()
    init_counter=counter
    docs = coll_ref.limit(500).get()
    deleted = 0

    for doc in docs:
        batch.delete(doc.reference)
        deleted = deleted + 1

    if deleted >= batch_size:
        new_counter= init_counter + deleted
        batch.commit()
        print("potentially deleted: " + str(new_counter))
        return delete_collection(coll_ref, batch_size, new_counter)
    batch.commit()

delete_collection(db.collection(u'productsNew'), 500, 0)

此操作从集合“ productNew”中删除所有文档(以500块为单位),这是当前可以传递到提交的最大文档数。参见Firebase write and transaction quotas

您可以变得更复杂,还可以处理API错误,但这对我来说很好。