Firebase firestore won't persist array - javascript

I have a cloud function that processes and stores data sent to it. Most of the data comes from the request except for some ID values I store in the main collection I am working with for reference. My problem is the data I get from the other cloud firestore collection is not persisted in the collection I am writing to. My code looks like this:
await emails.forEach(async (email: string) => {
try {
// This should only ever return one result
const student = await usersRef.where('userEmail', '==', email).get();
if (student.empty) {
console.log(`No matching documents for email: ${email}`);
} else {
student.forEach((s) => {
const studentData = s.data();
console.log('found student: ', studentData);
// StudentIds stores reference to student objects
studentIds.push(studentData.playerUniqueID);
});
}
} catch (err) {
console.log('Error finding students: ', err);
throw new functions.https.HttpsError('internal', 'Error finding students');
}
});
const sessionId = uuidv4();
const newSession: newSessionWriteRequest = {
emails, // emails is also an array of strings and is persisted properly
owner,
// StudentIds is not empty at this point and is populated correctly. StudentIds is an array of strings
studentIds,
ongoing: true,
sessionName: data.sessionName,
startTime: data.sessionStartDate,
sessionId
};
try {
// All values except studentIds are persisted to the sessionsRef except studentIds, which is a blank array
await sessionsRef.doc(sessionId).set(newSession);
console.log('new session: ', newSession);
return newSession;
} catch (err) {
console.log('Error creating new session: ', err);
throw new functions.https.HttpsError('internal', 'Error creating new session');
}
StudentIds is just an array of strings. emails is also an array of string but is stored correctly. The only difference between the two is that emails comes from the initial request to the function whereas studentIds comes from firestore. My question is why is studentIds not being persisted correctly? Is there some kind of interaction between firebase collections I am not aware of?

The issue lied with this block of code:
await emails.forEach(async (email: string) => {
try {
// This should only ever return one result
const student = await usersRef.where('userEmail', '==', email).get();
if (student.empty) {
console.log(`No matching documents for email: ${email}`);
} else {
student.forEach((s) => {
const studentData = s.data();
console.log('found student: ', studentData);
// StudentIds stores reference to student objects
studentIds.push(studentData.playerUniqueID);
});
}
} catch (err) {
console.log('Error finding students: ', err);
throw new functions.https.HttpsError('internal', 'Error finding students');
}
});
As pointed out in the comments, my await in front of the forEach loop was doing nothing. Changing it to this fixed the issue:
const studentIds: string[] = await getStudentPlayerUniqueIds(emails);
...
const getStudentPlayerUniqueIds = async(emails: string[]): Promise<string[]> => {
const studentIds: string[] = []
try {
for (const email of emails) {
const student = await usersRef.where('userEmail', '==', email).get();
if (student.empty) {
console.log(`No matching documents for email: ${email}`);
} else {
student.forEach((s) => {
const studentData = s.data();
console.log('found student: ', studentData);
studentIds.push(studentData.playerUniqueID);
});
}
}
return studentIds;
} catch (err) {
console.log('Error finding students: ', err);
throw new functions.https.HttpsError('internal', 'Error finding students');
}
}

Related

array returned in response object is empty

I have started using typescript. As part of project, I am using multer and csvtojson npm modules to upload a csv file to application and then return the data from csv file into Mongo Database.
Able to push elements to arrays members and errors i.e., confirmed by console logging the length of array, but array is returning empty in response object
//controller to import members
exports.importMembers = async (req: Request, res: Response) => {
try {
uploadFile(req, res); //Fileupload method is return in below
}
catch (error: any) {
if(error) {
return res.status(400).json({
status_code: 400,
Error: error
});
}
}
};
//method to uplaod data
const uploadFile = async (req: Request, res: Response) => {
try {
//arrays to store errors occured and members saved from file
var errors:{}[] = [], members:{}[] = [];
let filePath : string;
// multer method
upload(req, res, (err: any) => {
if(!(req.file)) {
return res.status(400).json({
status_code: 400,
Message: "Please select file to be imported!",
})
}
else if (err) {
return res.status(400).json({
status_code: 400,
Info: `Error occurred while uploading file: ${err}`,
});
}
else {
filePath = path.join("upload", req.file?.originalname);
csvtojson().fromFile(filePath).then((source: any) => {
// Fetching all data from each row and return to member object
for (var i = 0; i < source.length; i++)
{
var member = {
memberEnrollmentID: source[i]["Member Enrollment ID"],
name: source[i]["Name"],
dateOfBirth: source[i]["Date Of Birth"],
IDproofNumber: source[i]["ID Proof Number"],
email: source[i]["Email"],
phoneNumber: source[i]["Phone Number"],
address: source[i]["Address"]
};
const newMember = new Members(member); //saving member to collection
newMember.save().then((result) => {
console.log(members.length); // returning array length
members.push(result); // push saved members
}).catch((error) => {
console.log(errors.length); // returning array length
errors.push(error); // push errors occured while saving members
})
}
console.log(members.length); // returning 0
console.log(errors.length); // returning 0
return res.status(200).json({
status_code: 200,
Members_Imported : members, // returning empty array
Members_Rejected : errors // returning empty array
});
});
}
});
} catch (error: any) {
throw error; // throw an error to be handled by the calling function
}
};
How could i return the members and errors array with elements stored in a express response object?

