Ir para o console

Excluir coleções e subcoleções

Pode ser difícil implementar corretamente a exclusão de dados no Cloud Firestore, especialmente de um app para dispositivos móveis com recursos limitados. Veja os motivos para isso:

  • Nenhuma operação exclui atomicamente uma coleção.
  • A exclusão de um documento não remove os que estão nas subcoleções.
  • Se os seus documentos tiverem subcoleções dinâmicas, pode ser difícil saber quais dados devem ser excluídos de um determinado caminho.
  • A exclusão de uma coleção com mais de 500 documentos exige várias operações de gravação em lote ou centenas de exclusões únicas.
  • Em muitos apps, não é apropriado conceder permissão para que os usuários finais excluam coleções inteiras.

Você pode gravar uma função do Cloud Functions para executar exclusões seguras e eficientes de coleções inteiras ou de árvores de coleção.

Antes de continuar, leia sobre o modelo de dados do Cloud Firestore.

Solução: excluir dados com uma função chamável do Cloud Functions

Veja neste guia como usar uma função chamável do Cloud Functions para excluir dados. Depois de implantar essa função, é possível chamá-la diretamente do app ou site para dispositivos móveis caso você queira excluir os documentos e coleções de maneira recorrente.

Para implantar a função e testar uma demonstração, consulte o código de amostra.

Função do Cloud Functions

A função do Cloud Functions abaixo exclui uma coleção e todos os descendentes dela.

Em vez de implementar sua própria lógica de exclusão recorrente para a função do Cloud Functions, você pode aproveitar o comando firestore:delete na Firebase CLI. É possível importar qualquer função dessa interface para um aplicativo próprio do Node.js usando o pacote firebase-tools.

A Firebase CLI usa a API REST do Cloud Firestore para localizar todos os documentos no caminho especificado e excluí-los individualmente. Essa implementação não exige conhecimento da hierarquia de dados específica do seu app. Ela consegue até mesmo encontrar e excluir documentos "órfãos" que não têm mais um pai.

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

A função do Cloud Functions acima é implementada como uma função chamável. Isso significa que ela pode ser invocada diretamente do seu app ou site para dispositivos móveis, como você faria para uma função local.

Invocação do cliente

Para chamar a função, consiga uma referência à função do SDK do Firebase e transmita os parâmetros necessários:

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

Ao usar o SDK do cliente para funções chamáveis do Cloud Functions, o estado de autenticação dos usuários e o parâmetro path são transmitidos sem interrupções para a função remota. Quando a função for concluída, o cliente receberá um retorno de chamada com o resultado ou uma exceção. Para saber mais sobre como chamar uma função do Cloud Functions no Android, iOS ou em outra plataforma, leia a documentação.

Limitações

A solução mostrada acima demonstra a exclusão de coleções de uma função que chamável, mas fique atento às seguintes limitações:

  • Consistência: o código acima exclui um documento de cada vez. Se você fizer a consulta enquanto houver uma operação de exclusão em andamento, seus resultados poderão refletir um estado parcialmente completo, em que apenas alguns documentos de destino serão excluídos. Também não há garantia de que as operações de exclusão serão bem-sucedidas ou falharão de maneira uniforme. Portanto, prepare-se para lidar com casos de exclusão parcial.
  • Tempos limite: a função acima está configurada para ser executada por um máximo de 540 segundos antes do tempo limite. O código de exclusão pode remover 4.000 documentos por segundo no melhor dos casos. Se você precisar excluir mais de 2 milhões de documentos, execute a operação no seu próprio servidor para que ela não expire.