Mongoose - function inside .find method does not pass result - node.js

How do I pass the result of the function successfully into .find method ? I want the argument passed via body-parser to be used as value for the key 'name' if user submitted a non-empty string. If user submitted an empty string, then empty object should be passed into .find method.
Function console.log works. If I submit an empty string then 'empty querydb' is printed to the console. Likewise, if I submit any other string then it gets printed out to the console correctly.
How can I pass the req.body.querydb as a value to 'name' key ?
HTML
<form method="POST" action="form_query">
<input type="textarea" name="querydb">
<br><br>
<button> Query </button>
</form>
.JS
router.post('/form_query', function(req,res) {
someModel.find(function(err,results){
if(!req.body.querydb){
console.log("empty querydb")
return {}
} else {
console.log(req.body.querydb)
return {name:req.body.querydb}
}
}).exec(function(err, result) {
if (err) {
send(err)
} else {
console.log('omg')
}
Much thanks !

In mongoose you have two possibilities to make a find request
the find(query, callback) function
the query builder
I recommand you to use the find() function. In your case, your code is like more this :
router.post('/form_query', function(req,res, next) {
someModel.find({
name : req.body.querydb
}, function (err, users) {
if (err) {
// request has an error like connection problem
res({ error : err });
return;
}
// request works correctly, you can send the users array for example
res(users);
});
});
The find function works like the mongodb pure find() function and, for me, it's the best way to make your requests.
EDIT :
It is advisable to control the incoming variables in the best way to perform this find.
For example, You can make this :
let findQuery = {};
if (req.body.querydb) {
findQuery.name = req.body.querydb
}
someModel.find(findQuery, function (err, users) {
// ...
});

I am not really sure what you are looking for but trying to understand as much as possible i am giving some code. This way you pass req.body.querydb as a value to 'name' key.
router.post('/form_query', function(req,res) {
someModel.find({name:req.body.querydb},function(err,results){
if(!req.body.querydb){
console.log("empty querydb")
return {}
} else {
console.log(req.body.querydb)
return {name:req.body.querydb}
}
}).exec(function(err, result) {
if (err) {
send(err)
} else {
console.log('omg')
}

From what I can tell you are looking to pass a value from the api into a mongo query.
It might make sense to define the query outside of the actual call and then build it as needed or to ensure that a value is passed before getting to the query.
For example:
router.post('/form_query', function(req,res) {
if(!req.body.querydb) {
return res.status(404).send("empty querydb");
}
// validate querydb param
someModel.find({ name: req.body.querydb }, function(err,results){
if(!req.body.querydb){
console.log("empty querydb")
res.send
} else {
console.log(req.body.querydb)
return res.status(200).send(results);
}
})
});
If you are using mongoose then the callback will be called after execution, and the query is the first param passed into the .find function.

Related

Pull mongo data to ejs template

I have the following basic document in mongo:
connecting to: test
> db.car.find();
{ "_id" : ObjectId("5657c6acf4175001ccfd0ea8"), "make" : "VW" }
I am using express, mongodb native client (not mongoose) and ejs.
collection.find().toArray(function (err, result) {
if (err) {
console.log(err);
} else if (result.length) {
console.log('Found:', result);
mk = result;
console.log('mk = ', mk);
} else {
console.log('No document(s) found with defined "find" criteria!');
}
//Close connection
db.close();
});
}
});
Here is the render code:
// index page
app.get('/', function(req, res) {
res.render('pages/index', { make: result });
});
And i want to pass the data make: VW into my index.ejs file:
<h2>Cars:</h2>
<h3>My favorite make is <%= make %>
This should be real simple and straightforward but i just don't understand how to pass the "mk" variable (which i can console.log to the screen) to be rendered by the ejs view?
You should use the find method inside the route (or with a callback) to get the result and render it:
app.get('/', function(req, res) {
collection.find().toArray(function(err, result) {
if(err) {
console.log(err);
res.status('400').send({error: err});
} else if(result.length) {
console.log('Found:', result);
mk = result;
console.log('mk = ', mk);
res.render('pages/index', {make: mk});
} else {
console.log('No document(s) found with defined "find" criteria!');
res.status('400').send({error: 'No document(s) found'});
}
//Close connection
db.close();
});
});
Very simple, your are outputting an array of JSON objects
Here is one way to visualise it:
[{a:b},{a:b},{a:b},{a:b}];
If you want the first result it would be array[0].a
so you simply need to call it this way:
<%= make[0].(your database key here) =>
//example
<%= make[0].name =>
A more sensible way to get this done would be to iterate through the array and output only the result you want on the server side. If you send all your data to your client you might have security issues depending on what you send.
It should be simple, but you are defining mk just like this: mk = result So because you want to pass a variable to an ejs file you need
"var mk = result"
Have a good day, Ben.

rendering results of multiple DB/mongoose queries to a view in express.js

given the async nature of mongoose (or sequelize, or redis) queries, what do you do when you have multiple queries you need to make before rendering the view?
For instance, you have a user_id in a session, and want to retrieve some info about that particular user via findOne. But you also want to display a list of recently logged in users.
exports.index = function (req, res) {
var current_user = null
Player.find({last_logged_in : today()}).exec(function(err, players) {
if (err) return res.render('500');
if (req.session.user_id) {
Player.findOne({_id : req.session.user_id}).exec(function(err, player) {
if (err) return;
if (player) {
current_user = player
}
})
}
// here, current_user isn't populated until the callback fires
res.render('game/index', { title: 'Battle!',
players: players,
game_is_full: (players.length >= 6),
current_user: current_user
});
});
};
So res.render is in the first query callback, fine. But what about waiting on the response from findOne to see if we know this user? It is only called conditionally, so I can't put render inside the inner callback, unless I duplicate it for either condition. Not pretty.
I can think of some workarounds -
make it really async and use AJAX on the client side to get the current user's profile. But this seems like more work than it's worth.
use Q and promises to wait on the resolution of the findOne query before rendering. But in a way, this would be like forcing blocking to make the response wait on my operation. Doesn't seem right.
use a middleware function to get the current user info. This seems cleaner, makes the query reusable. However I'm not sure how to go about it or if it would still manifest the same problem.
Of course, in a more extreme case, if you have a dozen queries to make, things might get ugly. So, what is the usual pattern given this type of requirement?
Yep, this is a particularly annoying case in async code. What you can do is to put the code you'd have to duplicate into a local function to keep it DRY:
exports.index = function (req, res) {
var current_user = null
Player.find({last_logged_in : today()}).exec(function(err, players) {
if (err) return res.render('500');
function render() {
res.render('game/index', { title: 'Battle!',
players: players,
game_is_full: (players.length >= 6),
current_user: current_user
});
}
if (req.session.user_id) {
Player.findOne({_id : req.session.user_id}).exec(function(err, player) {
if (err) return;
if (player) {
current_user = player
}
render();
})
} else {
render();
}
});
};
However, looking at what you're doing here, you'll probably need to look up the current player information in multiple request handlers, so in that case you're better off using middleware.
Something like:
exports.loadUser = function (req, res, next) {
if (req.session.user_id) {
Player.findOne({_id : req.session.user_id}).exec(function(err, player) {
if (err) return;
if (player) {
req.player = player
}
next();
})
} else {
next();
}
}
Then you'd configure your routes to call loadUser wherever you need req.player populated and the route handler can just pull the player details right from there.
router.get("/",function(req,res){
var locals = {};
var userId = req.params.userId;
async.parallel([
//Load user Data
function(callback) {
mongoOp.User.find({},function(err,user){
if (err) return callback(err);
locals.user = user;
callback();
});
},
//Load posts Data
function(callback) {
mongoOp.Post.find({},function(err,posts){
if (err) return callback(err);
locals.posts = posts;
callback();
});
}
], function(err) { //This function gets called after the two tasks have called their "task callbacks"
if (err) return next(err); //If an error occurred, we let express handle it by calling the `next` function
//Here `locals` will be an object with `user` and `posts` keys
//Example: `locals = {user: ..., posts: [...]}`
res.render('index.ejs', {userdata: locals.user,postdata: locals.posts})
});
Nowadays you can use app.param in ExpressJS to easily establish middleware that loads needed data based on the name of parameters in the request URL.
http://expressjs.com/4x/api.html#app.param

function not worked synchronously in nodejs?

I am little bit confused with my code it's not worked synchronusly as it's should be. I use everyauth to do authentication.
registerUser(function(newUserAttrs) {
var login = newUserAttrs[this.loginKey()];
user.CreateNewUser(newUserAttrs.login, newUserAttrs.password, newUserAttrs.email, function(res, err) {
if(!err) {
return usersByLogin[login] = newUserAttrs;
}
else {
throw err;
}
});
})
in another file I have write this code
exports.CreateNewUser = function(login, pass, mail, callback) {
var sql = "insert into `user`(login,mail,pass) values(?,?,?)";
client.query(sql, [login, mail, pass], function(err, results, fields) {
if(!err) {
callback(results);
console.log('test')
}
else {
callback(results, err);
}
});
};
This code are working fine. I have tested him. the only problem is they are working synchronosly (as normal). Can someone explain me what thing I have done in wrong way that make it async. I want to get it done in sync way.
The current code give me error (it's make a entry in database and produce error on browser)
Error: Step registerUser of `password` is promising: userOrErrors ; however, the step returns nothing. Fix the step by returning the expected values OR by returning a Promise that promises said values.
at Object.exec (E:\proj\Node\node_modules\everyauth\lib\step.js:68:11)
at E:\proj\Node\node_modules\everyauth\lib\stepSequence.js:26:38
at [object Object].callback (E:\proj\Node\node_modules\everyauth\lib\promise.js:13:12)
at RouteTriggeredSequence._bind (E:\proj\Node\node_modules\everyauth\lib\stepSequence.js:25:20)
at RouteTriggeredSequence.start (E:\proj\Node\node_modules\everyauth\lib\stepSequence.js:52:33)
at RouteTriggeredSequence.routeHandler (E:\proj\Node\node_modules\everyauth\lib\routeTriggeredSequence.js:13:13)
at Object.<anonymous> (native)
at nextMiddleware (E:\proj\Node\node_modules\connect\lib\middleware\router.js:175:25)
at param (E:\proj\Node\node_modules\connect\lib\middleware\router.js:183:16)
at pass (E:\proj\Node\node_modules\connect\lib\middleware\router.js:191:10)
Thanks
The two pieces of code you present are asynchronous and not synchronous!
With everyauth, to be able to handle asynchronous user creation you should use a Promise. So your code will be something like :
registerUser(function(newUserAttrs) {
var promise = this.Promise();
var login = newUserAttrs[this.loginKey()];
user.CreateNewUser(newUserAttrs.login, newUserAttrs.password, newUserAttrs.email, function(res, err) {
if(!err) {
return promise.fulfill(newUserAttrs);
}
else {
promise.fulfill(user);
}
});
})
Without promise you couldn't be sure that your new user has been added in your database. But if it doesn't matter you could have something like that:
registerUser(function(newUserAttrs) {
var login = newUserAttrs[this.loginKey()];
user.CreateNewUser(newUserAttrs.login, newUserAttrs.password, newUserAttrs.email, function(res, err) {
if (err) console.log(err);
});
return newUserAttrs;
})
Because you are doing a database query, this code has to be asynchronous. The anonymous function you pass to client.query will not be called until the database query is complete, so your callback gets called asynchronously.
You will need to treat this all as asynchronous, so for instance you'll have to trigger some other callback instead of returning the user object/throwing.

NodeJS + Mongoose: Updating all fields on a Mongoose model

I'm building out an api using Node, MongoDB and Mongoose. One thing that is bugging me is that you can't seem to set multiple fields at once:
app.put('/record/:id', function(req, res) {
Record.findById(req.params.id, function(err, doc) {
if (!err) {
doc.update(req.params);
doc.save();
...
However, it seems that you have to work out the update query and run it on the Model object rather than on the document object. Unless you want to assign individual properties and run save() at the end.
Is there any way of accomplishing this without having to write a Mongo query?
jsaak's answer is good but doesn't work for nested objects. I elaborated on his answer by searching and setting nested objects.
I added these functions to a utility.js file
var _ = require('underscore');
exports.updateDocument = function(doc, SchemaTarget, data) {
for (var field in SchemaTarget.schema.paths) {
if ((field !== '_id') && (field !== '__v')) {
var newValue = getObjValue(field, data);
console.log('data[' + field + '] = ' + newValue);
if (newValue !== undefined) {
setObjValue(field, doc, newValue);
}
}
}
return doc;
};
function getObjValue(field, data) {
return _.reduce(field.split("."), function(obj, f) {
if(obj) return obj[f];
}, data);
}
function setObjValue(field, data, value) {
var fieldArr = field.split('.');
return _.reduce(fieldArr, function(o, f, i) {
if(i == fieldArr.length-1) {
o[f] = value;
} else {
if(!o[f]) o[f] = {};
}
return o[f];
}, data);
}
implement as:
var util = require('./utility');
app.put('/record/:id', function(req, res) {
Record.findById(req.params.id, function(err, doc) {
if (!err) {
utils.updateDocument(doc, Record, req.params);
doc.save();
...
Maybe this has changed since this question was first asked, but you can update multiple paths in Mongoose with the set method ike:
// object
doc.set({
path : value,
path2 : {
path : value
}
});
doc.save();
References
http://mongoosejs.com/docs/api.html#document_Document-set
direct updating is not recommended according to this document:
http://mongoosejs.com/docs/2.7.x/docs/updating-documents.html
i solved it like this:
Book.findOne({isbn: req.params.isbn}, function (err, book){
if (err) {
res.send(422,'update failed');
} else {
//update fields
for (var field in Book.schema.paths) {
if ((field !== '_id') && (field !== '__v')) {
if (req.body[field] !== undefined) {
book[field] = req.body[field];
}
}
}
book.save();
}
});
If you want to update the entire document , you can delete the document based on its id and store the entire object again.
That object must contain data for each and every fields of the mongo document.
Here is an example.
mongoDBCollectionObject.findOneAndRemove({ // -- it will delete the entire document
_id: req.body.fieldsdata._id // here fiedsdata is exact copy with modification of previous data
}, function(err, data) {
var newFieldsData = new mongoDBCollectionObject(fieldsdata); //-- fieldsdata updated data
newFieldsData.save(function(err, data) { // save document to that collection with updated data
if (err) {
console.log(err);
} else
res.json({
success: true
});
});
})
To clarify the question, it looks like you are taking the Request parameters and using those to find and update the given document.
Is there any way of accomplishing this without having to write a Mongo query?
The obvious answer is to update the Model object with the value from the Request. Which is what you suggest...
Unless you want to assign individual properties and run save() at the end.
But it seems like you don't want to do this? It sounds like you want to update the Model object directly from the Request object?
You can do this if you really want. You just loop through req.params and set the doc values where appropriate.
for(var i in req.params) {
if(req.params[i] != doc[i]){
doc[i] = req.params[i];
}
}
It should be as simple as this. However, you only want to do this if you have a whole bunch of validation code on the Model objects. The whole point to the Model is that you don't want to get random data in the DB. The line above will generically "set" the correct values, but you'll definitely need to include code for authentication, authorization and validation around that simple for loop.
try to updating the collection without the find, like this
Record.update({_id:req.params.id}, {$set: { field: request.field }}, {upsert: true}, function(err{...})
The option upsert create the document if not exist.
In case you have a new object and want to update whole object in the database, you can update multiple fields at once like this:
find the object
get all schema paths (fields)
save the new object.
SomeModel.findOne({ 'id': 'yourid' },function (err, oldObject) {
if (err) return handleError(err);
// get all schema paths (fields)
SomeModel.schema.eachPath(function(path) {
// leave __id and __v alone
if (path != '_id' && path != '__v') {
// update the data from new object
oldObject[path] = newObject[path];
}
})
oldObject.save(function(err) {
if (err)
console.log(err)
});
})
A neat and clean approach would be using async await and findOneAndRemove along with create Here is the sample code
try {
let resp = await this.findOneAndRemove({ _id: req.body._id });
let entry = await this.create(req.body);
} catch (err) {
}
Don't Forget to mark this whole function as async

validating against mongodb with nodejs

It should be very awesome to use non-blocking code but I'm running out of ideas how to acomplish this task. I have to validate a value by making few db queries like so:
validate = function() {
var valid = true;
db.collection('posts').findOne({date: ....}, function(err, post){
if (..) valid = false
}
db.collection('posts').findOne({author: .....}, function(err, post){
if (..) valid = false
}
return valid;
}
It is very good that validations can run in concurent manner, but the problem is how to return the final state. Obviously my example will not work. The function will return before db queries execution.
Welcome to the async world.
You should use something like async or fnqueue for your control flow,
then you can setup a chain of validations.
function isValid (mainCallback) {
new FnQueue({
date: function (callback) {
if (...) {
callback();
} else {
callback(new Error('what\'s happened here');
}
},
author: function (callback) {
db.collection('posts').findOne({ author: ..... }, callback);
}
},
function (err, data) {
mainCallback(Boolean(err)); //you should more than this :)
},
1 // concurrency level for serial execution
);
If you are using mongoose, then you can use the validations that are supported in the models. Take a look the validation docs for details and examples.
If you are not using mongoose, then you will need to pass a callback to your validate function, and the callback will receive the boolean. Also, you will need to handle the flow of your function so that they are run in series or parallel, depending on your needs. So if it is in in series, the following would work:
validate = function(callback) {
var valid = true;
db.collection('posts').findOne({date: ....}, function(err, post){
if (..) {
return callback(true);
}
db.collection('posts').findOne({author: .....}, function(err, post){
if (..) callback(false);
});
});
}

Resources