How to use sql returning id in front-end JavaScript?

I have this request in server.js file.
app.post("/insertRowtoMain", (req, res) => {
const {nodeid, maintenancetype, personnel, process, date} = req.body;
//console.log("description",description)
let insertQuery = `insert into maintenance(nodeid,maintenancetype, personnel, process, date)
values(${nodeid},'${maintenancetype}',${personnel},'${process}', '${date}') returning id`
pool.query(insertQuery, (err, result) => {
if (!err) {
console.log("insertRowtoMain", result.rows);
res.status(200).send(result.rows);
} else {
res.status(404).json(err.message)
console.log("insertRowtoMain error", err.message)
}
})
})
And I am calling this request function in front-end with this code:
const addNewMainTypes = async () => {
try {
await axios.post(`${serverBaseUrl}/insertRowtoMain`, {
nodeid: newMaintenance.nodeid,
maintenancetype: newMaintenance.maintenancetype,
personnel: newMaintenance.personnel,
process: newMaintenance.process,
date: newMaintenance.date,
});
} catch (err) {
throw err;
}
const maintenance = await getMain();
// console.log("main list", maintenanceList);
setMaintenance(maintenance);
const maintenanceList = await getMainTypes();
// console.log("main list", maintenanceList);
setMaintenanceList(maintenanceList);
};
When I insert a new row to this function, I got the returning id in server.js terminal.
How can I use that Id in front-end?
Save the response of the POST request in a variable and access the data property
// Here, "data" will be a variable with the response data
const { data } = await axios.post(`${serverBaseUrl}/insertRowtoMain`, {
nodeid: newMaintenance.nodeid,
maintenancetype: newMaintenance.maintenancetype,
personnel: newMaintenance.personnel,
process: newMaintenance.process,
date: newMaintenance.date,
});
/* Seems like your API is returning an array of objects with "id" property, so, for example... */
// The following should console.log the first element's id of the array
console.log(data[0]?.id);

Specifying a condition in payload sent in findByIdAndUpdate

Accounts model:
let accountsSchema = new mongoose.Schema({
posts:{
type: Array
}
});
const Post = mongoose.model("Account", postSchema);
controller in Posts microservice, which populates the posts array in the Accounts microservice:
let myAccountPosts = await axios.get(`${process.env.ACCOUNTS_MICROSERVICE_URL}/router/account/${userID}`)
.then((resp) => {
resp.data.message.map((account) =>{
account.posts.map((post) =>{
if(myUserPostID !== post._id){
//the following resets the entire posts array in the other microservice, consider resetting/deleting only that particular post
let deletePostFromAccountsMicroservice = axios.patch(`${process.env.ACCOUNTS_MICROSERVICE_URL}/router/account/update/${userID}`, {"posts.$[]": "abc"}, (err) =>{
if(err) return err;
});
}else{
console.log("no")
}
});
});
}).catch((err) =>{
console.log("err: ", err.message);
return err;
});
Now I need to be able to update a single object in the array and not override the whole array.
This is what the response looks like:
{
posts:[{_id:'6290ed85716a08d29dab3aa5'}, { _id: '1234ed85716a08d29b35342' } ]
}
In the following line of code:
let deletePostFromAccountsMicroservice = axios.patch(`${process.env.ACCOUNTS_MICROSERVICE_URL}/router/account/update/${userID}`, {"posts.$[]": "abc"}, (err) =>{
if(err) return err;
});
I want the posts.$[] to target only the
object that has an _id that matches this condition (myUserPostID !== post._id)
How do achieve that?

Delay in json data - Node, Express, MongoDB

