Borra colecciones y subcolecciones

Puede ser difícil implementar de manera correcta la eliminación de datos en Cloud Firestore, especialmente desde una app para dispositivos móviles con recursos limitados. Estos son los motivos:

  • No existe ninguna operación que borre una colección automáticamente.
  • Si borras un documento, no se borrarán los documentos de sus subcolecciones.
  • Si tus documentos tienen subcolecciones dinámicas, puede ser difícil saber qué datos borrar de una ruta de acceso determinada.
  • Se requieren varias operaciones de escritura en lotes o cientos de eliminaciones individuales para borrar una colección de más de 500 documentos.
  • En muchas apps, no es recomendable otorgar permiso a los usuarios finales para borrar colecciones completas.

Afortunadamente, puedes escribir una función de Cloud para ejecutar eliminaciones seguras y eficaces de colecciones completas o árboles de colecciones.

Antes de continuar, obtén información sobre el modelo de datos de Cloud Firestore.

Solución: Borra datos con una función de Cloud que admite llamadas

En esta guía, se describe cómo usar una función de Cloud que admite llamadas para borrar datos. Una vez que implementes la función, puedes llamarla directamente desde tu app o sitio web para dispositivos móviles a fin de borrar documentos y colecciones de manera recurrente.

Para implementar la función y probar una demostración, consulta el código de ejemplo.

Función de Cloud

La función de Cloud de más abajo permite borrar una colección y todas sus subcolecciones.

En lugar de implementar tu lógica de eliminación recurrente de la función de Cloud, puedes aprovechar el comando firestore:delete en la interfaz de línea de comandos (CLI) de Firebase. Puedes importar cualquier función de Firebase CLI a tu aplicación Node.js con el paquete de firebase-tools.

Firebase CLI usa la API de REST de Cloud Firestore para buscar todos los documentos en la ruta de acceso especificada y borrarlos de forma individual. Esta implementación no requiere conocer la jerarquía de datos específica de tu app. Incluso buscará y borrará documentos "huérfanos" que ya no tienen un elemento superior.

Node.js

/**
 * Initiate a recursive delete of documents at a given path.
 *
 * The calling user must be authenticated and have the custom "admin" attribute
 * set to true on the auth token.
 *
 * This delete is NOT an atomic operation and it's possible
 * that it may fail after only deleting some documents.
 *
 * @param {string} data.path the document or collection path to delete.
 */
exports.recursiveDelete = functions
  .runWith({
    timeoutSeconds: 540,
    memory: '2GB'
  })
  .https.onCall((data, context) => {
    // Only allow admin users to execute this function.
    if (!(context.auth && context.auth.token && context.auth.token.admin)) {
      throw new functions.https.HttpsError(
        'permission-denied',
        'Must be an administrative user to initiate delete.'
      );
    }

    const path = data.path;
    console.log(
      `User ${context.auth.uid} has requested to delete path ${path}`
    );

    // Run a recursive delete on the given document or collection path.
    // The 'token' must be set in the functions config, and can be generated
    // at the command line by running 'firebase login:ci'.
    return firebase_tools.firestore
      .delete(path, {
        project: process.env.GCLOUD_PROJECT,
        recursive: true,
        yes: true,
        token: functions.config().fb.token
      })
      .then(() => {
        return {
          path: path
        };
      });
  });

La función de Cloud anterior se implementa como función que admite llamadas, es decir, a la que puedes llamar directamente desde tu app para dispositivos móviles o sitio web como lo harías con una función local.

Invocación al cliente

Para llamar la función, obtén una referencia a la función desde el SDK de Firebase y pasa los parámetros obligatorios:

Web

/**
 * Call the 'recursiveDelete' callable function with a path to initiate
 * a server-side delete.
 */
function deleteAtPath(path) {
    var deleteFn = firebase.functions().httpsCallable('recursiveDelete');
    deleteFn({ path: path })
        .then(function(result) {
            logMessage('Delete success: ' + JSON.stringify(result));
        })
        .catch(function(err) {
            logMessage('Delete failed, see console,');
            console.warn(err);
        });
}

Cuando usas el SDK cliente para las funciones de Cloud que admiten llamadas, el parámetro path y estado de autenticación del usuario se pasan sin problemas a la función remota. Cuando se completa la función, el cliente recibirá una devolución de llamada con el resultado o una excepción. Para obtener información sobre cómo llamar a una función de Cloud desde Android, iOS o una plataforma distinta, consulta la documentación.

Limitaciones

La solución anterior muestra cómo borrar colecciones desde una función que admite llamadas. Sin embargo, debes tener en cuenta estas limitaciones:

  • Coherencia: El código anterior borra un documento a la vez. Si realizas una consulta cuando haya una operación de eliminación en curso, es posible que tus resultados reflejen un estado parcialmente completo en el que se borren solo algunos documentos objetivo. Tampoco hay garantía de que las operaciones de eliminación tengan éxito o fracasen de manera uniforme, por lo tanto, debes estar preparado para manejar casos de eliminación parcial.
  • Tiempos de espera: La función anterior está configurada para ejecutarse durante un período máximo de 540 segundos antes de que se agote el tiempo de espera. En el mejor de los casos, el código de eliminación puede borrar 4,000 documentos por segundo. Si necesitas borrar más de 2,000,000 de documentos, te recomendamos ejecutar la operación en tu servidor para que no se agote el tiempo de espera.

Enviar comentarios sobre...

Si necesitas ayuda, visita nuestra página de asistencia.