Url parameter in express and mongoose - node.js

Im building a basic web app with node/express and mongoose. How do i replace :id in express parameter to a string
For example
mongoose model
var UserSchema = new Schema({
name: String,
});
var User = mongoose.model('User', UserSchema);
Let say there is a user name "Mr robot king" in the User's collection right now
app.get('/:id', function(req, res, next) {
User.findOne({ _id: req.params.id }, function(err, user) {
res.render('home');
});
});
result of the url
localhost:8080/5asdasd43241asdasdasd
What i want is
localhost:8080/Mr-robot-king

In essence, that wouldn't be too difficult:
app.get('/:name', function(req, res, next) {
var name = req.params.name.replace(/-/g, ' ');
User.findOne({ name : name }, function(err, user) {
res.json(user);
});
});
Some considerations:
Since your example replaces spaces with hyphens, and the code above does the opposite, user names with actual hyphens in them will break this substitution scheme;
If user names are allowed to contain characters that have special meaning in URL's (like & or ?), when building the links, you should encode them. So the user name jill&bob will yield the link localhost:8080/jill%26bob
You probably want to add an index to the name field to make sure that queries are fast;
EDIT: to generate these links, for instance from an EJS template, you can use something like this:
go to page

Related

Return all objects from collection except for ones with certain field in mongoose

I am developing a node.js application using Mongoose (very new to it all) and I have a collection of products. Each product has a "username" field which links it back to a certain user. I want to get all of the objects in this collection, except for the ones where the username is equal to the username of the logged in user.
I know I can return all from a particular user using :
app.get('/profile', function(req, res){
var msg = req.flash('message');
var username1 = req.user.username;
Products.find({'username': username1}).sort('-date').exec(function(err, docs){
res.render('profile', { title: 'Products', products: docs, flashmsg: msg});
});
});
But how would I go about return objects with the exception of the logged in user?

Expressjs rest api how to deal with chaining functionality

