Angularfire: Feature Request: Official version to delete a collection and all nested documents

Created on 2 Jan 2018  路  17Comments  路  Source: angular/angularfire

Feature Request

Can you include in the Cloud Firestore documentation an official version on how to delete a collection and all nested documents please

For example, if I have the following data structure in Firestore
-- how can I delete a user and all the forms?

users
---userId
-----forms
--------formId

Most helpful comment

There's no angularfire version. You'd have to access the native Firestore SDK references:

export class SomeTSClass {
  constructor(private db: AngularFirestore) {}

  async deleteCol(path) {
    const qry: firebase.firestore.QuerySnapshot = await this.db.collection(path).ref.get();

    // You can use the QuerySnapshot above like in the example i linked
    qry.forEach(doc => {
      doc.ref.delete();
    });
  }
}

The above example is off the top of my head, so it might contain errors, but the principle is there.

All 17 comments

You have to do it manually: loop over every subcollection and delete each document.

Do you have a sample you can share?

@larssn thank you for the link. However, that is the plain vanilla version of Delete. I was wondering if there is an AngularFirestore version written in Typescript

There's no angularfire version. You'd have to access the native Firestore SDK references:

export class SomeTSClass {
  constructor(private db: AngularFirestore) {}

  async deleteCol(path) {
    const qry: firebase.firestore.QuerySnapshot = await this.db.collection(path).ref.get();

    // You can use the QuerySnapshot above like in the example i linked
    qry.forEach(doc => {
      doc.ref.delete();
    });
  }
}

The above example is off the top of my head, so it might contain errors, but the principle is there.

@larssn you're awesome!!! Thank you very much. I'll give it a try and update this ticket with findings

Hi @gigocabrera! I would love this feature as well. However, AngularFire is a wrapper around the core SDK. Route any future feature requests to the official SDK repo. They are much wiser than me :)

Thank you @davideast :beer:

Hi all,

Sharing a function to delete all documents in named collections, by batches. It returns a promise with total number of deleted documents. It does not delete subcollections. Internally, uses rxjs to do the magic of streaming and batching values to be deleted.

  /**
   * Delete all documents in specified collections.
   *
   * @param {string} collections Collection names
   * @return {Promise<number>} Total number of documents deleted (from all collections)
   */
  async deleteCollections (...collections: string[]) {
    let totalDeleteCount = 0
    const batchSize = 500
    return new Promise<number>((resolve, reject) => Observable
      .from(collections)
      .concatMap(collection => Observable.fromPromise(this.fireStore.collection(collection).ref.get()))
      .concatMap(q => Observable.from(q.docs))
      .bufferCount(batchSize)
      .concatMap((docs: QueryDocumentSnapshot[]) => Observable.create((o: Observer<number>) => {
          const batch = this.fireStore.firestore.batch()
          docs.forEach(doc => batch.delete(doc.ref))
          batch.commit()
            .then(() => {
              o.next(docs.length)
              o.complete()
            })
            .catch(e => o.error(e))
        })
      )
      .subscribe(
        (batchDeleteCount: number) => totalDeleteCount += batchDeleteCount,
        e => reject(e),
        () => resolve(totalDeleteCount)
      )
    )
  }

@kctang that's awesome, thank you! I'll give it a try this weekend

Thanks @kctang works like a charm.

here's the function from the docs (node.js but works as is in javascript if you replace process.nextTick by setTimeout 0)
https://github.com/firebase/snippets-node/blob/a2a7b6763c5bfd3b7eb833742086cb3b74a71375/firestore/main/index.js#L801-L840

I used this as a workaround to delete a user's Collection from the Android client. Passed into the method is the reference of the user's Collection and the batch size to process. I will refactor this onto the server as recommended by the documentation, but am using this for my proof of concept in the meantime.
```
fun deleteCollection(collection: CollectionReference, batchSize: Int) {
try {
// Retrieve a small batch of documents to avoid out-of-memory errors/
var deleted = 0
collection
.limit(batchSize.toLong())
.get()
.addOnCompleteListener {
for (document in it.result.documents) {
document.getReference().delete()
++deleted
}
if (deleted >= batchSize) {
// retrieve and delete another batch
deleteCollection(collection, batchSize)
}
}
} catch (e: Exception) {
System.err.println("Error deleting collection : " + e.message)
}
}

The solution here can be easily adapted for frontend too: https://github.com/firebase/firebase-admin-node/issues/361

I needed to delete a collection inside another collection, which @kctang solution wasn't working for me, so I changed it to this and using rxjs6 operators

import { AngularFirestore, AngularFirestoreCollection, QueryDocumentSnapshot } from '@angular/fire/firestore';
import { from, Observable, Observer } from 'rxjs';
import { bufferCount, concatMap } from 'rxjs/operators';

    async deleteCollection(collection: AngularFirestoreCollection<any>): Promise<number> {
        let totalDeleteCount = 0;
        const batchSize = 500;

        return new Promise<number>((resolve, reject) =>
            from(collection.ref.get())
                .pipe(
                    concatMap((q) => from(q.docs)),
                    bufferCount(batchSize),
                    concatMap((docs: Array<QueryDocumentSnapshot<any>>) => new Observable((o: Observer<number>) => {
                        const batch = this.fireStore.firestore.batch();
                        docs.forEach((doc) => batch.delete(doc.ref));
                        batch.commit()
                            .then(() => {
                                o.next(docs.length);
                                o.complete();
                            })
                            .catch((e) => o.error(e));
                    })),
                )
                .subscribe(
                    (batchDeleteCount: number) => totalDeleteCount += batchDeleteCount,
                    (e) => reject(e),
                    () => resolve(totalDeleteCount),
                ),
        );
    }

Then you can pass a collection reference, which can be a nested collection

There's no angularfire version. You'd have to access the native Firestore SDK references:

export class SomeTSClass {
  constructor(private db: AngularFirestore) {}

  async deleteCol(path) {
    const qry: firebase.firestore.QuerySnapshot = await this.db.collection(path).ref.get();
    const batch = this.db.firestore.batch();

    // You can use the QuerySnapshot above like in the example i linked
    qry.forEach(doc => {
      batch.delete(doc);
    });

    batch.commit();
  }
}

The above example is off the top of my head, so it might contain errors, but the principle is there. Remember that batch can currently only handle 500 operations, so if you have more, you'll need to chain batches. I believe they have an example for that too.

This still doesn't serve our purpose . it will delete all documents within collection but not collection itself.

This still doesn't serve our purpose . it will delete all documents within collection but not collection itself.

The collection is just an abstraction, it doesn't really exist. So it is "deleted" when all it's documents are removed.

Was this page helpful?
0 / 5 - 0 ratings