I'm fairly new to nodejs and I'm doing a full stack developer challenge from devchallenges.io (Shoppingify). Below, I'm trying to add a new item. However, there's a slight delay between the return value from the request and the actual value in the database. The value updates straight away which is great however, the return value in the request is the previous value rather than being the current quantity value in the database.
// #route POST api/category
// #desc Add category and items
// #access Private
router.post(
'/',
[
check('name', 'Name is required').notEmpty(),
check('category', 'Category is required').notEmpty(),
],
auth,
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({
errors: errors.array(),
});
}
const { name, note, image, category } = req.body;
const itemObject = { name, note, image, category };
try {
const categoryItem = await Category.find({
user: req.user.id,
});
// check if category object are empty
if (categoryItem.length === 0) {
const newCat = new Category({
user: req.user.id,
name: category,
items: itemObject,
});
await newCat.save();
res.json(categoryItem);
} else if (categoryItem.length !== 0) {
// check if category name already exists
categoryItem.map(async (cat) => {
if (cat.name.toLowerCase() === category.toLowerCase()) {
cat.items.push(itemObject);
await cat.save();
res.json(categoryItem);
} else {
// create new category
const newCat = new Category({
user: req.user.id,
name: category,
items: itemObject,
});
await newCat.save();
res.json(categoryItem);
}
});
}
} catch (error) {
console.error(error.message);
res.status(500).send('Server Error');
}
}
);
You are not returning the correct item…
Return the result of newcat.save()
Or try a new findById if newCat is not the correct object to return

Node js returning values from function

I live in the PHP world but I am attempting to build out a REST Api using Node.
I have been stuck all day of trying to return an array of results from a for loop. Basically I am passing an array of field_name:field_value. I want to push the result from the update into an array to return. I can get it to log in the console but no further.
Here is a sample post json data
{
"first_name":"Jeff",
"phone":"4855555555"
}
Here is the function and loop
function UpdateKey(user_id, key, value, cb) {
connection.query('UPDATE users SET ' + key + ' = ? WHERE id = ? LIMIT 1', [value, user_id], function(err, results) {
if (err) {
callback = key + " update failed.";
} else {
callback = key + " was updated.";
}
cb(callback);
});
}
for (myKey in post_data) {
UpdateKey(user_id, myKey, post_data[myKey], function(id) {
console.log(id);
});
}
res.send(JSON.stringify({ "status": 200, "error": "", "response": my_results_here }));
I have been researching async but not sure the best route here. Any help would be great!
You could collect all results in an array and send that when the arrays size equals the keys size:
const keys = Object.keys(post_data);
const response = [];
for(const myKey of keys) {
UpdateKey(user_id, myKey, post_data[myKey], function(id) {
response.push(id);
if(keys.length === response.length) {
res.send(JSON.stringify({
status: 200,
error: "",
response
}));
}
});
}
The solution You want:
const updateUserField = (userId, field, value) => {
return Promise((resolve) => {
const query = 'UPDATE users SET ' + field + ' = ? WHERE id = ?';
const data = [value, userId];
connection.query(query, data, (error) => {
if (error) return resolve(field + ' update failed');
resolve(field + ' was updated');
});
});
};
router.post('/user/:id', async (req, res) => {
const userId = req.params.id;
const data = req.body;
const response = [];
for (const field in data) {
response.push(
await updateUserField(userId, field, data[field])
);
}
res.status(200).send({
response
});
});
or in parallel:
router.post('/user/:id', async (req, res) => {
const userId = req.params.id;
const data = req.body;
const response = await Promise.all(
Object
.keys(data)
.map(field => updateUserField(userId, field, data[field]))
);
res.status(200).send({
response
});
});
Correct solution
As I understand You want to get post data and update record in users table.
So why not just do it in one query?
Try this way:
const updateUser = (userId, data) => {
return Promise((resolve, reject) => {
const query = 'UPDATE users SET ? WHERE id = ?';
connection.query(query, [data, userId], (error) => {
if (error) return reject(error);
resolve();
});
});
};
router.post('/user/:id', async (req, res) => {
try {
const userId = req.params.id;
const data = req.body;
await updateUser(userId, data);
res.status(200).send({
message: 'User account successfully updated'
})
}
catch (error) {
console.error(error);
res.status(500).send({
message: 'Failed update user account'
});
}
});
But better think about using ORM i.e. Sequelize for security, validation and etc features that eases dev's life.

Categories

Resources