Почему сборка "единый объемный" в Cloud Firestore невозможна, как в базе данных Realtime?

С базой данных в реальном времени Firebase мы можем удалить огромный список элементов одной командой, просто вызвав remove() на родительский узел (узел удален, а все тоже дети).

Но согласно документации с Firestore (https://firebase.google.com/docs/firestore/manage-data/delete-data#collections):
чтобы удалить коллекцию, мы должны закодировать пакет, который будет перебирать все его документы и удалять их один за другим.

Это неэффективно. Это потому, что Firestore находится в бета-версии или структурно невозможно удалить полный узел (Collection) за один звонок?

Ответ 1

RTDB может это сделать, потому что каждая база данных является локальной для одного региона. Чтобы обеспечить сериализованное представление, когда вы вызываете remove(), база данных останавливает все остальные работы до тех пор, пока удаление не будет завершено.

Такое поведение было причиной нескольких очевидных отключений: если вызов remove() должен удалить огромные полосы данных, вся другая активность эффективно блокируется до тех пор, пока она не завершится. В результате даже для пользователей RTDB, которые хотят удалить большое количество данных, мы рекомендуем рекурсивно находить и удалять документы в группах.

Firestore, с другой стороны, основано на более традиционной инфраструктуре хранения в стиле Google, где разные диапазоны ключей назначаются динамически на разные серверы (хранение на самом деле не поддерживается BigTable, но применяются те же принципы). Это означает, что удаление данных больше не обязательно является одним действием в области, и становится очень дорого, чтобы сделать делегирование транзакционным. В настоящее время транзакции Firestore ограничены до 100 участников, и это означает, что любое нетривиальное делегирование транзакционной суммы невозможно.

Мы изучаем, как лучше всего использовать API, который делает массовое удаление без перспективного транзакционного поведения. Просто представить, как это сделать с мобильным клиентом, но, как вы заметили, это было бы неэффективно, если бы все, что мы сделали, было встроено в цикл и пакетное удаление для вас. Мы также не хотим, чтобы клиенты REST были гражданами второго сорта.

Firestore - это новый продукт, и есть еще много дел. К сожалению, это не привело к сокращению. Хотя это то, на что мы надеемся обратиться в конечном итоге, я не могу предоставить никаких временных рамок, когда это будет.

Тем временем консоль и командная строка firebase предоставляют не транзакционные средства для этого, например, для автоматизации тестирования.

Спасибо за понимание и спасибо за попытку Firestore!

Ответ 2

Я с удовольствием реорганизовал мое приложение для Firestore из базы данных Realtime, наслаждаясь более коротким кодом и более простым синтаксисом, пока не переработал функции delete()! Чтобы удалить документ с субколлекциями:

  • Создайте массив обещаний.
  • get() подколлекция, которая не имеет последующих подкомпонов.
  • Перейдите через forEach() чтобы прочитать каждый документ в подмножестве.
  • Удалите каждый документ и нажмите команду delete в массив обещаний.
  • Перейдите к следующему подмножеству и повторите это.
  • Используйте 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);
});
};

Все это было бы одной строкой в базе данных Realtime.

Ответ 3

Это самый быстрый способ удалить все документы в коллекции: сочетание между циклами удаления python delete и пакетным методом python

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.

Вы можете получить более сложные и обрабатывать также ошибки API, но это просто отлично работает для меня.