Usuwanie danych za pomocą funkcji w Cloud Functions z możliwością wywoływania

Na tej stronie opisano, jak używać wywoływalnej funkcji w Cloud Functions do usuwania danych. Po wdrożeniu tej funkcji możesz wywołać ją bezpośrednio z aplikacji mobilnej lub strony internetowej, aby rekurencyjnie usunąć dokumenty i kolekcje. Możesz na przykład użyć tego rozwiązania, aby zezwolić wybranym użytkownikom na usuwanie całych kolekcji.

Inne sposoby usuwania kolekcji znajdziesz w sekcji Usuwanie danych.

Rozwiązanie: usuwanie danych za pomocą wywoływalnej funkcji w Cloud Functions

Usuwanie całych kolekcji z aplikacji mobilnej o ograniczonej ilości zasobów może być trudne do wdrożenia z tych powodów:

  • Nie ma operacji, która umożliwia atomowe usunięcie kolekcji.
  • Usunięcie dokumentu nie powoduje usunięcia dokumentów w kolekcjach podrzędnych.
  • Jeśli dokumenty zawierają dynamiczne podzbiory, trudno może być określić, które dane należy usunąć z danej ścieżki.
  • Usunięcie kolekcji zawierającej ponad 500 dokumentów wymaga wielu operacji zapisu zbiorczego lub setek pojedynczych usunięć.
  • W wielu aplikacjach nie ma sensu przyznawać użytkownikom uprawnień do usuwania całej kolekcji.

Na szczęście możesz napisać wywołaną funkcję w Cloud Functions, która umożliwia bezpieczne i efektywne usuwanie całych kolekcji lub drzew kolekcji. Poniżej znajduje się funkcja Cloud Functions, która implementuje funkcję wywołującą. Oznacza to, że można ją wywołać bezpośrednio z aplikacji mobilnej lub witryny, tak jak w przypadku funkcji lokalnej.

Aby wdrożyć funkcję i wypróbować wersję demonstracyjną, zapoznaj się z przykładowym kodem.

Funkcja w Cloud Functions

Funkcja Cloud Functions poniżej usuwa kolekcję i wszystkie jej elementy potomne.

Zamiast implementować w Cloud Functions własną logikę usuwania rekurencyjnego, możesz skorzystać z polecenia firestore:delete w interfejsie wiersza poleceń Firebase. Za pomocą pakietu firebase-tools możesz importować do aplikacji Node.js dowolną funkcję z wiersza poleceń Firebase.

Interfejs wiersza poleceń Firebase używa interfejsu API REST Cloud Firestore do znajdowania wszystkich dokumentów w podanej ścieżce i usuwania ich pojedynczo. Ta implementacja nie wymaga znajomości konkretnej hierarchii danych w aplikacji. Pozwala nawet znaleźć i usunąć „osierocone” dokumenty, które nie mają już rodzica.

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

Wywoływanie klienta

Aby wywołać funkcję, uzyskaj odwołanie do niej z pakietu SDK Firebase i podaj wymagane parametry:

Sieć
/**
 * 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
Uwaga: ta usługa nie jest dostępna na urządzeniach z systemem watchOS i nie można jej używać w przypadku App Clip.
    // Snippet not yet written
    
Objective-C
Uwaga: ta usługa nie jest dostępna na urządzeniach z systemem watchOS i nie można jej używać w przypadku 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
                    // ...
                }
            });
}

Dzięki użyciu pakietu SDK klienta do wywoływania funkcji w Cloud Functions stan uwierzytelniania użytkownika i parametr path są bezproblemowo przekazywane do funkcji zdalnej. Po zakończeniu działania funkcja wywoła klienta z wynikiem lub wyjątkiem. Aby dowiedzieć się, jak wywołać funkcję w chmurze z poziomu Androida, Apple lub innej platformy, przeczytaj dokumentację.

Ograniczenia

Rozwiązanie pokazane powyżej demonstruje usuwanie kolekcji z funkcji wywoływalnej, ale należy pamiętać o tych ograniczeniach:

  • Spójność: kod powyżej usuwa dokumenty po jednym. Jeśli prześlesz zapytanie, gdy trwa operacja usuwania, wyniki mogą odzwierciedlać stan częściowego ukończenia, w którym usunięte są tylko niektóre docelowe dokumenty. Nie ma również gwarancji, że operacje usuwania zakończą się równomiernie lub niepowodzenie. Przygotuj się więc na przypadki częściowego usunięcia danych.
  • Limity czasu – funkcja powyżej jest skonfigurowana tak, aby działać maksymalnie 540 sekund przed przekroczeniem limitu czasu. W najlepszym przypadku kod usuwania umożliwia usunięcie 4000 dokumentów na sekundę. Jeśli chcesz usunąć ponad 2 000 000 dokumentów, rozważ wykonanie tej operacji na własnym serwerze, aby nie przekroczyć limitu czasu. Przykład usuwania kolekcji z własnego serwera znajdziesz w sekcji Usuwanie kolekcji.
  • Usunięcie dużej liczby dokumentów może spowodować, że przeglądarka danych w konsoli Google Cloud będzie się wczytywać wolno lub zwróci błąd limitu czasu.