firebase - query - En Cloud Firestore, ¿por qué no es posible "eliminar de forma masiva" una colección(como se puede hacer con Realtime Database)?
firestore google (3)
¡Felizmente estaba refactorizando mi aplicación para Firestore de Realtime Database, disfrutando del código más corto y la sintaxis más simple, hasta que reformulé las funciones delete ()! Para borrar un documento con subcolecciones:
- Crear una serie de promesas.
-
get()
una subcolección, que no tiene subcolecciones adicionales. - Iterar a través de una función
forEach()
para leer cada documento en laforEach()
. - Elimine cada documento y presione el comando de eliminar en la matriz de promesas.
- Pase a la siguiente subcolección y repita esto.
- Use
Promise.all(arrayOfPromises)
para esperar hasta que se hayan eliminado todas las subcolecciones. - A continuación, elimine el documento de nivel superior.
Con múltiples capas de colecciones y documentos, querrá convertir esa función en una función, luego llamarla desde otra función para obtener la siguiente capa superior, etc.
Puedes ver esto en la consola. Para eliminar manualmente colecciones y documentos, elimine el documento que está más a la derecha, luego elimine la colección que está más a la derecha, y así sucesivamente, a la izquierda.
Aquí está mi código, en AngularJS. Solo funciona si la colección de nivel superior no se eliminó antes de las subcolecciones.
$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);
});
};
Todo eso habría sido una línea en la base de datos en tiempo real.
Con la base de datos en tiempo real de Firebase, podemos eliminar una gran lista de elementos con un solo comando simplemente llamando a remove ()
en el nodo principal (el nodo se elimina y todo es secundario también).
Pero de acuerdo con la documentación con Firestore ( https://firebase.google.com/docs/firestore/manage-data/delete-data#collections ):
Para eliminar una Colección, tenemos que codificar un lote que recorrerá todos sus documentos y los eliminará uno por uno.
Esto no es eficiente en absoluto. ¿Es porque Firestore está en versión beta o es estructuralmente imposible eliminar el nodo completo (Colección) en una sola llamada?
Esta es la forma más rápida de eliminar todos los documentos de una colección: mezcla entre el https://firebase.google.com/docs/firestore/manage-data/delete-data#collections y el método por lotes de 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)
esto elimina todos los documentos de la colección "productoNuevo" en bloques de 500, que actualmente es el número máximo de documentos que se pueden pasar a una confirmación. Ver las cuotas de escritura y transacción de Firebase .
Puede ser más sofisticado y manejar también los errores de la API, pero esto solo funciona bien para mí.
La RTDB puede hacer esto porque cada base de datos es local en una sola región. Para proporcionar una vista serializada, cuando llama a remove()
, la base de datos detiene todo otro trabajo hasta que se complete la eliminación.
Este comportamiento ha sido la causa de varias interrupciones aparentes: si una llamada remove()
tiene que eliminar grandes franjas de datos, todas las demás actividades se bloquean efectivamente hasta que se completan. Como resultado, incluso para los usuarios de RTDB que desean eliminar grandes cantidades de datos, hemos recomendado buscar y eliminar de forma recursiva los documentos en grupos ( CLI , node.js ).
Firestore, por otro lado, se basa en una infraestructura de almacenamiento más tradicional al estilo de Google, donde diferentes rangos de claves se asignan dinámicamente a diferentes servidores (el almacenamiento no está realmente respaldado por BigTable, pero se aplican los mismos principios). Esto significa que eliminar datos ya no es necesariamente una acción de una sola región y resulta muy costoso hacer que la eliminación parezca transaccional. Las transacciones de Firestore actualmente están limitadas a 100 participantes y esto significa que cualquier eliminación masiva transaccional no trivial es imposible.
Estamos investigando la mejor manera de mostrar una API que realice una eliminación masiva sin prometer un comportamiento transaccional. Es fácil imaginar cómo hacer esto desde un cliente móvil, pero como se ha observado, esto no sería eficiente si todo lo que hiciéramos fuera incrustado para usted. Tampoco queremos que los clientes de REST sean ciudadanos de segunda clase tampoco.
Firestore es un producto nuevo y hay muchas cosas por hacer. Lamentablemente esto simplemente no ha hecho el corte. Si bien esto es algo que esperamos abordar eventualmente, no puedo proporcionar ninguna línea de tiempo sobre cuándo sería eso.
Mientras tanto, tanto la consola como la línea de comandos de base de fuego proporcionan un medio no transaccional para hacerlo, por ejemplo, para la automatización de pruebas.
Gracias por su comprensión y gracias por probar Firestore!