if statement in node.js with with some mongoose query - node.js

Im trying to write an if statement in Node.js with some mongoose query but the if statement does not get executed correctly.
So I'm doing this:
app.get('Job/GetJobs', function(req,res){
if(JobDB.Find()==null){
req.render('home.html');
}
else req.render('Job.html')
})
But the above code works if it was in java but not in Node.js because req.render('home.html'); gets executed before JobDB.find() finishes.

Most functions in Node.js are asynchronous, so you need to use a callback:
app.get('Job/GetJobs', function(req,res){
JobDB.find({}, function(err, result) {
if(!result) req.render('home.html')
else req.render('Job.html')
})
})
(you'll have to include some error handling there)
See the documentation: http://mongoosejs.com/docs/2.7.x/docs/finding-documents.html

Related

MongooseError: Query was already executed

I updated Mongoose to the latest version (6.0.1) and now I'm getting this error whenever .findOne() is executed:
MongooseError: Query was already executed: Streams.findOne({ i: 6 })
at model.Query._wrappedThunk [as _findOne] (C:\Users\honza\ZiggerTestMaster\node_modules\mongoose\lib\helpers\query\wrapThunk.js:21:19)
at C:\Users\honza\ZiggerTestMaster\node_modules\kareem\index.js:370:33
at processTicksAndRejections (node:internal/process/task_queues:78:11)
at runNextTicks (node:internal/process/task_queues:65:3)
at listOnTimeout (node:internal/timers:526:9)
at processTimers (node:internal/timers:500:7) {
originalStack: 'Error\n' +
' at model.Query._wrappedThunk [as _findOne] (C:\\Users\\honza\\ZiggerTestMaster\\node_modules\\mongoose\\lib\\helpers\\query\\wrapThunk.js:25:28)\n' +
' at C:\\Users\\honza\\ZiggerTestMaster\\node_modules\\kareem\\index.js:370:33\n' +
' at processTicksAndRejections (node:internal/process/task_queues:78:11)\n' +
' at runNextTicks (node:internal/process/task_queues:65:3)\n' +
' at listOnTimeout (node:internal/timers:526:9)\n' +
' at processTimers (node:internal/timers:500:7)'
}
My code is as follows:
var visitorData = Visitor.find({});
app.get("/retrieve", function(req,res){
visitorData.exec(function (err,data) {
if (err) {
throw err;
}
res.render("retrieve", { title:"View Visitor Data", records: data});
});
});
It executes properly the first time I open the route but, whenever I refresh it, it throws the above error. Has been happening since Mongoose got the latest version.
Had the same issues, but the release notes on mongoose helped, I chained a .clone() method to the .find() method:
https://mongoosejs.com/docs/migrating_to_6.html#duplicate-query-execution
Mongoose no longer allows executing the same query object twice. If
you do, you'll get a Query was already executed error. Executing the
same query instance twice is typically indicative of mixing callbacks
and promises, but if you need to execute the same query twice, you can
call Query#clone() to clone the query and re-execute it.
So all you need to do is to add a .clone() method to the end of the mongoose method that needs to be called concurrently like below (I restructured your code a bit):
app.get("/retrieve", function (req, res) {
Visitor.find({}, function (err, data) {
if (!err) {
res.render("retrieve", { title: "View Visitor Data", records: data });
} else {
throw err;
}
}).clone().catch(function(err){ console.log(err)})
});
wrap your query within async-await.
const visitorData = async() => {
return await Visitor.find({});
}
Don't use the callback within the findOne, instead
use .then() , after the Query, like so
Visitor.find({})
.then(function (err, data) {
if (!err) {
res.render("retrieve", { title: "View Visitor Data", records: data });
} else {
throw err;
}
}
This should most likely work , otherwise use clone() if necessary , as stated by
Avis
The error predominantly arises because the same query is executed more than once, the most often cause being unknowingly using promise and callback at the same time, to avoid such mistakes this error is thrown.
To get rid of this error, stick to either promise or callback when executing this query method.
Use:
await Model.findOneAndUpdate(query,doc,options)
// (or) regular .then() .catch() syntax
Model.findOneAndUpdate(query)
.then(res=>...)
.catch(err=>...)
(OR)
Model.findOneAndUpdate(query, doc, options, callback)
The same principle applies to similar methods like findByIdAndUpdate.
Mongoose v6 does not allow duplicate queries.
Mongoose no longer allows executing the same query object twice. If you do, you'll get a Query was already executed error. Executing the same query instance twice is typically indicative of mixing callbacks and promises, but if you need to execute the same query twice, you can call Query#clone() to clone the query and re-execute it. See gh-7398
Duplicate Query Execution
In my case, I assumed my Apollo Server resolver will wait for the promise to resolve and return the result, but that wasn't the case. So I had to add async/await to the mongoose query.
I'm not sure about the details but mine and my co-workers problems got fixed by downgrading mongoose to 5.13.14
Mongoose no longer allows executing the same query object twice. If you do, you'll get a Query was already executed error. Executing the same query instance twice is typically indicative of mixing callbacks and promises, but if you need to execute the same query twice, you can call ".clone();" to clone the query and re-execute it.
// Mongoose 6.6.3
// Query.prototype.clone()
// Returns:
// «Query» copy
// Make a copy of this query so you can re-execute it.
// Example:`enter code here`
const q = Book.findOne({ title: 'Casino Royale' });
await q.exec();
await q.exec();
// Throws an error because you can't execute a query
twice
await q.clone().exec(); // Works
My mistake was using find() for finding a record like this:
foo.find(a=> a.id === id) //It is wrong!
It should be:
foo.findById(id)
The newer version of Mongoose (version 6+) doesn't support executing the same query object twice. So, just downgrade to an older version, like version 5.13.15.
Uninstall the current Mongoose package & then download the older version:
npm uninstall mongoose
npm i mongoose#5
Hope, this can resolve the issue.

Using async-await to execute a task AFTER dropping a collection from mongodb database

I am trying to use async await to execute an http request before executing some other code.
More precisely, I would like to drop a collection in my mongodb database, before executing some others tasks. Here's what I did:
app.component.ts:
async deleteRiskRatingData2() {
await this.saveInformationService
.deleteRiskRatingInformation()
.subscribe((data: string) => {
console.log('Deleting risk Rating');
console.log(this.riskRatingTable);
});
console.log('TASKS TO BE EXECUTED AFTER DROPIING COLLECTION');
}
save-information.service.ts
deleteRiskRatingInformation() {
console.log('INIDE deleteRiskRatingInformation INSIDE SAVE-INFORMATION.SERVICE');
return this.http.get(`${this.uri}/dropRiskRatingCollection`);
}
In the backend:
server.js
router.route('/dropRiskRatingCollection').get((req, res) => {
RiskRating.remove({},(err) => {
if (err)
console.log(err);
else
res.json("Risk Rating Collection has been dropped!");
});
});
And this is what happens:
I though my implementation of Async/Await should allow me to execute the:
console.log('TASKS TO BE EXECUTED AFTER DROPPING COLLECTION');
After the dropping of the collection request has been executed. But that didn't happen as you see. And I really don't understand why.
Any idea why is this happening? Is my logic flawed somewhere? And how can I achieve my goal?
Thank you!
async-await work only with Promises. You're try them with Observables. That won't work. Observables have an API that let's you convert them into Promises though. You can call a toPromise method on them in order to do that.
Try this:
async deleteRiskRatingData2() {
const data = await this.saveInformationService.deleteRiskRatingInformation().toPromise();
console.log('Deleting risk Rating');
console.log(this.riskRatingTable);
console.log('TASKS TO BE EXECUTED AFTER DROPIING COLLECTION');
}
NOTE: It's fine if you're trying this just for the sake of testing it. But I think you should not really switch back to promises just to use async-await, to make your code look synchronous.

MongoDB : does Collection.Find() support promise

I was trying to revamp an old Nodejs web service to replace callback functions with chained promises.
When querying mongodb we used the syntax below to iterate through a result set.
collection.find(filter).toArray(function(err, items) {
if (err) {
throw(err);
} else {
console.log(items);
}
If I try to replace the .toArray() section with a .then() I get the below error "col.find(...).then is not a function".
If I replace .find() with .findOne().then(), the code works perfectly.
Any help is appreciated.
find returns a Cursor, but the cursor's toArray method returns a promise. So you can do:
collection.find(filter).toArray().then(...)
I assume you're using mongoosejs.
collection.find() is just a query. To cause it to execute and return a promise, you need to call .exec() on it.:
collection.find(filter).exec()
.then(items => console.log(items))
.catch(err => { // handle error })
The mongoose docs give you more details on how to use mongoose with either callbacks or promises: https://mongoosejs.com/docs/api.html#model_Model.find

Returning variable value in sailsJS

I am trying to write a controller in sailsjs
module.exports = {
someFunction: function(req, res){
var userID = req.param('id')
User.findUserName(userID, function(err, receiverName){
if(err){
res.serverError(err);
}else{
ReceiverName = receiverName;
}
});
console.log("#####################################");
console.log(ReceiverName);
console.log("#####################################");
.
.
.
.
But when I deploy the controller, restart the sails app and call this function through the URL, I am getting the following error on the console:
error: Sending 500 ("Server Error") response:
ReferenceError: ReceiverName is not defined
I tried to declare the variable ReceiverName to be global, but still cant sort it out. I need guidance on making it global so that I assign value to it inside the else part and use it anywhere inside the controller.
Note 1:
However, when I am printing the value of the variable on console right inside the else block using the code:
User.findUserName(userID, function(err, receiverName){
if(err){
res.serverError(err);
}else{
console.log("#####################################");
console.log(receiverName);
console.log("#####################################");
}
});
the value is printed perfectly.
Note 2:
I am already making asynchronous call to retrieve the receiverName, so it is not a doubt about returning the response from asynchronous calls. I am looking for method to store the response in a variable that can be used further.
Your Note 2 is a little bit confusing so I'm not sure if you understand that the reason it does not work in your first example is because the User.findUserName() has not finished running before you execute your console.log().
Remember, the database calls are async. So any values you want to use from that database call have to run after it finishes. The only way to ensure that is to make sure your code runs inside the callback.
That is why the second example works. The code that needed the receiverName variable ran inside the callback. Try this, it is pretty much the same thing as your second example ...
User.findUserName(userID, function(err, receiverName){
if(err){
res.serverError(err);
}else{
doStuffWith(receiverName)
}
});
var dostuffWith = function(ReceiverName){
// all the code that you want to use ReceiverName should be in here.
console.log("#####################################");
console.log(ReceiverName);
console.log("#####################################");
}
You should do some reading on programming patterns with Node.js, async and callbacks. Like this
http://book.mixu.net/node/ch7.html

node.js sails.js waterline bluebird .then and .spread

I am trying to figure out how promises work in sails, and have been successful passing data from waterline queries via .then, but have not been able to utilize .spread. I am getting a function not defined error. Any suggestions how the first section of code can be improved to work?
//results in error
Promise.all([Xyz.find(), Abc.find()]).spread(function (someOtherResult, yetAnotherResult) {
console.log(someOtherResult)
}).catch(function (err) {
console.log(err);
})
The following work but would be either trickier to extract data from, or require excessively long nested .then clauses:
Promise.all([Xyz.find(), Abc.find()]).then(function (results) {
console.log(results[0][1]);
console.log(results[0].length);
})
Abc.find().then(function (foundAbcs) {
Promise.all(Xyz.find().then(function (foundXyzs) {
console.log(foundAbcs);
console.log(foundXyzs);
// additional syncranouse logic with Abc and Xyz
}))
})
Okay, very simple mistake, I didn't realize I needed:
var Promise = require('bluebird');
prior to module.exports in sails.js .11, problem solved.

Resources