Eliminare i dati con una funzione Cloud richiamabile

La pagina descrive come utilizzare una funzione Cloud richiamabile per eliminare i dati. Dopo aver eseguito il deployment di questa funzione, puoi chiamarla direttamente dalla tua app mobile o dal tuo sito web per eliminare in modo ricorsivo documenti e raccolte. Ad esempio, puoi utilizzare questa soluzione per consentire a utenti selezionati di eliminare intere raccolte.

Per altri modi di eliminare le raccolte, vedi Eliminare i dati.

Soluzione: elimina i dati con una funzione Cloud richiamabile

L'eliminazione di intere raccolte da un'app mobile con risorse limitate può essere difficile da implementare per i seguenti motivi:

  • Non esiste un'operazione che elimini una raccolta in modo atomico.
  • L'eliminazione di un documento non comporta l'eliminazione dei documenti nelle sue sottoraccolte.
  • Se i documenti hanno sottoraccolte dinamiche, può essere difficile sapere quali dati eliminare per un determinato percorso.
  • L'eliminazione di una raccolta di oltre 500 documenti richiede più operazioni di scrittura in batch o centinaia di eliminazioni singole.
  • In molte app non è appropriato concedere agli utenti finali l'autorizzazione a eliminare intere raccolte.

Fortunatamente, puoi scrivere una funzione Cloud invocabile per eseguire eliminazioni sicure e performanti di intere raccolte o strutture ad albero di raccolte. La funzione Cloud riportata di seguito implementa una funzione richiamabile, il che significa che può essere chiamata direttamente dalla tua app mobile o dal tuo sito web, come faresti per una funzione locale.

Per eseguire il deployment della funzione e provare una demo, consulta il codice campione.

Funzione Cloud Functions

La funzione Cloud Functions riportata di seguito elimina una raccolta e tutti i relativi discendenti.

Anziché implementare la tua logica di eliminazione ricorsiva per la funzione Cloud, puoi utilizzare il comando firestore:delete nell'interfaccia a riga di comando (CLI) di Firebase. Puoi importare qualsiasi funzione dell'interfaccia a riga di comando di Firebase nella tua applicazione Node.js utilizzando il pacchetto firebase-tools.

L'interfaccia a riga di comando di Firebase utilizza l'API REST Cloud Firestore per trovare tutti i documenti nel percorso specificato ed eliminarli singolarmente. Questa implementazione non richiede la conoscenza della gerarchia dei dati specifica dell'app e consente persino di trovare ed eliminare i documenti "orfani" che non hanno più un padre.

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(async (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'.
    await firebase_tools.firestore
      .delete(path, {
        project: process.env.GCLOUD_PROJECT,
        recursive: true,
        force: true,
        token: functions.config().fb.token
      });

    return {
      path: path 
    };
  });

Richiamo del client

Per chiamare la funzione, ottieni un riferimento alla funzione dall'SDK Firebase e passa i parametri richiesti:

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);
        });
}
Swift
Nota: questo prodotto non è disponibile su watchOS e sui target di App Clip.
    // Snippet not yet written
    
Objective-C
Nota: questo prodotto non è disponibile come target per watchOS e App Clip.
    // Snippet not yet written
    

Kotlin+KTX

/**
 * Call the 'recursiveDelete' callable function with a path to initiate
 * a server-side delete.
 */
fun deleteAtPath(path: String) {
    val deleteFn = Firebase.functions.getHttpsCallable("recursiveDelete")
    deleteFn.call(hashMapOf("path" to path))
        .addOnSuccessListener {
            // Delete Success
            // ...
        }
        .addOnFailureListener {
            // Delete Failed
            // ...
        }
}

Java

/**
 * Call the 'recursiveDelete' callable function with a path to initiate
 * a server-side delete.
 */
public void deleteAtPath(String path) {
    Map<String, Object> data = new HashMap<>();
    data.put("path", path);

    HttpsCallableReference deleteFn =
            FirebaseFunctions.getInstance().getHttpsCallable("recursiveDelete");
    deleteFn.call(data)
            .addOnSuccessListener(new OnSuccessListener<HttpsCallableResult>() {
                @Override
                public void onSuccess(HttpsCallableResult httpsCallableResult) {
                    // Delete Success
                    // ...
                }
            })
            .addOnFailureListener(new OnFailureListener() {
                @Override
                public void onFailure(@NonNull Exception e) {
                    // Delete failed
                    // ...
                }
            });
}

Utilizzando l'SDK client per le funzioni Cloud Functions richiamabili, lo stato di autenticazione dell'utente e il parametro path vengono passati senza soluzione di continuità alla funzione remota. Al termine della funzione, il client riceverà un callback con il risultato o un'eccezione. Per scoprire come chiamare una funzione cloud da Android, Apple o un'altra piattaforma, leggi la documentazione.

Limitazioni

La soluzione mostrata sopra mostra l'eliminazione delle raccolte da una funzione richiamabile, ma devi essere a conoscenza delle seguenti limitazioni:

  • Coerenza: il codice riportato sopra elimina i documenti uno alla volta. Se effettui una query durante un'operazione di eliminazione in corso, i risultati potrebbero riflettere uno stato parzialmente completo in cui vengono eliminati solo alcuni documenti target. Inoltre, non vi è alcuna garanzia che le operazioni di eliminazione andranno a buon fine o falliranno in modo uniforme, quindi preparati a gestire i casi di eliminazione parziale.
  • Timeout: la funzione riportata sopra è configurata per essere eseguita per un massimo di 540 secondi prima del timeout. nel migliore dei casi, il codice di eliminazione può eliminare 4000 documenti al secondo. Se devi eliminare più di 2.000.000 di documenti, ti consigliamo di eseguire l'operazione sul tuo server in modo che non si verifichi il timeout. Per un esempio di come eliminare una raccolta dal tuo server, consulta Eliminare le raccolte.
  • L'eliminazione di un numero elevato di documenti potrebbe causare un caricamento lento del visualizzatore dei dati nella console Google Cloud o restituire un errore di timeout.