How to list all subcollections of a Firestore document? - javascript

Hi,
I have a problem with downloading all collections from the document. I would like after finding the id (userUid) document to be able to download all its collections, I need the id of each of these collection
export const getAllMessagesByUserId = async (userUid) => {
const result = await firebase
.firestore()
.collection('messages')
.doc(userUid)
.onSnapshot((snapshot) => {
console.log(snapshot);
});
};

I wrote an article which proposes solutions to this problem: How to list all subcollections of a Cloud Firestore document? As a matter of fact, "retrieving a list of collections is not possible with the mobile/web client libraries" as explained in the Firestore documentation.
I would suggest you use the second method proposed in the article, using a Cloud Function.
Here is the code copied from the article.
Cloud Function:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.getSubCollections = functions.https.onCall(async (data, context) => {
const docPath = data.docPath;
const collections = await admin.firestore().doc(docPath).listCollections();
const collectionIds = collections.map(col => col.id);
return { collections: collectionIds };
});
Example of calling the Cloud Function from a web app:
const getSubCollections = firebase
.functions()
.httpsCallable('getSubCollections');
getSubCollections({ docPath: 'collectionId/documentId' })
.then(function(result) {
var collections = result.data.collections;
console.log(collections);
})
.catch(function(error) {
// Getting the Error details.
var code = error.code;
var message = error.message;
var details = error.details;
// ...
});

Related

how to trigger a firestore collection via cloud function

I want to trigger a new collection (timeline collection) from the existing collection of followers collection and videos collection whenever I clicked the following button in my app.
Now the problem is that, the Cloud Function is created from the view log but the new collection (timeline collection) won't be created.
Below is the code for the Cloud Function where I target the followers collection and the videos collection to create a new timeline collection. I anticipate for your help.
Videos collection
Followers collection
Cloud function view logs
const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();
// // Create and Deploy Your First Cloud Functions
// // https://firebase.google.com/docs/functions/write-firebase-functions
//
// exports.helloWorld = functions.https.onRequest((request, response) => {
// response.send("Hello from Firebase!");
// });
exports.onCreateFollower = functions.firestore
.document("/followers/{userId}/userFollowers/{userfollowerId}")
.onCreate(async (snapshot, context) => {
console.log("The Event has Created The Follower", snapshot.id);
const userId = context.params.userId;
const userfollowerId = context.params.userfollowerId;
// 1) Create followed users posts ref
const followedUserVideosCollection = admin
.firestore()
.collection("videos")
.doc(userId)
.collection("userVideos");
// 2) Create following user's timeline ref
const timelineVideosCollection = admin
.firestore()
.collection("timeline")
.doc(userfollowerId)
.collection("timelinePosts");
// 3) Get followed users posts
const querySnapshot = await followedUserVideosCollection.get();
// 4) Add each user post to following user's timeline
querySnapshot.forEach(doc => {
if (doc.exists) {
const videoId = doc.id;
const videoData = doc.data();
timelineVideosCollection.doc(videoId).set(videoData);
}
});
});
I figured out what causes the error "querySnapshot.forEach isn't a function". According to this answer, you need to query the collection first because get() returns a document instead of a snapshot. Here's a sample code (see step 3):
// 1) Create followed users posts ref
const followedUserVideosCollection = admin
.firestore()
.collection("videos")
.doc("Videos 1") // I changed the value with your sample for test purposes and also because I'm not sure how you fill up this doc.
.collection("userVideos");
// 2) Create following user's timeline ref
const timelineVideosCollection = admin
.firestore()
.collection("timeline")
.doc(userfollowerId)
.collection("timelinePosts");
// 3) Get followed users posts & Add each user post to following user's timeline
await followedUserVideosCollection.where('id', '==', 0).get().then((querySnapshot) => {
if (querySnapshot) {
querySnapshot.forEach(doc => {
if (doc) {
const videoId = doc.id;
const videoData = doc.data();
timelineVideosCollection.doc(videoId).set(videoData);
}
});
}else {
console.log("Document not found");
}
}).catch((error) => {
console.log(error);
});
A solution is to create a filter, and make sure that the document you're looking for matches the filter. For example, a document inside the subcollection userVideos should have a field called id with value of 0.
You may have to remodel your DB to fix the line where I put a comment but this code should write the timeline collection.

