Skip to content

Instantly share code, notes, and snippets.

@gregfenton
Last active September 10, 2023 02:18
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gregfenton/fb9b3aeede570964b84fe6129a463561 to your computer and use it in GitHub Desktop.
Save gregfenton/fb9b3aeede570964b84fe6129a463561 to your computer and use it in GitHub Desktop.
Deletes all records from a Firestore collection that match some criteria -- command-line JavaScript/node
/**
* cleanup_firestore.js
*
* This code is a "node script" used to delete all documents from a Firestore
* collection that are older than a given date. This script can run against
* "the cloud" or the emulator.
*
* The jump point for the script is this line in main():
*
* await deleteDocs('orders', 'closedDate', '<', new Date('2021/08/31'))
*
* where `orders` is the collection and `closedDate` is a field in each document.
*
* The script is a "client app", so it logs in with Firebase Auth using email/password from the variable USER1.
* This USER1 user (the.admin.guy@test.com) must exist in your Firebase project and have write access
* to the collection you are deleting from. You can create that user account via
* Firebase Console >> Authentication.
*
* This script is assumed to live in an existing Javascript project that has its own package.json.
* I store this script in <PROJECT_ROOT>/tools/cleanup/.
*
* To use:
* 1. Import the 'firebase' and 'esm' NPM modules to your project (Run the command: `npm install firebase esm`)
* 2. Edit the data for USER1 and FIREBASE_CONFIG, then adjust parameters in `main` for the call to `deleteDocs()`.
*
* NOTE: this code works with the Firebase Web SDK v9 API. Earlier revisions used the v8 API.
*
* To run: node cleanup_firestore.js
*
* Alternatively, you can create a "script" in package.json:
* "scripts": {
* ...
* "cleanup": "node tools/cleanup/cleanup_firestore.js"
* }
*
* and then run the script with: npm run cleanup
*/
import {initializeApp} from 'firebase/app';
import {
addDoc,
collection,
connectFirestoreEmulator,
getFirestore,
writeBatch
} from 'firebase/firestore';
import {getAuth, signInWithEmailAndPassword} from 'firebase/auth';
let FIRESTORE;
let AUTH;
// Firebase Console >> Authentication
const USER1 = {
email: 'the.admin.guy@test.com', // USER THAT EXISTS IN FIREBASE AUTH
password: 'let_me_in',
uid: UID_FROM_FIREBASE_AUTH_FOR_THIS_USER
};
// Firebase Console >> Project Overview >> General >> Apps
// (use existing app values or click Add app to create new WEB app)
const FIREBASE_CONFIG = {
apiKey: VALUES_FROM_FIREBASE_APP_CONFIG,
appId: VALUES_FROM_FIREBASE_APP_CONFIG,
authDomain: VALUES_FROM_FIREBASE_APP_CONFIG,
databaseURL: VALUES_FROM_FIREBASE_APP_CONFIG,
messagingSenderId: VALUES_FROM_FIREBASE_APP_CONFIG,
projectId: VALUES_FROM_FIREBASE_APP_CONFIG
};
const initializeFB = async () => {
let fbApp = initializeApp(FIREBASE_CONFIG);
FIRESTORE = getFirestore(fbApp);
let USING_FIREBASE_EMULATOR = true;
if (USING_FIREBASE_EMULATOR) {
// I explicitly set "host" and "port" in firebase.json so that devices
// on my local network can access the emulator:
// "emulators": {
// "functions": {
// "port": 5001,
// "host": "192.168.1.53"
// },
// "firestore": {
// "port": 5002,
// "host": "192.168.1.53"
// },
// ...
//
connectFirestoreEmulator(FIRESTORE, '192.168.1.53', 5002);
}
try {
AUTH = getAuth();
await signInWithEmailAndPassword(AUTH, USER1.email, USER1.password);
let currUser = AUTH.currentUser;
console.log(`Logged in with USER1 uid(${currUser.uid})`);
} catch (ex) {
console.error(ex.message);
throw ex;
}
};
const deleteDocs = async (collectionPath, field, compareOp, compareValue) => {
let queryRef = query(
collection(FIRESTORE, collectionPath),
where(field, compareOp, compareValue)
);
let querySnap = getDocs(queryRef);
console.log(`WHERE returned ${querySnap.size} records`);
let batch = writeBatch(FIRESTORE);
querySnap.forEach((docSnap) => {
console.log(`DELETING ${docSnap.ref.id}`);
batch.delete(docSnap.ref);
});
try {
await batch.commit();
console.log('Transaction successfully committed!');
} catch (ex) {
console.log(`Transaction failed: ${ex.message}`);
throw ex;
}
};
const main = async () => {
console.log('>>> START LOADING Users - ', Date());
await initializeFB();
await deleteDocs('orders', 'closedDate', '<', new Date('2021/08/31'));
console.log('>>> DONE - ', Date());
process.exit(0);
};
main();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment