how to use mongo db.eval through Mongoose - node.js

From my mongo shell I can run functions with db.eval like:
db.eval('return 7;');
And, but for a deprecation warning, the parameter function is run properly.
Now I want to call such a 'script' from node.js, through mongoose.
I've tried to just call db.eval with the stringified function code as parameter (as in here):
mongoose.connect(PATH_TO_A_MONGO_DATABASE);
mongoose.connection.db.eval('return 9;', function(err, result){
console.log(result);
//etc.
});
This silently ignores the callback. No error is thrown, and the callback function is never called.
When doing this, I checked that mongoose.connection.db.eval is actually a function.
Since db.eval also has a Promise interface, also tried:
mongoose.connection.db.eval('return 5;').then(console.log).catch(console.log);
With the same result: no errors, just silence.
So, I guess I'm missing something with the syntax, but I can't find documentation or examples about this. Any similar questions like this or this didn't help.
PD1: I'm willing to do this because I need to call a procedure stored in system.js through mongoose. I can't do that too. However, I can't even run a silly script like return 5;, so I'm asking for the simpler task before. Any advice on how to call server scripts with mongoose is welcome.
PD2: I'm aware stored server scripts in mongo are a bad practice, and that they are deprecated for a reason and so on... but I can't just decide about that. I've been told to do that at my company, and the co-worker who set up the original code of the stored server script is not here now.

Ok, I figured out why my db.eval callbacks was being ignored. It is related with the mongoose connection.
If you start a connection like:
const conn = mongoose.connect(PATH_TO_DB);
And then just make a query:
conn.model(a_collection, a_schema).find({}).exec().then()...
Even if you just call the query right after the connection -it is, logically, an asynchronous process-, mongooose figures that it has to wait to the connection to be in a proper state, then fire the query, then call the callback with the query results.
However, this doesn't work the same way with db.eval(). just trying to call db.eval() right after the call to the connection, the callback is silently ignored:
const conn = mongoose.connect(PATH_TO_DB);
conn.db.eval('return 5;', (err, response) => {
//nothing happends
})
That was the way I was creating the connection and calling db.eval, so I couldn't get db.eval() to work.
In order to fire a db.eval and make it work, it seems that you need to wait for the connection explicitly:
const conn = mongoose.connect(PATH_TO_DB, err => {
mongoose.connection.db.eval('return 5', (err, result) => {
result; //5!
})
});
To make a query before any attemps to call db.eval also works:
const conn = mongoose.connect(PATH_TO_DB);
conn.model(a_collection, a_schema).find({}).exec()
.then(() => {
conn.db.eval('return 5', (err, result) => {
result; //5!
})
})

Related

Node.js: mongoose.once('open') doesn't execute callback function

I'm trying to save some json files inside my database using a custom function I've wrote. To achieve that I must connect to the database which I'm trying to do using this piece of code at the start of the function:
let url = "mongodb://localhost:27017/database";
(async () => {
const directory = await fs.promises.readdir(__dirname + '/files')
let database = await mongoose.createConnection(url, {useNewUrlParser:true, useUnifiedTopology:true});
database.on('error', error => {
throw console.log("Couldn't Connect To The Database");
});
database.once('open', function() {
//Saving the data using Schema and save();
Weirdly enough, when executing database.once('open', function()) the callback function isn't being called at all and the program just skips the whole saving part and gets right to the end of the function.
I've searched the web for a solution, and one solution suggested to use mongoose.createConnection instant of mongoose.connect.
As you can see it didn't really fixed the issue and the callback function is still not being called.
How can I fix it, and why it happens?
Thanks!
mongoose.createConnection creates a connection instance and allows you to manage multiple db connections as the documentation states. In your case using connect() should be sufficient (connect will create one default connection which is accessible under mongoose.connection).
By awaiting the connect-promise you don't actually need to listen for the open event, you can simply do:
...
await mongoose.connect(url, {useNewUrlParser:true, useUnifiedTopology:true});
mongoose.model('YourModel', YourModelSchema);
...

How to console.log a promisified mongoose query *without* bluebird

I am trying to run a mongoose query and write it to the console without installing yet another library like bluebird. I have been unable to find this in the documentation.
Here is the query:
function activityH(){
return setRecords.find({'item.title': 'marzipan'}
, 'item.title item.quantity').exec();
}
And the context in which I am calling it:
....[a bunch of promises].then(activityF).then(activityG).then(activityH).then(function(doc){console.log(doc);});
All of the prior activities are completing, but nothing is logging even though my query should have results. I feel this is a very basic question but I have looked for hours trying to find a solution and if this is a duplicate, the original answer is very hard to search for!
Do I absolutely need bluebird to do this? E.g. this blog post
Thank you
You could write a little logging function wrapper to help you out. Something like
function log(data) {
console.log(data);
return data;
}
And then add it your Promise chain.
....[a bunch of promises]
.then(log)
.then(activityF)
.then(log)
.then(activityG)
.then(log)
.then(activityH)
.then(log)
If you want some default messaging you could also pass a message string
function log(msg) {
return function(data) {
console.log(msg, data);
return data;
}
}
And then would add to the chain like:
activityA()
.then(log('activityA'))
.then(activityB)
.then(log('activityB'))

Unable to understand why the try and catch is not working as expected in mongoose

I am new to mongoose.I am using Sails js, Mongo DB and Mongoose in my project. My basic requirement was to find details of all the users from my user collection. My code is as follows:
try{
user.find().exec(function(err,userData){
if(err){
//Capture the error in JSON format
}else{
// Return users in JSON format
}
});
}
catch(err){
// Error Handling
}
Here user is a model which contains all the user details. I had sails lifted my app and then I closed my MongoDB connection. I ran the API on DHC and found the following:
When I ran the API for the first time on DHC, the API took more than 30 sec to show me an error that the MongoDB connection is not avaliable.
When I ran the API for the second time, The API timed out without giving an response.
My Question here why is the try and catch block unable to handle such an error exception effectively in mongoose or is it something that I am doing wrong?
EDIT
My Requirement is that mongoose should display the error immediately if the DB connection is not present.
First let’s take a look at a function that uses a synchronous usage pattern.
// Synchronous usage example
var result = syncFn({ num: 1 });
// do the next thing
When the function syncFn is executed the function executes in sequence until the function
returns and you’re free to do the next thing. In reality, synchronous functions should be
wrapped in a try/catch. For example the code above should be written like this:
// Synchronous usage example
var result;
try {
result = syncFn({ num: 1 });
// it worked
// do the next thing
} catch (e) {
// it failed
}
Now let’s take a look at an asynchronous function usage pattern.
// Asynchronous usage example
asyncFn({ num: 1 }, function (err, result) {
if (err) {
// it failed
return;
}
// it worked
// do the next thing
});
When we execute asyncFn we pass it two arguments. The first argument is the criteria to be used by the function. The second argument is a callback that will execute whenever asyncFn calls the callback. asyncFn will insert two arguments in the callback – err and result). We
can use the two arguments to handle errors and do stuff with the result.
The distinction here is that with the asynchronous pattern we do the next thing within the callback of the asynchronous function. And really that’s it.

Execute a server-side function from MongoClient in node.js

I've created a set of functions on my MongoDB server that I'd like to be able to use from MongoClient in my nodejs scripts. All of the documentation I've read tells me how to do this, but from the shell only it seems.
How it would normally work in the shell:
mongo database my.script.js
mongo
> use database
> db.loadServerScripts()
> add("This is a string taken in by the add function I just loaded")
This is what I've tried/looked into (mind the CoffeeScript):
MongoClient = require('mongodb').MongoClient
MongoClient.connect 'mongodb://127.0.0.1:27017/database', (e, db) ->
console.log db.eval #Function, but not sure what to call with
console.log db.runCommand #undefined
console.log db.loadServerScripts #undefined
console.log db.load #undefined
console.log db.command #Function, but not sure what to call with
console.log db.add #this is one of my custom functions
Hopefully this is possible with MongoClient. It seems as though I could use eval() if I can manage to load the script, but that's proving to be the difficult part so far. Alternatively, I suppose I could minify my functions and run those through eval(), but I'd prefer not to do that.
You can use eval (it looks like you don't need to load any functions first, the load command is only to make the functions available from the shell):
db.eval('add("This is a string taken in by the add function I just loaded")', function(err, result) {
...
});
Or, if you want to be a bit more flexible in passing arguments:
db.eval('function(x) { return add(x); }', [ ARG ], function(err, result) { ... });
(it seems that you can't just use 'add' as first argument, you need to wrap it in an anonymous function first, but I might be missing something...)

mongodb crashed node with exception within try catch

try
p = req.params.name
Item.update('name': p, req.body , {upsert: true}, (err) ->
if err?
throw err
res.send("ok")
)
catch e
handle_error(e, "Error salvando hoja de vida.", res)
This produces an error in my code right now - that's alright, but why does my nodejs program crash even if I have a try catch here?
The error is:
MongoError: Mod on _id not allowed
(so it must be in the update call)
I am specifically looking for a way to catch an error, I already know how to get rid of it.
Ah yes, you have entered the realm of asynchronous code. The reason you pass a callback into your MongoDB calls like Item.update(..., callback) is because the MongoDB driver is written to be asynchronous. Consider this (mongoose code):
var user = User.findOne({ name: 'joe' });
doSomethingElse();
If it had structured its API like the above code then your entire application would halt until User.findOne() returned the results from the database. Your doSomethingElse() call wouldn't be invoked until the user was retrieved, even if you don't do anything with user inside doSomethingElse. This is a big no no these days and especially in node which has been written to be asynchronous as much as possible. Have a look at the following:
User.findOne({ name: 'joe' }, function (err, user) {
if (err) return console.error(err);
console.log('Do stuff with user... ;)');
});
doSomethingElse();
The above code is asynchronous. The findOne function returns immediately and doSomethingElse is able to begin even before the user is ever retrieved from the database. But of course we still want to do stuff with our user, so in order to accomplish this we pass an anonymous function in to be used as a callback. The MongoDB driver is smart enough to call that function when it's all done retrieving data.
Wrapping the above code in a try/catch would be pointless, unless you suspected the findOne function to throw an exception (Which you shouldn't. It returns immediately remember?).
try {
User.findOne({ name: 'joe' }, function (err, user) {
throw new Error("Something went wrong...");
});
} catch (err) {
console.error(err);
}
The above error would still crash your program because it happened long after findOne returned and your program moved on way beyond that precious try/catch. This is the very reason why your callback function receives an err argument. The MongoDB driver knows that if some error occurs while fetching your data it would be no good to throw an exception and call it good. This is because all this processing has happened later on subsequent trips through the event loop, having left your try/catch behind several iterations ago.
To get around this the MongoDB driver wraps its own internal synchronous code in a try/catch where it actually will handle the exception and then it invokes your callback passing in the error as the first argument. This allows you to check that argument and handle any errors within your callback.
I wrote an entire blog post explaining callbacks, as well as another way of handling asynchronous code called "promises" and I think it would help clarify some things for you :) http://codetunnel.io/what-are-callbacks-and-promises
I hope that helps answer your question.
Maybe I'm wrong (because I don't know Coffeescript), but I suppose the problem is, that your try/catch is outside the callback function. The catch doesn't affect your err -> so the exception is not caught by you, but by node.js.
If I'm wrong, could you perhaps provide the compiled JavaScript code?
Edit
Usually one would handle the error more like this (without try/catch):
Item.update('name': p, req.body , {upsert: true}, (err) ->
if err?
log.error('error updating Item name=' + name)
res.status(500).end()
return
res.send("ok")
)
Probably your handle_error will do this already.
When programming in node.js, I use try/catch only for JSON.parse or if I have a lot of unchecked input which might have wrong types or might be null and I'm too lazy to check all input manually. Surrounding asynchronous functions with try/catch is only helpful if the function has programming errors (e.g. not checking your input correctly).

Resources