How to save object immediately in MongoDB? - node.js

In my Node application I use the mongoose module.
I found out that after
model_name.create({...})
object is not created immediately but with some delay (buffer?).
How can I force to save this new object in this particular moment?

Mongoose inserts the value inmediatly.
The problem is that you are not using it´s callback. Try to code this way:
//There is the definition
Customer.create(YOURDATA, function (err, obj) {
if (err) {
//This will be executed if create is going bad.
return handleError(res, err);
}
//This code will be executed AFTER SAVE
console.log(obj._id):
return res.json(201, obj);
});
Callback functions will be automatically executed by mongoose after the CREATE function has been executed in BD.
In obj, you will receive the saved object from DB, and then you will be able to access its properties.
Anyway, there you have some documentation:
the mongoose docs about callbacks
Some docs about Async methods:
Hope it will solve you problem

Related

Mongoose reacting differently to presence of callbacks

I was debugging a chunk of my Mongoose queries that I wanted to execute one after another to avoid inconsistency. After lot of trial, errors and tests, I finally stumbled upon a solution that seemed to work.
In one query, I include a query and in another, I skip including the callback function. The callback I am referring to is seen in the syntax of a Model.findByIdAndUpdate statement.
Model.findByIdAndUpdate(id, [update], [options], [***callback***])
The one which has a callback function executes perfectly and the one which doesn't, doesn't execute. It's encapsulated in a function which returns a mongoose promsise.
Here is the first query with the schema in Robomongo which adds an 'organiser' successfully:
function addUserToHackathonOrganisers(userId, hackathonId) {
return Hackathon.findOneAndUpdate(
{hackathonId: hackathonId},
{$addToSet: {organisers: userId.toString()}},
{new: true}
);
}
Notice organisers Array[0] which indicates no update.
Now, here is the query that works (with the callback in). In the attached image, you can see the organisers Array[1] which indicates that its updated.
function addUserToHackathonOrganisers(userId, hackathonId) {
return Hackathon.findOneAndUpdate(
{hackathonId: hackathonId},
{$addToSet: {organisers: userId.toString()}},
{new: true},
function (err) {
if (err) {
console.log(err);
}
}
);
}
I'm clueless why this happens. Any ideas?
It is because the findOneAndUpdate() method without a callback option returns a Query, it does not execute but the one with a callback executes hence why you see an update with the callback and none without.
From the docs:
A.findOneAndUpdate(conditions, update, options, callback) // executes
A.findOneAndUpdate(conditions, update, options) // returns Query
A.findOneAndUpdate(conditions, update, callback) // executes
A.findOneAndUpdate(conditions, update) // returns Query
A.findOneAndUpdate() // returns Query
If you had written the function as
function addUserToHackathonOrganisers(userId, hackathonId) {
return Hackathon.findOneAndUpdate(
{hackathonId: hackathonId},
{$addToSet: {organisers: userId.toString()}},
{new: true}
).exec(); // chaining exec() to a Query returns a Promise
}
then when you call it, it executes and returns a promise, e.g.
var promise = addUserToHackathonOrganisers(userId, hackathonId);
promise.then(function(addedUser){
console.log(addedUser); // shows the updated user
});
UPDATE
To address your follow-up question in the comments, the callback in the promise's then() method
promise.then(function(addedUser){
console.log(addedUser); // shows the updated user
});
is just the same as the callback in the findOneAndUpdate() option except that instead of two arguments, the promise handles the arguments separately with the error argument caught in the promise's catch() method:
promise.then(function(addedUser){
console.log(addedUser); // shows the updated user
})
.catch(function(err){
console.log(err);
});
For more details, refer to the docs here.

Using Mongodb variables out of its functions

So I'm making a web application and I'm trying to send variables to an EJS file but when they are sent out of the mongo functions they come out as undefined because it's a different scope for some reason. It's hard to explain so let me try to show you.
router.get("/", function(req, res){
var bookCount;
var userCount;
Books.count({}, function(err, stats){
if(err){
console.log("Books count failed to load.");
}else{
bookCount = stats;
}
});
User.count({}, function(err, count){
if(err){
console.log("User count failed to load.")
}else{
userCount = count;
console.log(userCount);
}
});
console.log(userCount);
//Get All books from DB
Books.find({}, function(err, allbooks){
if(err){
console.log("Problem getting all books");
}else{
res.render("index", {allbooks: allbooks, bookCount: bookCount, userCount: userCount});
}
});
});
So in the User.Count and Books.count I'm finding the number of documents in a collection which works and the number is stored inside of the variables declared at the very top.
After assigning the numbers like userCount i did console.log(userCount) which outputs the correct number which is 3, If was to do console.log(userCount) out of the User.count function it would return undefined, which is a reference to the declaration at the very top.
What is really weird is that Book.Find() has the correct userCount even though its a totally different function. The whole goal im trying to accomplish is doing res.render("index", {userCount: userCount}); outside of the Books.find(). I can do it but of course for some reason it passes undefined instead of 3. I hope this made a shred of sense.
I seem to have found a solution. but if anyone knows a different way I would love to know. So basically all you need to do is move the User.Count function outside of the router.get() function. Not completely sure about the logic of that but it works...
This is a classic asynchronous-operation problem: Your methods (Books.count, Books.find, User.count) are called immediately, but the callback functions you pass to them are not. userCount is undefined in your log because console.log is called before the assignment in the callback function is made. Your code is similar to:
var userCount;
setTimeout(function() {
userCount = 3;
}, 1000);
console.log(userCount); // undefined
User.count takes time to execute before calling back with the result, just like setTimeout takes the specified time to execute before calling its callback. The problem is JS doesn't pause and wait for the timeout to complete before moving on and calling console.log below it, it calls setTimeout, calls console.log immediately after, then the callback function is called one second later.
To render a complete view, you need to be sure you have all of the data before you call res.render. To do so you need to wait for all of the methods to call back before calling res.render. But wait, I just told you that JS doesn't pause and wait, so how can this be accomplished? Promise is the answer. Multiple promises, actually.
It looks like you are using Mongoose models. Mongoose has been written so that if you don't pass a callback function to your methods, they return a promise.
Books.count({}) // returns a promise
JS promises have a method then which takes a callback function that is called when the promise has been resolved with the value of the asynchronous method call.
Books.count({}) // takes some time
.then(function(bookCount) { // called when Books.count is done
// use the bookCount here
})
The problem is, you want to wait for multiple operations to complete, and multiple promises, before continuing. Luckily JS has a utility just for this purpose:
Promise.all( // wait for all of these operations to finish before calling the callback
Books.count({}),
User.count({}),
Books.find({})
)
.then(function(array) { // all done!
// the results are in an array
bookCount = array[0];
userC0unt = array[1];
allBooks = array[2];
})

How to return promise to the router callback in NodeJS/ExpressJS

I am new to nodejs/expressjs and mongodb. I am trying to create an API that exposes data to my mobile app that I am trying to build using Ionic framework.
I have a route setup like this
router.get('/api/jobs', (req, res) => {
JobModel.getAllJobsAsync().then((jobs) => res.json(jobs)); //IS THIS THe CORRECT WAY?
});
I have a function in my model that reads data from Mongodb. I am using the Bluebird promise library to convert my model functions to return promises.
const JobModel = Promise.promisifyAll(require('../models/Job'));
My function in the model
static getAllJobs(cb) {
MongoClient.connectAsync(utils.getConnectionString()).then((db) => {
const jobs = db.collection('jobs');
jobs.find().toArray((err, jobs) => {
if(err) {
return cb(err);
}
return cb(null, jobs);
});
});
}
The promisifyAll(myModule) converts this function to return a promise.
What I am not sure is,
If this is the correct approach for returning data to the route callback function from my model?
Is this efficient?
Using promisifyAll is slow? Since it loops through all functions in the module and creates a copy of the function with Async as suffix that now returns a promise. When does it actually run? This is a more generic question related to node require statements. See next point.
When do all require statements run? When I start the nodejs server? Or when I make a call to the api?
Your basic structure is more-or-less correct, although your use of Promise.promisifyAll seems awkward to me. The basic issue for me (and it's not really a problem - your code looks like it will work) is that you're mixing and matching promise-based and callback-based asynchronous code. Which, as I said, should still work, but I would prefer to stick to one as much as possible.
If your model class is your code (and not some library written by someone else), you could easily rewrite it to use promises directly, instead of writing it for callbacks and then using Promise.promisifyAll to wrap it.
Here's how I would approach the getAllJobs method:
static getAllJobs() {
// connect to the Mongo server
return MongoClient.connectAsync(utils.getConnectionString())
// ...then do something with the collection
.then((db) => {
// get the collection of jobs
const jobs = db.collection('jobs');
// I'm not that familiar with Mongo - I'm going to assume that
// the call to `jobs.find().toArray()` is asynchronous and only
// available in the "callback flavored" form.
// returning a new Promise here (in the `then` block) allows you
// to add the results of the asynchronous call to the chain of
// `then` handlers. The promise will be resolved (or rejected)
// when the results of the `job().find().toArray()` method are
// known
return new Promise((resolve, reject) => {
jobs.find().toArray((err, jobs) => {
if(err) {
reject(err);
}
resolve(jobs);
});
});
});
}
This version of getAllJobs returns a promise which you can chain then and catch handlers to. For example:
JobModel.getAllJobs()
.then((jobs) => {
// this is the object passed into the `resolve` call in the callback
// above. Do something interesting with it, like
res.json(jobs);
})
.catch((err) => {
// this is the error passed into the call to `reject` above
});
Admittedly, this is very similar to the code you have above. The only difference is that I dispensed with the use of Promise.promisifyAll - if you're writing the code yourself & you want to use promises, then do it yourself.
One important note: it's a good idea to include a catch handler. If you don't, your error will be swallowed up and disappear, and you'll be left wondering why your code is not working. Even if you don't think you'll need it, just write a catch handler that dumps it to console.log. You'll be glad you did!

Where need it call dropCollection using nodejs with mongodb?

I was doing a server using nodejs, it need get data from mongodb. I retrieve data after require(../db.js). Somebody said the mongodb needn't be close in nodejs,because nodejs is a single process.....
My question: Need I call dropCollection to close the collection after invoked the db function many times;and How to do?Where to do that? Please,Thanks.
You dont need to drop the collection after invoking db functions,simply call db.close() though it is not needed. But if you want to do it , you can do it as follows:
var dropRestaurants = function(db, callback) {
db.collection('restaurants').drop( function(err, response) {
console.log(response)
callback();
});
};

Having trouble understanding how the pre() function in Mongoose is working in this code snippet

I am learning how to make web apps with node.js. I have been following the tutorial provided by Alex Young. I am having trouble understanding how the pre() function works in Mongoose. I have read the Mongoose API documentation and understand that it is a way of "chaining" functions to an existing one, but I do not understand how it is actually working when I look at a code example (see below code snippets).
My question is what is going on in the here? There are three functions involved here (userSaved(), userSaveFailed(), and the password validation function defined in the pre() function). How are these function related to each other in terms of order in which they run? Is the actual saving of the document into the database completed before userSaved() and userSavedFail() are run?
I admit that my lack of understanding understanding may be due to my lack of knowledge on javascript (I come from a PHP background), but I just can't follow what is going on in this code.
Modified save behavior define in models.js :
User.pre('save', function(next) {
if (!validatePresenceOf(this.password)) {
// Through error if password fails validation.
next(new Error('Invalid password'));
}
else {
next();
}
});
Call to save data to database from app.js :
app.post('/users.:format?', function(req, res) {
var user = new User(req.body.user);
function userSaved() {
switch (req.params.format) {
case 'json':
res.send(user.__doc);
break;
default:
req.session.user_id = user.id;
res.redirect('/documents');
}
}
function userSaveFailed() {
res.render('users/new.jade', {
locals: { user: user }
});
}
user.save(userSaved, userSaveFailed);
});
In my opinion,the function followed by "save", is the method which gets executed before the save function is called(as the function name "PRE" imply).
The first function that is ran is user.save() passing in two callbacks, one for if user.save() completes without errors (userSaved) and another if it fails (userSavedFailed)
User.pre('save', function(next) {
if (!validatePresenceOf(this.password)) {
// Through error if password fails validation.
next(new Error('Invalid password'));
}
else {
next();
}
});
This code is running a set of asynchronous functions in parallel, only returning a response once all the functions have completed. In this case it returns by calling a callback function (userSaved or UserSavedFailed). Which is called depends on whether there was an error during the process of any of the functions.
The Async Module also chains functions and allows them to run synchronously or in parallel and may provide some examples of how this is accomplished to help you better understand what's actually happening.

Resources