Unable to retrieve data from Multiple collection firebase

Hope you're in good health.
I have a problem.
export function fetchListing() {
return function (dispatch) {
dispatch(fetchListingRequest());
//Getting Listing where status is in_review
firebase
.firestore()
.collection("listings")
.where("status", "==", "in_review")
.onSnapshot(
snapshot => {
const data = [];
snapshot.docs.forEach(doc => {
const temp = {};
// Getting address of business
firebase
.firestore()
.collection("users")
.doc(doc.data().business_id)
.get()
.then(users => {
temp["address"] = users.data().address;
})
.catch(error => {
dispatch(fetchListingFailed(error));
});
temp["title"] = doc.data().title;
temp["description"] = doc.data().description;
temp["listing_file"] = doc.data().listing_file;
data.push([doc.id, temp]);
});
dispatch(fetchListingSucess(data));
},
error => {
dispatch(fetchListingFailed(error));
}
);
};
}
I am Unable to get address in state but when I log it It displayed in console. I am able to access address when I am retrieving it from firebase and also in reducer I am also getting address.
reasons can be :
The hierarchy of your firestore collection
or you have to know that in js you can't affect the data to a variable that is in the main prog just when it is in a one level functions
for exemple here you can do this
locale = this ;
than you can use all the variables in two other levels

Firebase cloud function call client side script

I have a script in Reactjs that get data (numbers) from api and addup this numbers with numbers from Firebase collection when user opens the page and the user can see this numbers.
There are going to be many users in the app and every user is going to have diffrent numbers from the same script
I was wondering if its possible with Firebase Cloud Functions to run this Client side script on the server and do the callculations of this numbers on the server and store this numbers in a Firestore collection.
im a begginer in nodejs and cloud functions i dont know if this is possible to do
get the numbers from Api
getLatestNum = (sym) => {
return API.getMarketBatch(sym).then((data) => {
return data;
});
};
Cloud function i was trying
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const db = admin.firestore();
exports.resetAppointmentTimes = functions.pubsub
.schedule('30 20 * * *')
.onRun((context) => {
const appointmentTimesCollectionRef = db.collection('data');
return appointmentTimesCollectionRef
.get()
.then((querySnapshot) => {
if (querySnapshot.empty) {
return null;
} else {
let batch = db.batch();
querySnapshot.forEach((doc) => {
console.log(doc);
});
return batch.commit();
}
})
.catch((error) => {
console.log(error);
return null;
});
});
It is indeed possible to call a REST API from a Cloud Function. You need to use a Node.js library which returns Promises, like axios.
It's not 100% clear, in your question, to which specific Firestore doc(s) you want to write, but I make the asumption it will be done within the batched write.
So, something along the following lines should do the trick:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const axios = require('axios');
admin.initializeApp();
const db = admin.firestore();
exports.resetAppointmentTimes = functions.pubsub
.schedule('30 20 * * *')
.onRun((context) => {
let apiData;
return axios.get('https://yourapiuri...')
.then(response => {
apiData = response.data; //For example, it depends on what the API returns
const appointmentTimesCollectionRef = db.collection('data');
return appointmentTimesCollectionRef.get();
})
.then((querySnapshot) => {
if (querySnapshot.empty) {
return null;
} else {
let batch = db.batch();
querySnapshot.forEach((doc) => {
batch.update(doc.ref, { fieldApiData: apiData});
});
return batch.commit();
}
})
.catch((error) => {
console.log(error);
return null;
});
});
Two things to note:
If you want to add the API result to some fields value, you need to give more details on your exact need
Important: You need to be on the "Blaze" pricing plan. As a matter of fact, the free "Spark" plan "allows outbound network requests only to Google-owned services". See https://firebase.google.com/pricing/ (hover your mouse on the question mark situated after the "Cloud Functions" title)

