Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
gigocabrera opened this issue Jan 2, 2018 · 18 comments

Comments

@gigocabrera
Copy link

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

@larssn
Copy link
Contributor

larssn commented Jan 3, 2018

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

@gigocabrera
Copy link
Author

Do you have a sample you can share?

@larssn
Copy link
Contributor

larssn commented Jan 3, 2018

Yeah, the official docs have an example: https://firebase.google.com/docs/firestore/manage-data/delete-data

@gigocabrera
Copy link
Author

@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

@larssn
Copy link
Contributor

larssn commented Jan 4, 2018

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.

@gigocabrera
Copy link
Author

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

@davideast
Copy link
Member

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 :)

@gigocabrera
Copy link
Author

Thank you @davideast 🍺

@kctang
Copy link

kctang commented Mar 16, 2018

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

@gigocabrera
Copy link
Author

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

@tgangso
Copy link

tgangso commented Apr 1, 2018

Thanks @kctang works like a charm.

@louisptremblay
Copy link

louisptremblay commented May 1, 2018

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

@adam-hurwitz
Copy link

adam-hurwitz commented Sep 3, 2018

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

@swftvsn
Copy link

swftvsn commented Sep 28, 2018

The solution here can be easily adapted for frontend too: firebase/firebase-admin-node#361

@RezaRahmati
Copy link

RezaRahmati commented Aug 29, 2019

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

@Santosh183
Copy link

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.

@larssn
Copy link
Contributor

larssn commented Oct 3, 2019

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.

@kriskott
Copy link

kriskott commented Aug 5, 2021

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.

Would anyone be able to share a Python version of this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests