Meteor - no more callbacks for "findOne" function - mongodb

i'm working on a Meteor project, and I must say that isn't easy at all, especially for one thing: callbacks !
Everything is async, so I wonder how do I must do to get results from my mongodb.
var user = Meteor.users.findOne({username: "john"});
return (user); // sometimes returns "undefined"
...
var user = Meteor.users.findOne({username: "john"});
if (user) // so ok, I check if it exists!
return (user); // Cool, I got my user!
return (); // Ok and what should I return here? I want my user!
I don't want to be dirty and put like setTimeout everywhere.
Anybody has a solution for this ?
EDIT :
I noticed in router.js with console.log that my data is returned 4 times. 2 times with an undefined value and 2 other times with the expected value. In the view, it's still undefined.
Why the router passes like 4 times in this route ? Does it display the first result of the return value in the router ?
What should I return if the find() doesn't find anything ?
EDIT 2: Here is some code to understand.
this.route('profilePage', {
path: 'profil/:_id?',
waitOn: function() {
return [
Meteor.subscribe('article', { prop: this.params._id}), // id can be id or username
Meteor.subscribe('article', { userId: this.params._id}), // id can be id or username
Meteor.subscribe('params'),
Meteor.subscribe('profil', (this.params._id ? this.params._id : Meteor.userId()))
];
},
data: function() {
if (this.params._id) {
var user = Meteor.users.findOne(this.params._id);
if (!user)
user = Meteor.users.findOne({username: this.params._id});
console.log(user);
return user;
}
else if (Meteor.userId())
return Meteor.user();
else
Router.go("userCreate");
}
});
I get this on the console:
http://puu.sh/debdJ/69419911f7.png
(text version following)
undefined
undefined
Object_id: "o3mgLcechYTtHPELh"addresses: (....)
Object_id: "o3mgLcechYTtHPELh"addresses: (....)

findOne(yourId) is a sync method which is equivalent to find({ _id: yourId}, callback). The difference is that find() allows you to define a callback. If you don't pass a callback to find() this method will be sync.
check wrapAsync: http://docs.meteor.com/#/full/meteor_wrapasync
It allows you to code in a sync style with a async operations.
Free lesson on EventedMind: https://www.eventedmind.com/feed/meteor-meteor-wrapasync

