mongoose Query was already executed - node.js

I was trying to learn a MERN stack beginner tutorials and i encountered this problem the query was already executed I'm getting this error whenever .findOne() is executed:
MongooseError: Query was already executed: elders.findOne({ _id: new ObjectId("636caae0e3c24f1df7b4e6b7...
router.put("/updateElders", async (req,res)=>{
const newAge = req.body.newAge
const id = req.body.id
try{
await EldersModel.findById(id, (error, eldertoUpdate) =>{
eldertoUpdate.age = Number (newAge);
eldertoUpdate.save();
})
}catch(err){console.log(err);}

You should decide on whether to use callbacks or promises. You chose to use await (used for awaiting promises) and callbacks (your second parameter passed to findById. You should decide on which one to use, e.g.:
router.put("/updateElders", async (req,res)=>{
const newAge = req.body.newAge
const id = req.body.id
try{
const eldertoUpdate = await EldersModel.findById(id);
eldertoUpdate.age = Number(newAge);
await eldertoUpdate.save();
}catch(err){
console.log(err);
}
this code uses promises returned from findById and save calls and awaits those.

Related

mongoose findOne : different between async & sync

I have confuse with know what is different between :
var users = Users.findOne({key})
var users = await Users.findOne({key})
var users = Users.findOne({key})
Users.findOne() method returns a promise which can either be resolved or rejected. See promises
So in above case users variable actually contains a promise object and if you want to get the resolved data you have to do something like
users.then(data=>{
// your db query data
}).catch(err=>{
// error if something goes wrong
})
The below syntax with await actually gives you the resolved data of promise see await
var users = await Users.findOne({key})
so you will have data like this [{key:value}] of your mongodb stored in users variable.
Also await is valid only in async function so you need to wrap it inside async function
async function foo(){
var users = await Users.findOne({key})
}

Google Cloud Function not working properly with firestore .get() - NODE JS

I'm a novice web developer. I'm currently working on Firebase for a blog project.
This is my DB structure:
On my home page, I'm using this code to get the some latest posts from each of my category
app.get('/test', (req, res) => {
async function getDocument(db) {
const ex = await db.collection('Posts').where('category', '==', 'Exercise').orderBy("artID", "asc").limit(4).get();
const hl = await db.collection('Posts').where('category', '==', 'Health').orderBy("artID", "asc").limit(2).get();
const fl = await db.collection('Posts').where('category', '==', 'Food & Lifestyle').orderBy("artID", "asc").limit(3).get();
const md = await db.collection('Posts').where('category', '==', 'Mindfulness').orderBy("artID", "asc").limit(2).get();
// const author = await db.collection('authors').doc(doc.data().author).get();
return {
ex: ex.data(),
hl: hl.data(),
fl: fl.data(),
md: md.data()
}
}
getDocument(db).then(function (data) {
res.send(data);
})
})
I've checked the code, and also made sure that the query is indexed in firestore.
But when I execute the function, the browser is throwing an error:
Error: could not handle the request
and I get this error logged in my console.
TypeError: ex.data is not a function
at getDocument (/workspace/index.js:69:14)
at process._tickCallback (internal/process/next_tick.js:68:7)
To get data from Firestore, placing a '.data()' function is needed.
Can somebody help me with where I'm typing the code wrong
I think the response of await db.collection('Posts').where('category', '==', 'Exercise').orderBy("artID", "asc").limit(4).get(); is a QuerySnapshot - the filter will match more then one value and this is why you cannot access data() because there is more then one object.
You should do something like ex.docs.map(doc => ({ id: doc.id, ...doc.data() }))
As per #stefan-prokop-cz mentioned, you need to map each document snapshot from ex.docs. Also, you might wanna consider using Promise.all() to run these queries in parallel to improve computation time.

Loop through an array to generate a new array. It returns 0 at the end of the loop

There are 4900 books in the database. I expect to have the same number of books in the new array at the end of the loop. However, I am getting a zero length for the booksArray. What could be the problem and possible solution?
const getOverview = async(req, res) => {
const books = await Book.find();
const booksArray = new Array();
books.forEach(book => {
const url =`https://www.goodreads.com/book/isbn/${book.isbn}key=${process.env.KEY}`;
request.get(url).then(result => {
parseString(result, (error, goodReadsResult) => {
const goodreadsBook = goodReadsResult.GoodreadsResponse.book[0];
booksArray.push(goodreadsBook);
})
});
})
console.log(booksArray.length);
};
First a bit of an overview. Node.js does all networking as non-blocking and asynchronous. That means when you make a networking request, it starts the operation and immediately returns and continues executing the rest of your code. A callback or promise associated with that asynchronous operation gets called sometime later.
In your specific case, your entire .forEach() loop runs to completion starting all the networking requests in it before any of them have finished. Thus you are doing console.log(booksArray.length); before any of your callbacks have run and thus the array is still empty.
Any time you want to sequence or coordinate more than one asynchronous operation in node.js you will want to use promises. It's been built into the language for a couple years now and it's just a massively better way to program with asynchronous operations. I would suggest you find some good tutorials on how promises work and learn them.
Then, the library you are using request() is old and has been put in maintenance mode and does not support promises. There are a variety of more modern alternatives. I personally use the got() library and that's what I've illustrated below.
Then, I don't know what your parseString() function is, but it apparently doesn't use promises and appears to be asynchronous so, in order to use it in a promise-based workflow, I "promisified" it using util.promisify() which is built-into node.js. If it has a promise interface, you should just use that directly.
So anyway, here's what I would suggest:
const got = require('got');
const {promisify} = require('util');
const ps = promisify(parseString);
// this function returns a promise
async function getOverview(req, res) {
const books = await Book.find();
const booksArray = await Promise.all(books.map(book => {
const url =`https://www.goodreads.com/book/isbn/${book.isbn}key=${process.env.KEY}`;
let bookData = await got(url);
return ps(bookData);
}));
console.log(booksArray.length);
// make the booksArray be the resolved value of the promise returned
// by this async function
return booksArray;
}
This makes all your networking calls in parallel and then uses Promise.all() to indicate when they are all done and to collect all the results in order.
If, for some reason, you can't make all these requests in parallel (like there are so many that the target server objects), then you can run them one at a time too like this:
const got = require('got');
const {promisify} = require('util');
const ps = promisify(parseString);
// this function returns a promise
async function getOverview(req, res) {
const books = await Book.find();
const booksArray = [];
for (let book of books) {
const url =`https://www.goodreads.com/book/isbn/${book.isbn}key=${process.env.KEY}`;
let bookData = await got(url);
let data = await ps(bookData);
booksArray.push(data);
}
console.log(booksArray.length);
// make the booksArray be the resolved value of the promise returned
// by this async function
return booksArray;
}
And, since these are async functions, they return a promise so that caller must use await or .then() to get the value from the promise.
getOverview(...).then(results => {
console.log(results);
}).catch(err => {
console.log(err);
});
If you actually intend to pass req and res into that function and use them there, then you will probably need local error handling too so if any of your await operations reject, you can catch that error and send an error response. You would do that with try/catch inside the function body. You can probably just use one top level try/catch and then send an error response inside the catch handler.

How to push an object into an array in async function

i have been trying to insert an object into an array in async function ,but it
return an empty array as output in nodejs ,mongoose
var data = [];
app.get("/api/post", async (req, res) => {
const post = await UserPost.find();
post.forEach(async element => {
const email = await element.userid;
const user = await Account.find({ email });
const usern = await user[0].username;
var userobject = {
element,
usern
};
//Promise.all(userobject)
data.push(userobject);
});
console.log(data);
res.send({ data });
});
It seems you are struggling with promises. In order to achieve this specific scenario, you can use Promise.all and Array.map.
Here is a code I edited for you:
(*please note that this is just a dummy code for the sake of explanation)
app.get("/api/post", async (req, res) => {
try {
const posts = await dummyPromiseResolver(); // first promise
const promises = posts.map(async element => {
const user = await dummyEmailReturn(element.userid); // second promise
const usern = user[0].username;
return {
usern,
...element
};
});
const fresult = await Promise.all(promises);
res.send(fresult);
} catch (error) {
console.error("error in posts fetch:" + error);
}
});
If I describe this code, posts.map is creating an Array of promises since we need to iterate through every object in the array and needs to add values from separate promises.
Then Promise.all can execute your promise array and return final results array with your desired results.
Note: You can also use for … of as well but when we need to happen things parallelly we use Promise.all. You can find more information from this thread.
here is a link for code sandbox: https://codesandbox.io/embed/serverless-cookies-nu4h0
Please note that I have added dummyPromiseResolver and dummyEmailReturn which would be equal to UserPost.find() and Account.find() functions respectively. In addition to that, I removed a few unnecessary awaits in your code. I added a try catch block to catch any exceptions. You can change that try catch as you please.
hope this will help you. let me know if you need more clarifications.

How to update a list of users in Firestore using Firebase admin sdk?

So I'm learning how to use the firebase admin sdk and I've come across a problem that I'm not understanding enough to solve. Basically, I've queried my database and gotten a subset of users that I would like to update with the same value. So I stored the id's of each user that I want to update in an array, now I am trying to loop through that array and update each user. But I'm having a problem implementing this part and it has to do with the fact that .update() returns a promise and I'm not 100% how to handle this in a loop...
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp()
exports.getAllUsers = functions.https.onRequest((request, response) => {
const db = admin.firestore()
const users = db.collection('users').where('role', '==', 'artist').get()
.then(function (querySnapshot) {
//const users = querySnapshot.data()
const promises = []
querySnapshot.forEach(function (doc){
promises.push(doc)
})
return Promise.all(promises) //returning promises sends the resolved results to
}) //to the next .then()
.then(function (person){
let results = []
person.forEach(function(personSnap){
//const data = personSnap.data()
results.push(personSnap.id)
})
return results // results is the array of ids to update
})
//This .then() is where I have trouble understanding how to update
.then(function (ids){
for(var i = 0; i<ids.length; i++){
var artistsRef = db.collection('users').doc(ids[i]);
artistsRef.update({ //Should not be a return statement. This fixed my problem. Otherwise it would update the first user in the array and then leave the for loop.
'free_credits': '2'
})
}
return 'finished'
})
.then(function(reply) {
return response.send(reply)
})
.catch(function (error){
response.send(error)
})
})
I feel like I should be returning 'artistsRef.update()' since it is a promise but I think that leads to unexpected results.
If I don't return the update() then ESLint throws the error: "Each then() should return a value or throw"
You're returning early out of the final then() callback, before a response is sent. Maybe you should add another then() just for sending the result after whatever final updates are complete.

Resources