Sampath
Sampath

Reputation: 65870

Build Firestore transaction operation

I have a delete operation as shown below on Firestore. It is working fine. But could you tell me how can I use a transaction for doing that? At this moment if there is any failure it does partial delete.

  async delete(project: DtoProject): Promise<void> {
    await this.fireStore.doc<Project>(`projects/${project.id}/`).delete();
    const budgetGroupsQuerySnapshot = await this.authenticationProvider.firestoreDb.collection(`projects/${project.id}/budgetGroups`).get();//budgetGroups
    budgetGroupsQuerySnapshot.forEach(async (doc: any) => {
      await this.fireStore.doc<BudgetGroup>(`projects/${project.id}/budgetGroups/${doc.id}`).delete();
    });
    const budgetsQuerySnapshot = await this.authenticationProvider.firestoreDb.collection(`projects/${project.id}/budgets`).get();//budgets
    budgetsQuerySnapshot.forEach(async (doc: any) => {
      await this.fireStore.doc<Budget>(`projects/${project.id}/budgets/${doc.id}`).delete();
    });
    const categoriesQuerySnapshot = await this.authenticationProvider.firestoreDb.collection(`projects/${project.id}/categories`).get();//categories
    categoriesQuerySnapshot.forEach(async (doc: any) => {
      await this.fireStore.doc<Category>(`projects/${project.id}/categories/${doc.id}`).delete();
    });
    const transactionsQuerySnapshot = await this.authenticationProvider.firestoreDb.collection(`projects/${project.id}/transactions`).get();//transactions
    transactionsQuerySnapshot.forEach(async (doc: any) => {
      const notesQuerySnapshot = await this.authenticationProvider.firestoreDb.collection(`projects/${project.id}/transactions/${doc.id}/notes`).get();//notes
      notesQuerySnapshot.forEach(async (n: any) => {
        await this.fireStore.doc<Note>(`projects/${project.id}/transactions/${doc.id}/notes/${n.id}`).delete();
      });
      const photosQuerySnapshot = await this.authenticationProvider.firestoreDb.collection(`projects/${project.id}/transactions/${doc.id}/photos`).get();//photos
      photosQuerySnapshot.forEach(async (p: any) => {
        await this.fireStore.doc<Photo>(`projects/${project.id}/transactions/${doc.id}/photos/${p.id}`).delete();
      });
      await this.fireStore.doc<Transaction>(`projects/${project.id}/transactions/${doc.id}`).delete();
    });
}

Upvotes: 2

Views: 454

Answers (1)

You have to get all the doc references that you want to delete and then iterate through each one them using Promise.all in the transaction.

Like below:

  deleteAll(arrayOfDocReferencesToBeDeleted){
    return this.db.runTransaction(async transaction => {
      return Promise.all(arrayOfDocReferencesToBeDeleted.map(async (ref) => {
        await transaction.delete(ref);
      }));
    }).then(() => console.log('success')).catch(() => console.log('error'))
  }

In your case you can change your function to this code:

async delete(project: DtoProject): Promise<void> {

    //first get all the doc referencs that you want to delete

    var  firstReferenceToBeDelete = this.fireStore.doc<Project>(`projects/${project.id}/`);

    //get all the docs from the collection
    const budgetGroupsQuerySnapshot = await this.authenticationProvider.firestoreDb.collection(`projects/${project.id}/budgetGroups`).get();//budgetGroups
    //map the doc references to an array
    var groupsRef = budgetGroupsQuerySnapshot.docs.map((doc) => { return this.fireStore.doc<BudgetGroup>(`projects/${project.id}/budgetGroups/${doc.id}`) });
    //repeat it until you have all the doc references that you want to delete

    const budgetsQuerySnapshot = await this.authenticationProvider.firestoreDb.collection(`projects/${project.id}/budgets`).get();//budgets
    var budgetsRef = budgetsQuerySnapshot.docs.map((doc) => { return this.fireStore.doc<Budget>(`projects/${project.id}/budgets/${doc.id}`) });

    const categoriesQuerySnapshot = await this.authenticationProvider.firestoreDb.collection(`projects/${project.id}/categories`).get();//categories
    var categoriesRef = categoriesQuerySnapshot.docs.map((doc) => { return this.fireStore.doc<Category>(`projects/${project.id}/categories/${doc.id}`) });

    const transactionsQuerySnapshot = await this.authenticationProvider.firestoreDb.collection(`projects/${project.id}/transactions`).get();//transactions

    var notesRef = [];
    var photosRef = [];
    var lastRefs = [];

    transactionsQuerySnapshot.forEach(async (doc: any) => {
      const notesQuerySnapshot = await this.authenticationProvider.firestoreDb.collection(`projects/${project.id}/transactions/${doc.id}/notes`).get();//notes
      var notes = notesQuerySnapshot.docs.map((note) => {
        return this.fireStore.doc<Note>(`projects/${project.id}/transactions/${doc.id}/notes/${note.id}`)
      });
      notesRef.concat(notes);

      const photosQuerySnapshot = await this.authenticationProvider.firestoreDb.collection(`projects/${project.id}/transactions/${doc.id}/photos`).get();//photos
      var photos = photosQuerySnapshot.docs.map((photo) => { return this.fireStore.doc<Photo>(`projects/${project.id}/transactions/${doc.id}/photos/${photo.id}`) });
      photosRef.concat(photos);

      lastRefs.push(this.fireStore.doc<Transaction>(`projects/${project.id}/transactions/${doc.id}`));
    });

    //make a single array with them
    var arrayOfDocReferencesToBeDeleted = [firstReferenceToBeDelete].concat(groupsRef, budgetsRef, categoriesRef, notesRef, photosRef, lastRefs)

    //after you have an array with all your doc references you can iterate them in the transaction

    return this.db.runTransaction(async transaction => {
      return Promise.all(arrayOfDocReferencesToBeDeleted.map(async (ref) => {
        await transaction.delete(ref);
      }));
    }).then(() => console.log('success')).catch(() => console.log('error'))
  }

Of course you should break this function in another functions but this will work.

Upvotes: 1

Related Questions