I am building a restful API using express, mongoose and mongodb. It works all fine but I have a question about how to deal with requests that contain more functionality than just one find, delete or update in the database. My user model looks as follows:
var UserSchema = new Schema({
emailaddress: {type: String, unique: true},
firstname: String,
lastname: String,
password: String,
friends: [{type: Schema.Types.ObjectId, unique: true}]
});
As you can see is the friends array just an array of ObjectIds. These ObjectIds refer to specific users in the database. If I want to retrieve an array of a user's friends I now have to look up the user that makes the request, then find all the users that have the same id as in the friends array.
Now it looks like this:
methods.get_friends = function(req, res) {
//find user.
User.findOne({_id: req.params.id}, function(err, user, next) {
if(err) next(err);
if(user) {
console.log(user);
//find friends
User.find({_id: {$in: user.friends}}, {password: 0}).exec(function (err,
friends, next) {
if(err) next(err);
if(friends) {
res.send(friends);
};
});
}
Would it be possible to seperate the lookup of the user in a certain method and chain the methods? I saw something about middleware chaining i.e. app.get('/friends', getUser, getFriend)but would that mean that I have to alter the req object in my middleware (getUser) method and then pass it on? How would you solve this issue? Would you perhaps change the mongoose model and save all friend data (means that it could become outdated) or would you create a method getUser that returns a promise on which you would collect the friend data?
I will be grateful for all the help I can get!
Thank you in advance.
Mongoose has a feature called population which exists to help in these kinds of situations. Basically, Mongoose will perform the extra query/queries that are required to load the friends documents from the database:
User.findOne({_id: req.params.id})
.populate('friends')
.exec(function(err, user) {
...
});
This will load any related friends into user.friends (as an array).
If you want to add additional constraints (in your example, password : 0), you can do that too:
User.findOne({_id: req.params.id})
.populate({
path : 'friends'
match : { password : 0 },
})
.exec(function(err, user) {
...
});
See also this documentation.

Creating a search option in Nodejs with database mongodb

I'm trying to make a search option by name in node.js and it will search local database, if it finds the name it will display in webpage. But I'm getting the error in this syntax : user.find({id : findname}, which I have used in below code.
router.get('/', function(req, res, next) {
var findname = req.body.findname;
res.render('detail', { title: 'Detail' });
});
mongoose.connect('mongodb://localhost/student');
var testSchema = new mongoose.Schema({
id : Number,
name : String,
email : String,
age : Number,
college : String
});
var user = mongoose.model('stud', testSchema, 'stud');
router.post('/show', function(req, res){
user.find({name : findname}, function(err, docs){
res.render('detail',{users:docs});
});
});
So, the problem is you've defined your findname variable in one function and are trying to use it in another. In order to accomplish what you're looking for, your router.post function should probably look something like this:
router.post('/show', function(req, res){
var findname = req.body.findname
user.find({name : findname}, function(err, docs){
res.render('detail',{users:docs});
});
});
Make sure you're using the correct middleware to populate the req.body object as well, or the code still won't work.
I suggest you to read the mongoose documentation Mongoose Doc. The id (and _id) fields are automatically assigned by Mongoose. The id is a virtual getter on the _id, it is a string or hex representation of the _id. Try to change the name of your field ­id by myId.
Anyway, your id is of type number and your try to match it with findname which is I guess a string ? Is there a type mismatch here ?

node.js/express/mongoose noob issue

I just start to use node.js with express and mongoose, and I have a stupid question...
Somewhere in my routes.js file, I have the following section :
// DASHBOARD SECTION, GET MY GROUPS
app.get('/dashboard', isLoggedIn, function(req, res) {
var Group = require('../app/models/group'); //adding mongoose Schema
Group.find({"groupDetails.userId" : req.user._id})
.exec(function(err, myGroups) {
if (err)
res.send(err);
var myGroups = myGroups;
//console.log("myGroups: " + myGroups); // check with "heroku logs"
res.render('dashboard.ejs', {
user : req.user,
myGroups : myGroups
});
});
});
This code works. When someone browse the dashboard page, I receive "myGroups" which is an array with all the groups for the current logged in user.
Now, here is my question :
Actually when someone browse the dashboard page, I would like to make a second query (based on the exact same pattern) to get all groups and all files for the current logged in user.
Then I will send "user", "myGroups" and "myFiles" to the dashboard page.
How can I do that ?
I tried several things with no result so far... I think I'm a little bit lost in node.js callback functions :D
Thanks a lot for your help.
You have two options here:
1) deal with callback hell (callback inside callback inside...) to retrieve 3 sets of data. This way is least elegant and efficient
2) Use a library that will do the job asynchronously and have one callback when all the data is retrieved, you can use async library which is just awesome. In this case you will have just one callback in which you can access all the data you have fetched.
Here's what you can do with async in your case:
var async = require('async');
..........
app.get('/dashboard', isLoggedIn, function(req, res) {
var Group = require('../app/models/group'); //adding mongoose Schema
var User = require('../app/models/user'); //adding mongoose Schema
var Files = require('../app/models/files'); //adding mongoose Schema
async.parallel({
groups: function(callback){
Group.find(...).exec(callback);
},
users: function(callback){
Users.find(...).exec(callback);
},
files: function(callback){
Files.find(...).exec(callback);
}
}, function(err, results) {
if (err)
res.send(err);
var groups = results.groups;
var users = results.users;
var files = results.files;
res.render('dashboard.ejs', {
user : req.user,
myGroups : groups,
users: users,
files: files
});
});
});

Updating a model instance

I want to understand what the correct way to update a model instance is, using mongoose, ie:
Having:
User = {
username: {
type: String,
indexed: true
},
email: {
type: String,
indexed: true
},
name: String,
.........
};
I'm sending the whole form through ajax to a controller.
So far, i know of two options:
app.put('/users/', function(req, res){
var id = ObjectId(req.body._id);
User.findOne({_id: id}, function(err, user){
user.name = req.body.name;
....
....
user.save();
});
});
or:
app.put('/users/', function(req, res){
var id = ObjectId(req.body._id);
delete req.body._id
User.update({_id: id}, req.body, function(err){
.....
};
});
Both ways have disadvantages:
In the first approach i have to map all properties one by one;
In the second approach i have to delete all properties that can't be changed;
there is a third possible approach that would make me send from client-side, only the changed properties, but i think that would be a big hassle to.
Is there a good, standardized way that i'm not seeing, to do this?
Thanks
This is the approach I typically use, it involves a small npm package called mongoose-mass-assignement:
https://github.com/bhelx/mongoose-mass-assignment
The docs are pretty self explanatory, easy to use. It basically does the delete for you. So you add protected:true to your model, and then it deletes those properties for you. I like this approach because it still allows me to use the req.body as is. You can use this for updates and inserts as well.
A variant of the first approach is to use underscore's extend method to apply all properties of the body to your model instance:
app.put('/users/', function(req, res){
var id = ObjectId(req.body._id);
User.findOne({_id: id}, function(err, user){
_.extend(user, req.body);
user.save();
});
});
However, be sure to delete any properties you don't want the user to be able to set from req.body first.

Resources