How to append an element into array inside a firestore document? - node.js

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(...);

Related

firebase cloud functions iterate over collection

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.

Firebase Functions won't read document on Firestore

Hi I'm trying to read a users document stored on Firestore using Firebase Functions. Each user has a unique document with extra data that cannot be stored on Firebase Auth. The document name is the user UID.
But I can't access the doc when I'm trying to read it on my callable function.
Code to create doc when user is created:
exports.createdacc = functions.auth.user().onCreate(user => {
console.log('User created', user.phoneNumber);
return admin.firestore().collection('users').doc(user.uid).set({
number: user.phoneNumber,
verified: false,
});
});
Callable function to read that doc so I can make some decisions
exports.checkVerification = functions.https.onCall((data, context) => {
if (!context.auth){
throw new functions.https.HttpsError('unauthenticated');
}
console.log('user is ', context.auth.uid);
const user = admin.firestore().collection('users').doc(context.auth.uid);
user.get().then(doc => {
//temp code -- Not working
console.log('data read');
if (doc.get().verified){
console.log('verified');
} else {
console.log('not verified');
}
return "success";
}).catch(error => {
throw new functions.https.HttpsError('internal');
});
});
Why cant I read the doc? Nothing inside there executes.
Try to use data() at callback of user.get()
user.get().then(doc => {
//you get user doc value by using data()
const userData = doc.data();
// then you can use all properties from userData
const verified = userData.verified;
});
You don't return the promise returned by user.get().then(...);: your Cloud Function may be cleaned up before the asynchronous work is complete and the response sent back to the front-end.
Note that doing doc.get().verified is incorrect: as you will see in the doc, you need to pass the field path of a specific field to this method. So either you do doc.get("verified") or you can do doc.data().verified;.
Therefore the following should work:
exports.checkVerification = functions.https.onCall((data, context) => {
if (!context.auth) {
throw new functions.https.HttpsError('unauthenticated');
}
console.log('user is ', context.auth.uid);
const user = admin.firestore().collection('users').doc(context.auth.uid);
return user.get().then(doc => {
console.log('data read');
if (doc.get("verified") {
console.log('verified');
} else {
console.log('not verified');
}
return "success";
}).catch(error => {
throw new functions.https.HttpsError('internal');
});
});
In addition, note that you may throw an error if the user document does not exist and return a specific error to the front-end, i.e. not the generic internal one (maybe not-found, see the list of possible codes).
I have seen, on occasion, that information coming in to the function via context and data are actually JSON, and not strictly a standard Javascript object. In a similar issue of matching (in my case, a customClaim on the context.auth.token), I had to do something like:
JSON.parse(JSON.stringify(context.auth.token.customCLaim))
They behave like an object (i.e. I can call/assign context.auth.token.customClaim), but results from a console.log are different.
console.log(context.auth.token.customCLaim);
//prints {"userID": "1234567890"}
console.log(JSON.parse(JSON.stringify(context.auth.token.customClaim)));
//prints {userID: "1234567890"}
Subtle, but it tripped me up in a few authentication cases.

How to get back the Object id of the item pushed in an array in mongoose

I am trying to push an object in an array in a Mongodb document.
const relative_history = {
purposeOfVisit: purposeOfVisit,
currentIssue: currentIssue,
};
await relative_details.history.push(relative_history);
await user_details.save();
If i put a constant against await relative_details.history.push(relative_history); that constant just shows the number of elements present in the array. How can i get the object id for the object that is pushed?
you can use callback function like:
user_details.save((err, doc)=> {
if (err) return console.error(err);
console.log(doc);
});
In order to save the user_details I assume you have previously done a find operation to get the document based in some query and that the relative_details is a field whithin the user_details, so in the end you could just perform the code below:
const relative_history = {
purposeOfVisit: purposeOfVisit,
currentIssue: currentIssue,
};
try {
const user_details = await "YOUR_USER_SCHEMA".findById("YOUR_USER_ID");
if(!user_details){throw New Error("User not found")}
user_details.relative_details.history.push(relative_history);
const updated_user_details = await user_details.save();
console.log(updated_user_details._id);
} catch(err){// handle errors}

Not getting firestore document field data even though document has couple of fields

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

node.js, MongoDB - async and await functionality not working as expected in findOne() function

I'm relatively new to node.js and MongoDB, so sorry in advance if it is an obvious answer.
I am trying to create the logic to validate an object that is being passed in, but I am having difficulty using async and await to time things correctly. Basically, I will be passing in a string and am trying to see if there is any record in the db that exists with that string. If there isn't, then errors returns empty, and if there is, then errors has something in it. Here are the relevant parts of my code:
The Schema (../models/FriendRequest):
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var friendRequestSchema = new Schema({
requestFrom: {type: String, required: true},
requestTo: {type: String, required: true},
});
module.exports = mongoose.model('FriendRequest', friendRequestSchema);
The Logic:
var FriendRequest = require('../models/FriendRequest');
async function checkForDuplicateRecord(requestTo) {
let errors = "";
// do not allow duplicate requests
FriendRequest.findOne({
requestTo: requestTo
}, (err, record) => {
if (err) {
errors += "{'\n'}Server Errors.";
} else if (record) {
errors += "{'\n'}You have already sent this person a friend request.";
}
console.log(record);
})
return await errors
}
// Function to perform server-side validation of the friend request before sending to db.
const FriendRequestValidator = (requestTo) => {
let errors = "";
(async() => {
let duplicationErrors = await checkForDuplicateRecord(requestFrom, requestTo);
console.log('duplication errors: ' + duplicationErrors);
errors += duplicationErrors;
})()
return errors;
};
module.exports = FriendRequestValidator;
When I print record, I see that if it exists, the data in records is correct. However, duplicateErrors is being printed before record, and is empty, even if record is not empty. This leads me to believe that the timing is the reason my results are not going as expected, and that I'm using async and await incorrectly.
Thank you very much in advance for any help!
Change function checkForDuplicateRecord in this way
async function checkForDuplicateRecord(requestTo) {
let errors = "";
try{
// do not allow duplicate requests
let record = await FriendRequest.findOne({
requestTo: requestTo
});
if (record) {
errors += "{'\n'}You have already sent this person a friend request.";
console.log(record);
}
}
catch(e){
errors += "{'\n'}Server Errors.";
}
return errors
}
await requires function to return the promise. But in your case you was calling the function and handling errors in callback function so you was getting errors as empty string in next statement.
async function checkForDuplicateRecord(requestTo) {
let errors = "";
try{
let friendRequestSent = await FriendRequest.findOne({
requestTo: requestTo
});
//If already sent request to same user throw error
if(friendRequestSent)
throw new Error("Your error message")
//.... rest of code
}catch(err){
//it will throw error if anything goes wrong in your findOne query or error thrown by your logic if request is sent to same user
errors = err.message
}
return errors;
}

Resources