My experience thus far is that the Meteor Mongodb package is that the functions do not generally provide callbacks (for some reason insert does...), the functions are atomic (thus sync).
There are meteor packages that can make Mongodb async if you want (I havn't tried any).
I guess this sync approach is in line with the simple maintenance goal of Mongodb. Thinking about it, one of my pet peeves using Node is working with async callback waterfalls/nests, they are a pain to create and maintain... and hopefully this will make my code easier to read and understand and change...

var future = new Future();
var _h = Hunts.findOne({huntId});
if(_h) {
future.return(_h)
} else {
return future.wait();
}
on server/startup.js you need:
Future = Npm.require('fibers/future');

Related

How mongoose middleware works and what is next()?

userSchema.pre('save',async function(next){
//hash the password before saving user to database
next()
})
Hey guys I'm trying to understand the concept of middleware in mongoose. Assuming that I have an userSchema that I run the pre hook on to hash the password before saving the user to the database. On the surface, as far as I understand, the above code that I have will hash the password (not the important part for this question) and then call next() to signal that the function is done. However, I am trying to understand how things work under the hood. I want to know what is next() ? Can you guys walk me through an example of how everything works together under the hood from start to end once the code get executed or somehow help me to have a better understanding of this concept? Thanks
Short : with the pre method you can register listeners for certain events of your Schemas. So pre('save', callback) will fire whenever you save a document of said Model. pre means it will execute before the event, so it can be used (for example) to hash a password before saving it to the document.
However, you have several options to define them, see below :
The combination of using an async callback function and providing the next parameter is not necessary, you can either :
use normal callback with next parameter
the next parameter is a function provided to you by mongoose to have a way out, or to tell mongoose you are done and to continue with the next step in the execution chain. Also it is possible to pass an Error to next it will stop the execution chain.
schema.pre('save', function(next) {
// do stuff
if (error) { return next(new Error("something went wrong"); }
return next(null);
});
use async callback
Here the execution chain will continue once your async callback has finished. If there is an error and you want to break/stop execution chain you just throw it
schema.pre('save', async function() {
// do stuff
await doStuff()
await doMoreStuff()
if (error) { throw new Error("something went wrong"); }
return;
});
Straight from the docs : https://mongoosejs.com/docs/middleware.html#pre
Example
const { Schema, model } = require('mongoose');
const SomeSchema = new Schema ({
name : { type : String }
});
SomeSchema.pre('save', function (next) {
console.log('pre save fired!');
return next();
});
const Some = model('Some', SomeSchema);
console.log('before create');
const doc = new Some({ name : 'test' });
doc.save((err) => {
console.log('after saved');
});
This will output
before create
pre save fired!
after saved

Returning multiple server operations in Meteor in one call. How to?

I've very new to this but essentially I want to create a cleanup function that runs on the server that I can call at any time to reset various things like collections and sessions in one call.
I'm really very new but this is what I have so far. Can someone please help fill me in where I'm going wrong?
I am trying essentially to return two things (and many more in the future) at once. I've done some research on this but it's as far as I can fathom with my skill level at the moment.
It would be much appreciated. Thank you.
if (Meteor.isServer) {
Meteor.startup(function () {
// code to run on server at startup
return Meteor.methods({
//Use this to emplty the form data
cleanUpForms: function() {
var cleanUpPhoneNumbers = orgPhoneNumbers.remove({});
var cleanUpEmailAddresses = orgEmailAddresses.remove({});
return {
cleanUpPhoneNumbers : cleanUpPhoneNumbers;
cleanUpEmailAddresses : cleanUpEmailAddresses;
}
}
});
});
}
By the way, the current error is for line :
cleanUpPhoneNumbers : cleanUpPhoneNumbers;
It states:
Unexpected token
I'm not sure if I'm doing this correctly. I essentially want it to run multiple cleanups in one go, all called from the client to the server with the above method. I hope that makes sense.
The unexpected token is likely for the ; at the end of the line. When building a JSON object, use a comma between the elements...
return {
cleanUpPhoneNumbers : cleanUpPhoneNumbers,
cleanUpEmailAddresses : cleanUpEmailAddresses
}
I think this will return the number of items that where removed. Is that what you are expecting?
Also, just in case you didn't know, you can run 'meteor reset' from the command line to erase ALL collections.
This is the fully adjusted code for any future reference which may help others. Thanks so much to FloatingCoder for the help.
if (Meteor.isServer) {
Meteor.startup(function () {
// code to run on server at startup
return Meteor.methods({
removeAllNewOrgs: function() {
var PhoneNumbers = newOrgPhoneNumbers.remove({});
var Organsations = newOrgansations.remove({});
//If we want to return the data, to get around only being able to return one thing at a time we're return via an array. CLEVS!
return {
PhoneNumbers : PhoneNumbers,
Organsations : Organsations
}
}
});
});
}

High frequency calls leads to duplicates with findOrCreate in Waterline & Sails

How to handle high frequency updateOrCreate requests with Waterline in Sails for a Postgresql database ?
I tried to use findOrCreate and then update the item, I tried findOne and then update or create the item, I tried to put a beforeCreate, a beforeValidation hook method to check if the item exists but without any success.
Should I add an error handler to get errors from the unique index and try again?
In the Waterline docs, there is a warning about it but no direction to solve this problem.
Thank you for any tips.
Should I add an error handler to get errors from the unique index and try again?
That's going to be the only option until such time as Waterline implements transactions. Something like:
// This will hold the found or created user
var user;
// Keep repeating until we find or create a user, or get an error we dont expect
async.doUntil(
function findOrCreate(cb) {
// Try findOrCreate
User.findOrCreate(criteria, values).exec(function(err, _user) {
// If we get an error that is not a uniqueness error on the
// attribute we expect collisions on, bail out of the doUntil
if (err &&
(
!err.invalidAttributes["myUniqueAttribute"] ||
!_.find(err.invalidAttributes["myUniqueAttribute"], {rule: 'unique'})
)
) {
return cb(err);
}
// Otherwise set the user var
// It may still be undefined if a uniqueness error occurred;
// this will just cause doUntil to run this function again
else {
user = _user;
return cb();
}
},
// If we have a user, we are done. Otherwise go again.
function test() {return user},
// We are done!
function done(err) {
if (err) {return res.serverError(err);}
// "user" now contains the found or created user
}
});
Not the prettiest, but it should do the trick.

Meteor onRendered function and access to Collections

When user refresh a certain page, I want to set some initial values from the mongoDB database.
I tried using the onRendered method, which in the documentation states will run when the template that it is run on is inserted into the DOM. However, the database is not available at that instance?
When I try to access the database from the function:
Template.scienceMC.onRendered(function() {
var currentRad = radiationCollection.find().fetch()[0].rad;
}
I get the following error messages:
Exception from Tracker afterFlush function:
TypeError: Cannot read property 'rad' of undefined
However, when I run the line radiationCollection.find().fetch()[0].rad; in the console I can access the value?
How can I make sure that the copy of the mongoDB is available?
The best way for me was to use the waitOn function in the router. Thanks to #David Weldon for the tip.
Router.route('/templateName', {
waitOn: function () {
return Meteor.subscribe('collectionName');
},
action: function () {
// render all templates and regions for this route
this.render();
}
});
You need to setup a proper publication (it seems you did) and subscribe in the route parameters. If you want to make sure that you effectively have your data in the onRendered function, you need to add an extra step.
Here is an example of how to make it in your route definition:
this.templateController = RouteController.extend({
template: "YourTemplate",
action: function() {
if(this.isReady()) { this.render(); } else { this.render("yourTemplate"); this.render("loading");}
/*ACTION_FUNCTION*/
},
isReady: function() {
var subs = [
Meteor.subscribe("yoursubscription1"),
Meteor.subscribe("yoursubscription2")
];
var ready = true;
_.each(subs, function(sub) {
if(!sub.ready())
ready = false;
});
return ready;
},
data: function() {
return {
params: this.params || {}, //if you have params
yourData: radiationCollection.find()
};
}
});
In this example you get,in the onRendered function, your data both using this.data.yourData or radiationCollection.find()
EDIT: as #David Weldon stated in comment, you could also use an easier alternative: waitOn
I can't see your collection, so I can't guarantee that rad is a key in your collection, that said I believe your problem is that you collection isn't available yet. As #David Weldon says, you need to guard or wait on your subscription to be available (remember it has to load).
What I do in ironrouter is this:
data:function(){
var currentRad = radiationCollection.find().fetch()[0].rad;
if (typeof currentRad != 'undefined') {
// if typeof currentRad is not undefined
return currentRad;
}
}

How to return Mongoose results from the find method?

Everything I can find for rending a page with mongoose results says to do it like this:
users.find({}, function(err, docs){
res.render('profile/profile', {
users: docs
});
});
How could I return the results from the query, more like this?
var a_users = users.find({}); //non-working example
So that I could get multiple results to publish on the page?
like:
/* non working example */
var a_users = users.find({});
var a_articles = articles.find({});
res.render('profile/profile', {
users: a_users
, articles: a_articles
});
Can this be done?
You're trying to force a synchronous paradigm. Just does't work. node.js is single threaded, for the most part -- when io is done, the execution context is yielded. Signaling is managed with a callback. What this means is that you either have nested callbacks, named functions, or a flow control library to make things nicer looking.
https://github.com/caolan/async#parallel
async.parallel([
function(cb){
users.find({}, cb);
},
function(cb){
articles.find({}, cb);
}
], function(results){
// results contains both users and articles
});
I'll play the necromancer here, as I still see another, better way to do it.
Using wonderful promise library Bluebird and its promisifyAll() method:
var Promise = require('bluebird');
var mongoose = require('mongoose');
Promise.promisifyAll(mongoose); // key part - promisification
var users, articles; // load mongoose models "users" and "articles" here
Promise.props({
users: users.find().execAsync(),
articles: articles.find().execAsync()
})
.then(function(results) {
res.render('profile/profile', results);
})
.catch(function(err) {
res.send(500); // oops - we're even handling errors!
});
Key parts are as follows:
Promise.promisifyAll(mongoose);
Makes all mongoose (and its models) methods available as functions returning promises, with Async suffix (.exec() becomes .execAsync(), and so on). .promisifyAll() method is nearly-universal in Node.JS world - you can use it on anything providing asynchronous functions taking in callback as their last argument.
Promise.props({
users: users.find().execAsync(),
articles: articles.find().execAsync()
})
.props() bluebird method takes in object with promises as its properties, and returns collective promise that gets resolved when both database queries (here - promises) return their results. Resolved value is our results object in the final function:
results.users - users found in the database by mongoose
results.articles - articles found in the database by mongoose (d'uh)
As you can see, we are not even getting near to the indentation callback hell. Both database queries are executed in parallel - no need for one of them to wait for the other. Code is short and readable - practically corresponding in length and complexity (or rather lack of it) to wishful "non-working example" posted in the question itself.
Promises are cool. Use them.
The easy way:
var userModel = mongoose.model('users');
var articleModel = mongoose.model('articles');
userModel.find({}, function (err, db_users) {
if(err) {/*error!!!*/}
articleModel.find({}, function (err, db_articles) {
if(err) {/*error!!!*/}
res.render('profile/profile', {
users: db_users,
articles: db_articles
});
});
});
Practically every function is asynchronous in Node.js. So is Mongoose's find. And if you want to call it serially you should use something like Slide library.
But in your case I think the easiest way is to nest callbacks (this allows f.e. quering articles for selected previously users) or do it completly parallel with help of async libraries (see Flow control / Async goodies).
I have a function that I use quite a bit as a return to Node functions.
function freturn (value, callback){
if(callback){
return callback(value);
}
return value;
};
Then I have an optional callback parameter in all of the signatures.
I was dealing with a very similar thing but using socket.io and DB access from a client. My find was throwing the contents of my DB back to the client before the database had a chance to get the data... So for what it's worth I will share my findings here:
My function for retrieving the DB:
//Read Boards - complete DB
var readBoards = function() {
var callback = function() {
return function(error, data) {
if(error) {
console.log("Error: " + error);
}
console.log("Boards from Server (fct): " + data);
}
};
return boards.find({}, callback());
};
My socket event listener:
socket.on('getBoards', function() {
var query = dbConnection.readBoards();
var promise = query.exec();
promise.addBack(function (err, boards) {
if(err)
console.log("Error: " + err);
socket.emit('onGetBoards', boards);
});
});
So to solve the problem we use the promise that mongoose gives us and then once we have received the data from the DB my socket emits it back to the client...
For what its worth...
You achieve the desired result by the following code. Hope this will help you.
var async = require('async');
// custom imports
var User = require('../models/user');
var Article = require('../models/article');
var List1Objects = User.find({});
var List2Objects = Article.find({});
var resourcesStack = {
usersList: List1Objects.exec.bind(List1Objects),
articlesList: List2Objects.exec.bind(List2Objects),
};
async.parallel(resourcesStack, function (error, resultSet){
if (error) {
res.status(500).send(error);
return;
}
res.render('home', resultSet);
});