MongoDB : does Collection.Find() support promise - node.js

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

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.

How to return item from .exec in mongoose, console.log in exec returns element but outside return undefined

I have asked this question .exec callback using returns is returning undefined, but console.log do have item in it
The answer does work, but it throws .exec away, I have been trying to re-implement it using .exec, however when I console.log something inside the .exec, it does print.
The reason I have been trying to use the .exec way is that a lot of StackOverflow questions use a lot .exec and I think ".exec" will be faster?
When I return it, it returns undefined. What is the cause and how can I solve it while keeping the .exec
let user = await User.findById(paymentBody.user_id)
.populate(User.schedule_table)
.exec((err, foundDocument) => {
console.log(foundDocuments)
return foundDocument;
});
console.log(user)
The code inside .exec (console.log(foundDocuments)), returns the result of query.
However, when I perform console.log(user) after the query, it shows undefined.
You are mixing await and callbacks. Remove the callback and see if it works.

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.

Monk - Where goes the data after a query?

I have started using Monk today, and there are a few things that I don't really get, and documentation is too light.
First here is the code:
const movieToProcess = movieCollection.findOne({ link: videoURL }).then((doc) => {
console.log(doc)
console.log("BLABLA")
});
console.log("CURSOR", typeof(movieToProcess))
First thing, I don't understand why the two console.log inside the promise .then() are not displaying, is that normal? If so, why?
And if this is not normal that the console.logs don't work, why is that?
And finally, in then, how can I get the return value of findOne()?
Bonus: Is there another function than findOne() to check if the value exist in the database?
I apologise for these questions, but there are not that much documentation for Monk.
A few things:
In your example you are setting movieToProcess to the value of movieCollection.findOne() while also calling .then() on it.
in your .then, doc is the return value of findOne()
ALSO, referring to #Geert-Jan's comment, the promise is probably being rejected and you aren't catching it.
Try this:
movieCollection.findOne({ link: videoURL })
.then((doc) => {
console.log(doc)
console.log("BLABLA")
})
.catch((err) => {
console.log(err)
})
I'll also add that findOne() does not return a cursor, it returns a document.

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