Firestore Cloud Function empty collection

I have a problem that's bugging me for days. I am trying to create a Firebase Cloud function that reads from the Firestore database.
My Firestore DB looks like this:
Problem is that I cannot list users like this:
db.collection('users').get().then((snapshot) => snapshot.forEach(...));
If I try to do this I get empty response, like there are no users in my users collection.
But I try to access user directly it works:
await db.collection('users/5CZxgu8nmNXu2TgplwOUdOIt8e33/receipts').get()
My complete code:
import * as functions from 'firebase-functions';
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.cat = functions.https.onRequest(async (req, res) => {
const receiptList: any = [];
const db: Firestore = admin.firestore();
const usersRef = await db.collection('users').get();
console.log(usersRef.empty); // Returns true
const receiptsRef = await db
.collection('users/5CZxgu8nmNXu2TgplwOUdOIt8e33/receipts')
.get();
receiptsRef.forEach((receipt: any) => {
console.log(receipt);
receiptList.push(receipt);
// Here I can access data
});
res.send(receiptList);
return '';
});
Does anyone have any idea what I'm doing wrong? Thank you!
Your users collection is actually empty. See how the document IDs are shown in italics? That means there is not actually a document in its place, however, there are subcollections with documents organized underneath them.
When you query a collection, you only get the documents that are immediately within that collection. A query will not pick up documents organized in subcollections. In this respect, queries are said to be "shallow". As you've seen, you need to reach deeper into the subcollection to get its documents.
Bottom line is that the queries you're showing are doing exactly what they're supposed to do.
Thanks again Doug for your help.
I manage to solve my problem. Here is my complete solution.
import * as functions from 'firebase-functions';
import {
Firestore
} from '#google-cloud/firestore';
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.cat = functions.https.onRequest(async (req, res) => {
const receiptList: any = [];
const db: Firestore = admin.firestore();
const receipts = await db.collectionGroup('receipts').get();
receipts.forEach((doc: any) => {
console.log(doc.id, ' => ', doc.data());
receiptList.push(doc.data());
});
res.send(receiptList);
return '';
});
.get() gets all documents. In your case those documents are empty therefore .get() doesn't consider them.
The simplest solution that I found for this is to replace .get() with .listDocuments(). Now you could read each doc entry like you would a doc.

How to program Firebase Cloud Functions to iterate through array of references?

I'm pretty new to Cloud Functions on Firebase and I'm struggling to program some code to iterate through an array of document references that have been downloaded from the Firestore.
The array is stored in my Firestore and contains references to each admin user in my users collection. Each of these users has a field in their document with their messaging token, which I need to send the message. I've manage to get the code to send a notification to a token that I define as a constant in the code however haven't had any luck sending to the tokens stored in the database.
Here is my code so far;
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
// // Create and Deploy Your First Cloud Functions
// // https://firebase.google.com/docs/functions/write-firebase-functions
//
// exports.helloWorld = functions.https.onRequest((request, response) => {
// response.send("Hello from Firebase!");
// });
exports.notifyNewReport = functions.firestore
.document('admin/reportsToReview')
.onUpdate((change, context) => {
console.log('Change to doc function registered');
// Get an object representing the document
const newValueReports = change.after.data().reports;
// ...or the previous value before this update
const previousValueReports = change.before.data().reports;
if (newValueReports.length > previousValueReports.length) {
console.log('Report added to review list');
var adminsArray = ""
admin.firestore()
.collection('admin')
.doc('admins')
.get()
.then(doc => {
adminsArray = doc.data().admins
return console.log('Found admin UID: ' + adminsArray);
})
.catch(error => {
console.error(error);
res.error(500);
});
//Code to get send notification to each device
console.log("Construct the notification message.");
var message = {
notification: {
body: 'There are new reports to review!',
},
token: token
};
admin.messaging().send(message)
}
});
If anyone can point me in the right direction that would be greatly appreciated! :)

Categories