I am building a forum, and when a person comments on a thread, I would like to email everyone that someone has added a comment. This requires iterating over a firestore collection. How do I do this using firebase cloud functions?
exports.onCommentCreation = functions.firestore.document('/forum/threads/threads/{threadId}/comments/{commentId}')
.onCreate(async(snapshot, context) => {
var commentDataSnap = snapshot;
var threadId = context.params.threadId;
var commentId = context.params.commentId;
// call to admin.firestore().collection does not exist
var comments = await admin.firestore().collection('/forum/threads/threads/{threadId}/comments/');
// iterate over collection
});
You need to run get query, not sure what is admin here, but you can do like this:
const citiesRef = db.collection('cities'); // pass your collection name
const snapshot = await citiesRef.where('capital', '==', true).get(); // query collection
if (snapshot.empty) {
console.log('No matching documents.');
return;
}
snapshot.forEach(doc => {
console.log(doc.id, '=>', doc.data());
});
You can check this link, for more details.
Also, I would suggest you go through this link to correctly set up the cloud function for firestore if there are any issues with it.
Related
I am creating a messaging application and I am looking for a way to check to see if there already exists a conversation between people before a new one is created. Here is the implementation I currently have:
export const createConversation = async (req, res) => {
const conversation = req.body.conversation;
const message = req.body.message;
try {
//Check if conversation between participants exists
const newConversation = await Conversation.find({participants: conversation.participants});
if(newConversation != null) {
newConversation = new Conversation(conversation);
}
message.conversationId = newConversation._id;
const newMessage = new Messages(message);
await newConversation.save();
await newMessage.save();
res.status(201).json({conversation: newConversation, message: newMessage});
} catch (error) {
res.status(409).json({ message: error.message });
}
};
I want to check if there exists a conversation in MongoDB atlas where the participant list contains the same Ids as the conversation that is trying to be created. What is the most efficient way to implement this?
I am writing this post because I am facing some issues with my scheduled cloud function.
Step 1: Access the following / collection: users => doc: uid => collection: bank => doc: account.
Step 2: Increase my users' (all of them) dailyRewardCounter by 150 every 24h.
The thing is that my function can't access my users' collection uid and return an error because the scheduled function is not able to read the uid (cf. picture).
Question: Do you know how can I access each users' subcollection based on their individual 'uid' to add 150 in the collection 'bank' ?
enter image description here
export const dailyCreditReinitialize = functions.pubsub.schedule('0 0 * * *').onRun(async (context) => {
const uid = context.auth!.uid; // seems like the function does not read that
const userRef = db.collection('users').doc(uid);
const userSnap = await userRef.get();
const getUid = userSnap.data()!.uid;
const bankCollectionRef = db.collection('users').doc(getUid).collection('bank');
return bankCollectionRef.get()
.then(querySnapshot =>{
if (querySnapshot.empty){
console.log("Nothing to return")
return null;
} else {
let batch = db.batch();
querySnapshot.forEach(doc => {
batch.update(doc.ref, {
dailyRewardCounter: 150,
});
});
return batch.commit();
}
})
.catch(error => {
console.log(error);
return null;
});
})
Contrary to a Realtime Database triggered Cloud Function, with a scheduled Cloud Function, context.auth is null.
If I correctly understand your question ("Increase my users' (all of them)") you need to loop over all the users documents of the Collection. Something along the following lines in pseudo-code:
Loop on the users Collection and get all the users IDs
For each user, get the querySnapshot of the bankcollection
Loop on each of the querySnapshots to update the bank doc
I use Node.js and back4app.com
I try to update the user object. Therefore I have read a lot and found this promissing documentation:
let progressId = "xyz";
let userId = "12354"; //aka objectId
const User = new Parse.User();
const query = new Parse.Query(User);
// Finds the user by its ID
query.get(userId).then((user) => {
// Updates the data we want
user.set('progressId', progressId);
// Saves the user with the updated data
user.save()
.then((response) => {
console.log('Updated user', response);
})
.catch((error) => {
console.error('Error while updating user', error);
});
});
But there also is a warning. It states:
The Parse.User class is secured by default, you are not able to invoke save method unless the Parse.User was obtained using an authenticated method, like logIn, signUp or current
How would this look like in code?
My solution
Well, I got it to work. While I figured it out, I have found some small show stoppers. I list it for anyone it may concern.
Thanks #RamosCharles I added the Master Key in Parse._initialize. Only with that .save(null, {useMasterKey: true}) works. Take notice, without null it also won't work.
That's my working code:
let progressId = "xyz";
const User = Parse.Object.extend('User'); //instead of const User = new Parse.User();
const query = new Parse.Query(User);
query.equalTo("objectId", '123xyz');
query.get(userId).then((userObj) => {
// Updates the data we want
userObj.set('progressId', progressId);
// Saves the user with the updated data
userObj.save(null, {useMasterKey: true}).then((response) => {
console.log('Updated user', response);
}).catch((error) => {
console.error('Error while updating user', error);
});
});
Now I'm wondering
why my working code is different from documentation?
how secure is my code? And what is to do to get it more secure?
Yes, their API Reference is very helpful! On this section, there's a "try on JSFiddle" button, have you already seen that?
To update a user object, you must use the Master Key. On the frontend, it's not recommended, and it's better to create a cloud code function and call it on your frontend. However, for test purposes, you can keep using the API Reference, but on JSFiddle, you need to do some changes, here is their sample code, but with the adjustments:
Parse.serverURL = 'https://parseapi.back4app.com';
Parse._initialize('<your-appID-here>', '<your-JSKey-here>', '<Your-MasterKey-here>');
const MyCustomClass = Parse.Object.extend('User');
const query = new Parse.Query(MyCustomClass);
query.equalTo("objectId", "<object-ID-here>");
query.find({useMasterKey: true}).then((results) => {
if (typeof document !== 'undefined') document.write(`ParseObjects found: ${JSON.stringify(results)}`);
console.log('ParseObjects found:', results);
}, (error) => {
if (typeof document !== 'undefined') document.write(`Error while fetching ParseObjects: ${JSON.stringify(error)}`);
console.error('Error while fetching ParseObjects', error);
});
You'll need to insert the "_" before the "initialize" in your "Parse._initialize" and insert the Master Key in your query as I did on the query.find.
I'm new in firestore functions and database, so I'm kind of stuck. I have this document:
As you can see, answers in an empty array for now, but I'll have a bunch of string.
The problem is the Cloud Function I'm using is failing. this is my function
exports.registerUserResponse = functions.https.onRequest((request, response) => {
const original = request.body;
const type_form_id = original.form_response.form_id
var userRef = admin.firestore().collection('users').doc(user_email);
var transaction = admin.firestore().runTransaction(t => {
return t.get(userRef)
.then(doc => {
console.log(doc.data());
var newAnswer = doc.data().answers.arrayUnion(type_form_id);
t.update(userRef, {answers: newAnswer});
});
}).then(result => {
//return response.status(200).send();
return response.status(200).json({result: `Message added.`}).send();
}).catch(err => {
console.log(err);
return response.status(500).json({result: `Message: ${err} error.`}).end();
});
All the values are okay, but I'm getting this error at arrayUnion funtion
TypeError: Cannot read property 'arrayUnion' of undefined
at t.get.then.doc (/user_code/index.js:27:58)
at process._tickDomainCallback (internal/process/next_tick.js:135:7)
So, I don't know how I should use that function. Thanks for any answers!
arrayUnion isn't a method that exists on any item of data you can get back from a Firestore query. It's definitely not available on the value of undefined that you're getting in doc.data().answers.
It looks like you might be confused about how to use FieldValue.arrayUnion(). You don't need a transaction to use that. Just perform an update as described in the documentation:
var washingtonRef = db.collection('cities').doc('DC');
// Atomically add a new region to the "regions" array field.
var arrUnion = washingtonRef.update({
regions: admin.firestore.FieldValue.arrayUnion('greater_virginia')
});
Yours might look something like this:
admin.firestore().collection('users').doc(user_email).update({
answers: admin.firestore.FieldValue.arrayUnion(type_form_id)
}).then(...);
I have migrated the firebase real time data to cloud firestore through the below nodejs script:
var fireastoreDB = admin.firestore();
var db = admin.database();
var ref = db.ref("/users");
let items = [];
ref.once("value", function(snapshot) {
let collectionRef = fireastoreDB.collection("users");
snapshot.forEach(item => {
collectionRef.doc(item.key).set(item.val());
});
I got the data into cloud firestore. Now I have to implement REST APIs for this data in the nodejs.
var docusRef = db.collection("users").get().then( (data) => {
console.log(data);
data.forEach( item => {
let docObj = item.data();
console.log(docObj['Coins']);
console.log(docObj['Coins']['Total Coins']);
});
});
from this code, I am able to get all documents field data. But when I am trying to get specific document data directly I was getting undefined(exists: false) but data was there under this document.
var db = admin.firestore();
var docusRef = db.collection("users").doc('Atest - 12345')
docusRef.get().then(function (col) {
var name=col.get("Coins");
console.log(name); // undefined & exists: false
});
when i have added document/fields manually from firebase console, I was getting data.
Is this issue with migrated data or what?
Can anyone please find the issue.
The get() method returns "a Promise resolved with a DocumentSnapshot containing the current document contents". Therefore you have to use the data() method of the DocumentSnapshot to get the fields of the document, as follows:
docusRef.get().then(function (col) {
var name=col.data().Coins;
console.log(name);
});