Supprimer des collections et sous-collections

Il peut être difficile de mettre correctement en œuvre la suppression de données dans Cloud Firestore, en particulier à partir d'une application mobile à ressources limitées, pour les raisons suivantes :

  • Aucune opération ne supprime de manière atomique une collection.
  • La suppression d'un document ne supprime pas les documents de ses sous-collections.
  • Si vos documents comportent des sous-collections dynamiques, il peut être difficile de savoir quelles données supprimer pour un chemin donné.
  • La suppression d'une collection de plus de 500 documents nécessite plusieurs opérations d'écriture par lot ou des centaines de suppressions uniques.
  • Dans de nombreuses applications, il n'est pas approprié de donner aux utilisateurs finaux l'autorisation de supprimer des collections entières.

Heureusement, vous pouvez écrire une fonction Cloud pour exécuter des suppressions sécurisées et performantes de collections entières ou d'arbres de collection.

Avant de poursuivre, lisez les informations relatives au modèle de données Cloud Firestore.

Solution : supprimer des données avec une fonction Cloud appelable

Ce guide décrit comment utiliser une fonction Cloud appelable pour supprimer des données. Une fois cette fonction déployée, vous pouvez l'appeler directement depuis votre application mobile ou votre site Web pour supprimer de manière récursive des documents et des collections.

Pour déployer la fonction et essayer une version de démonstration, consultez l'exemple de code.

Fonction Cloud

La fonction Cloud ci-dessous supprime une collection et tous ses descendants.

Au lieu de mettre en œuvre votre propre logique de suppression récursive pour votre fonction Cloud, vous pouvez tirer parti de la commande firestore:delete de l'interface de ligne de commande (CLI) Firebase. Vous pouvez importer n'importe quelle fonction de la CLI Firebase dans une application Node.js propre à l'aide du package firebase-tools.

La CLI Firebase utilise l'API REST Cloud Firestore pour rechercher tous les documents dans le chemin spécifié et les supprimer individuellement. Cette mise en œuvre ne nécessite aucune connaissance de la hiérarchie de données propre à votre application et permet même de rechercher et de supprimer les documents "orphelins" qui n'ont plus de parent.

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 fonction Cloud ci-dessus est mise en œuvre comme fonction appelable, ce qui signifie qu'elle peut être appelée directement depuis votre application mobile ou votre site Web, de la même manière qu'une fonction locale.

Invocation de client

Pour appeler la fonction, obtenez une référence à la fonction du SDK Firebase et transmettez les paramètres requis :

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);
        });
}

En utilisant le SDK du client pour les fonctions Cloud appelables, l'état d'authentification des utilisateurs et le paramètre path sont transmis en toute transparence à la fonction distante. Lorsque la fonction est terminée, le client reçoit un rappel avec le résultat ou une exception. Pour savoir comment appeler une fonction Cloud depuis Android, iOS ou une autre plate-forme, lisez la documentation.

Limites

La solution ci-dessus illustre la suppression de collections à partir d'une fonction appelable, mais vous devez tenir compte des limites suivantes :

  • Cohérence : le code ci-dessus supprime les documents un par un. Si vous lancez une requête alors qu'une opération de suppression est en cours, vos résultats peuvent indiquer un état partiellement complet où seuls certains documents ciblés sont supprimés. Il n'existe aucune garantie que les opérations de suppression aboutiront ou échoueront de manière uniforme. Soyez donc prêt à gérer les cas de suppression partielle.
  • Expiration de délai : la fonction ci-dessus est configurée pour s'exécuter pendant 540 secondes au maximum avant expiration du délai. Dans le meilleur des cas, le code de suppression peut supprimer 4 000 documents par seconde. Si vous devez supprimer plus de 2 000 000 documents, vous pouvez envisager d'exécuter l'opération sur votre propre serveur afin de ne pas interrompre le traitement.