How to recursively list subcollections in Firestore - node.js

We back up all our firestore collections daily, using an empty array [] in:
const client = new firestore.v1.FirestoreAdminClient();
return client
.exportDocuments({
name: databaseName,
outputUriPrefix: storageName,
collectionIds:[], // empty array backs up all collections and subcollections
})
However, according to the docs, were we ever to want to import data, we would need to import ALL collections and subcollections, which is substantial. In order to import more granularly (import only necessary collections), we need to provide exportDocuments.collectionIds an array of all our collections' and subcollections' names. ie: ['Coll1', 'Coll2', 'SubCollX', etc.]. Since our collections are subject to change, we need a way to get an array of the names of all collections and subcollections programatically.
This code gets the names of only the root collections, not any subcollections:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.getCollections = functions.https.onRequest(async (req, res) => {
var rootCollectionIds = [];
var subCollectionIds = [];
const rootCollections = await admin.firestore().listCollections();
rootCollections.map(async rootColl => {
rootCollectionIds.push(rootColl.id);
// without named doc, this returns empty:
const subCollections = await rootColl.doc().listCollections();
subCollectionIds = subCollections.map(subColl => subColl.id);
});
res.status(200).send({"rootCollectionIds": rootCollectionIds, "subCollectionIds": subCollectionIds});
});
This must be doable. In the console (https://console.cloud.google.com/firestore/import-export?project={myproject}), Google lists all collections and subcollections when using
Export one or more collection groups
Best I can tell, listCollections() onlys works at the root, or on a specific document. Since we do not know which specific documents contain subcollections, we need a way to find the subcollections globally. Is there some kind of allDescendents param that could be used?
Running node.js in a Cloud Function.
Any help greatly appreciated.

No, no such option exists. The only way you can find out about subcollections is to build a DocumentReference and call listCollections on it to find which subcollections are available under that one document.

Related

firebase functions date query

Im trying to run a cron job to delete old notifications. Following a post on this on medium:
https://medium.com/#varunpvp/how-to-schedule-a-firebase-cloud-function-ded2db0cf9f3
This is where im up to:
const oneMonthOld = admin.firestore.Timestamp.now().toMillis() - 2592000000;
const oldNotifications = await admin.firestore()
.collection('notifications')
.where('createdAt', '>', oneMonthOld);
I've created a document in this collection. I have other queries in the function working against this collection. The article sais that more than is < (?) i've tried both ways round
const results2 = await oldNotifications.get();
results2.forEach(doc => {
console.log('in function');
oldNotificationsBatch.delete(doc.ref);
});
the function neither deletes the record or emits the log in the UI
Can anyone suggested whats going wrong here?

MongoDb: Accessing Collection and console log Object

I am trying to access a MongoDB Collection and I want to console.log a Object from this Collection.
I connect to my MongoDB as follows:
async function main(){
/**
* Connection URI. Update <username>, <password>, and <your-cluster-url> to reflect your cluster.
* See https://docs.mongodb.com/ecosystem/drivers/node/ for more details
*/
const uri = process.env.DB_Port;
const client = new MongoClient(uri,{ useNewUrlParser: true, useUnifiedTopology: true});
try {
// Connect to the MongoDB cluster
await client.connect();
// Make the appropriate DB calls
await listDatabases(client);
} catch (e) {
console.error(e);
} finally {
await client.close();
}
}
main().catch(console.error);
async function listDatabases(client){
databasesList = await client.db().admin().listDatabases();
console.log("Databases:");
databasesList.databases.forEach(db => console.log(` - ${db.name}`));
};
This console logs:
Databases:
- radiototem
- test
- admin
- local
Now I want to access the collection test and console log everything that is inside it. How can I achieve this?
Best regards
In order to get items from MongoDB, you first need to access the collection in which they are stored. Collections in MongoDB are basically the same as tables in SQL.
To get a collection, call the .collection() function on your DB object you get from client.db() with the name of your collection like this:
client.db().collection('test'); // This gives you the collection "test"
If you want to get items from a collection, you can use the .find() method. You can pass it a query parameter which is an object where you define, which items should be selected based on their properties.
Example, get all users named peter from the users collection:
const db = client.db();
const users = db.collection('users');
const usersNamedPeterCursor = users.find({ name: 'Peter' });
Now if you want to get all items from a collection, you can simply use the find method without the query parameter. This will return all items from the collection.
The find method returns a Cursor object which lets you interact with the returned data. You can call methods on the Cursor object like max(), min(), toArray(), each() and many more.
So, if you want to console.log every item in your collection you can do it like this:
client.db().collection('test').find().each(function(error, item) {
// console .log your item or do something else with it
});

Firebase Cloud Function onDelete variable param length

I have the following code
//index.js
export const deletePicture = functions.region("europe-west1").database
.ref("galleries/{galleryId}/{pictureId}")
.onDelete(pictures.deletePicture)
//pictures.js
export const deletePicture = (snap, {params: {galleryId}}) => {
console.log("Image deletion detected in the database, deleting images
from the Storage...")
const {fileName} = snap.val()
const bucket = storage.bucket()
const baseURL = `galleries/${galleryId}`
// Thumbnails
const promises = [sizes
.map(size =>
bucket.file(`${baseURL}/thumb_${size}_${fileName}`).delete())]
// Original
promises.push(bucket.file(`${baseURL}/${fileName}`).delete())
return Promise
.all(promises)
.then(() => console.log(`All versions of ${fileName} are now deleted.`))
}
In my realtime database and Storage, I have the following structure:
galleries
|_rooms
| |_roomId
| |_picture
|_foods
|_picture
Is there any way that the above mentioned onDelete Cloud Function would trigger for the deletion of either of the pictures? The difference here is that the rooms picture is one level deeper, so I think that pictureId does not match roomId/picture.
Cloud Functions has no knowledge of the meaning of your JSON. So the trigger for delete of galleries/{galleryId}/{pictureId}, really just means that this function gets triggered whenever a node at the second level under galleries gets trigged.
In the structure you show, that means that this function will trigger whenever /galleries/room/roomId or /galleries/foods/picture gets deleted. The first of these will get triggered when you delete the last picture from a room.

How to iterate through every document in every collection in Firestore?

Let's say my app has a collection for every restaurant that uses it. Each document for each collection is basically a type of food that has an expiration time stamp on it. What I need to do it query through every 'food' in every 'restaurant' and delete each 'food' if the current time that I get from my node js server is past the expiration time stamp. What is an efficient way to do this?
First you need a HTTP Request trigger Cloud Function that you can invoke by cron. ie. https://cron-job.org/
With this cloud function you should use the Admin JS SDK to loop through collections and fetch all documents.
You can get all collections with the function getCollections() See https://cloud.google.com/nodejs/docs/reference/firestore/0.9.x/Firestore#getCollections
import * as admin from 'firebase-admin'
try {
admin.initializeApp(functions.config().firebase)
} catch (e) {}
const db = admin.firestore()
export const getCollections = functions.https.onRequest(async(req, res) => {
const collections = await db.getCollections()
res.status(200).send(collections.map(collection => collection.id))
})

Get Value Key pair from Firebase with Node.js function

Ok, I am using Algolia for handling search within my app. The following code adds whatever is in my database to my Algolia Index. However, whenever the data is imported, all of the values in a firebase node get placed under "text" in the Algolia Index.
How can make it to where it is stored as the key value pair found in Firebase.
i.e.
address: 1234 Main St.
VS. / (Instead of)
text:
address: 1234 Main St.
My Code:
exports.indexentry = functions.database
.ref('/blog-posts/{blogid}/text')
.onWrite(event => {
const index = client.initIndex(ALGOLIA_POSTS_INDEX_NAME);
const firebaseObject = {
text: event.data.val(),
objectID: event.params.blogid,
};
});
So what you are missing here, is the pushing of the data to Algolia. What you can do if you want to have all the firebase data in an object, is assigning it to a new object, as well as the objectID
exports.indexentry = functions.database
.ref('/blog-posts/{blogid}/text')
.onWrite(event => {
const index = client.initIndex(ALGOLIA_POSTS_INDEX_NAME);
const firebaseObject = Object.assign({}, event.data.val(), {
objectID: event.params.blogid,
});
index.saveObject(firebaseObject); // .then or .catch as well
});
Does that make sense?

Resources