I'm fairly new to nodeJs and mongodb. I was having some problems regarding querying mongoose objects. I have 2 models
User model :
var mongoose = require('mongoose');
var bcrypt = require('bcrypt');
var gravatar = require('gravatar');
var Schema = mongoose.Schema;
var SendSchema = require('./Send').schema;
var TravelSchema = require('./Travel').schema;
var UserSchema = new Schema({
name: String,
email:{type: String, required: true, unique:true},
phone: {type: String, required: true, unique:true},
password: {type:String,required:true},
token: String,
is_admin : Boolean,
sendings : [SendSchema],
travels : [TravelSchema],
created_at : Date,
updated_at : Date,
image_url: String
})
UserSchema.pre('save',function(next){
var user = this;
if (this.isModified('password')||this.isNew){
bcrypt.genSalt(10,function(err,salt){
if(err){
return next(err);
}
bcrypt.hash(user.password,salt,function(err,hash){
if(err){
return next(err);
}
user.password = hash;
next();
});
});
} else {
return next();
}
});
UserSchema.pre('save', function(next) {
var currentDate = new Date();
this.updated_at = currentDate;
if (!this.created_at)
this.created_at = currentDate;
next();
});
UserSchema.methods.comparePassword = function (pw,cb) {
bcrypt.compare(pw,this.password,function(err,isMatch){
if(err){
return cb(err);
}
cb(null,isMatch);
});
};
module.exports = mongoose.model('User',UserSchema);
and Travel model :
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var TravelSchema = new Schema({
travelling_from:String,
travelling_to:String,
amount:String,
date:Date,
created_at: Date,
updated_at: Date,
traveller : {type:Schema.Types.ObjectId ,ref:'User'}
});
TravelSchema.pre('save', function(next) {
var currentDate = new Date();
this.updated_at = currentDate;
if (!this.created_at)
this.created_at = currentDate;
next();
});
module.exports = mongoose.model('Travel',TravelSchema);
now using express routes I'm querying the mongoose models like this:
router.post('/travellers',passport.authenticate('jwt',{session:false}), function(req, res, next) {
var pickup_location = req.body.pickup_location;
var delivery_location = req.body.delivery_location;
var date = req.body.date;
var sender = req.user._id;
var senders = [];
var travellers =[];
Travel.find({'date':date},function (err,travels) {
if(err) console.error(err.message);;
async.forEach(travels,function (travel,callback) {
User.findById(travel.traveller,function (err,user) {
if(err) throw err;
data = {
name:user.name,
email:user.email,
phone:user.phone,
image_url:user.image_url,
type:'traveller'
};
console.log(data);
travellers.push(data);
callback();
});
},function (err) {
if(err) console.error(err.message);;
});
});
console.log(travellers);
res.json(travellers);
});
When I try to access the traveller array after in the res.json() the query is complete I get an empty response whereas when I console.log() the data It prints correctly during the query, can someone help me out through this new asynchronous paradigm, I've been banging my head for 2 days now.
Add the async.series API which will run one function at a time, wait for it to call its task callback, and finally when all tasks are complete it will run callback (the final callback).
For example:
router.post('/travellers',
passport.authenticate('jwt', { "session": false }), function(req, res, next) {
var pickup_location = req.body.pickup_location;
var delivery_location = req.body.delivery_location;
var date = req.body.date;
var sender = req.user._id;
var locals = {
travellers: [],
senders: []
};
async.series([
// Load travels first
function(callback) {
Travel.find({ "date": date }, function (err, travels) {
if (err) return callback(err);
locals.travels = travels;
callback();
});
},
// Load users (won't be called before task 1's "task callback" has been called)
function(callback) {
async.forEach(locals.travels, function (travel, callback) {
User.findById(travel.traveller, function (err, user) {
if (err) return callback(err);
data = {
"name": user.name,
"email": user.email,
"phone": user.phone,
"image_url": user.image_url,
"type": "traveller"
};
console.log(data);
local.travellers.push(data);
callback();
});
}, function (err) {
if (err) return callback(err);
callback();
});
}
], function(err) { /* This function gets called after
the two tasks have called their "task callbacks" */
if (err) return next(err);
//Here locals will be populated with `travellers` and `senders`
//Just like in the previous example
console.log(locals);
console.log(locals.travellers);
res.json(locals.travellers);
});
});
An alternative is to use the $lookup operator in the aggregation framework where you can run an aggregation operation like the following:
router.post('/travellers',
passport.authenticate('jwt', {session: false }), function(req, res, next) {
var pickup_location = req.body.pickup_location;
var delivery_location = req.body.delivery_location;
var date = req.body.date;
Travel.aggregate([
{ "$match": { "date": date } },
{
"$lookup": {
"from": "users",
"localField": "traveller",
"foreignField": "_id",
"as": "traveller"
}
},
{ "$unwind": "$traveller" },
{
"$group": {
"_id": null,
"travellers": {
"$push": {
"name": "$traveller.name",
"email": "$traveller.email",
"phone": "$traveller.phone",
"image_url": "$traveller.image_url",
"type": "traveller"
}
}
}
}
], function(err, results) {
if (err) return next(err);
console.log(results);
console.log(results[0].travellers);
res.json(locals[0].travellers);
});
});
Related
i am new in node.js. I'm trying to store three different objects in collection in mongodb using node.js.
index.js
var mongoose = require('mongoose');
var bodyparser = require('body-parser');
var app = express();
var control = require('./controllers/controller');
var port = 3200;
mongoose.connect(
'mongodb://localhost:27017/create-company',
{useNewUrlParser: true},
err => {
if(err) throw err;
console.log('connection successfully');
}
)
app.use(bodyparser.json());
app.use(bodyparser.urlencoded({extended: true}));
app.use('/api', control);
app.listen(port, function(){
console.log('start your server on: ', port);
});
model.js
var mongoose = require('mongoose');
var CompanySchema = new mongoose.Schema({
companyname: String,
founder: String,
contact: String,
location: String
});
var company = mongoose.model("company", CompanySchema);
var BranchSchema = new mongoose.Schema({
branchname: String,
branchmanger: String,
contact: String,
location: String
});
var branch = mongoose.model('branch', BranchSchema);
var UserSchema = new mongoose.Schema({[enter image description here][1]
username: String,
userrole: String,
age: Number
});
var user = mongoose.model('user', UserSchema);
module.exports = {
company: company,
branch: branch,
user: user
}
controller.js
var express = require('express');
var router = express.Router();
var company = require('../models/model');
router.post('/create_company', function (req, res) {
var new_company = new company.company(req.body);
var new_branch = new company.branch(req.body);
var new_user = new company.user(req.body);
new_company.save(function (err, data) {
if (err)
res.send(data);
console.log(data);
});
new_branch.save(function (err, data) {
if (err)
res.send(data);
console.log(data);
});
new_user.save(function (err, data) {
if (err)
res.send(data);
console.log(data);
});
});
i am pass data to postman like this:
[{
"companyname": "stack",
"founder": "alex",
"contact": "1234567890",
"location": "in"
},
{
"branchname": "am",
"branchmanager": "abc",
"contact": "8754216523",
"location": "inn"
},
{
"username": "xyz",
"userrole": "admin",
"age": "23"
}]
enter image description here
There are three problems here:
You are attempting to create a new document by passing the entire body to the constructor. You need to pass the correct array element to it's respective constructor.
var new_company = new company.company(req.body[0]);
var new_branch = new company.branch(req.body[1]);
var new_user = new company.user(req.body[2]);
You are attempting to send a response more than once. You need to coordinate the callbacks so that you send the response after they all have completed. I suggest you use promises to accomplish this, fortunately Mongoose supports promises.
Promise.all([
new_company.save(),
new_branch.save(),
new_user.save()
]).then(function (data) {
res.send(data);
console.log(data);
});
You are not handling the error correctly. Your if (err) statement will cause res.send(data) to be called when there is an error, this is certainly not desired. Using the promise based method defined above error handling is very simple.
Promise.all([
new_company.save(),
new_branch.save(),
new_user.save()
]).then(function (data) {
res.send(data);
console.log(data);
}).catch(function (err) {
res.status(400).send('Uh oh! Something bad happened.');
console.error(err);
});
If you use ES6 syntax you can write a synchronous looking route, which may be more familiar if you are coming from a different language:
router.post('/create_company', async (req, res) => {
try {
const [company, branch, user] = req.body;
const data = await Promise.all([
new company.company(company).save(),
new company.branch(branch).save(),
new company.user(user).save()
]);
res.send(data);
} catch (err) {
res.status(400).send('Uh oh! Something bad happened.');
console.error(err);
}
});
I am trying to get all documents in mongo collection but instead i am getting empty response. my database name is taskDb which has a collection named item in which all the documents are stored. I think maybe there is some problem with schema but mongo is schema less db so i am not able to find the solution.
index.js
var express = require('express');
var app = express();
var bodyParser = require('body-parser');
var mongoose = require('mongoose');
var cors = require('cors');
app.use(bodyParser.json());
app.use(cors());
Items = require('./items.js');
mongoose.connect('mongodb://localhost/taskDb');
var db = mongoose.connection;
app.get("/",(req, res)=> {
res.send('Visit /api/*****');
});
app.get("/api/items",(req, res)=> {
Items.getItems(function(err, items){
if(err){
throw err;
}
console.log(res.json(items));
res.json(items);
});
});
// app.get("/api/matches",(req, res)=> {
// Matches.getMatches(function(err, matches){
// if(err){
// throw err;
// }
// res.json(matches);
// });
// });
// app.get("/api/deliveries/:playerName",(req, res)=> {
// Deliveries.getPlayerStats(req.params.playerName ,function(err, deliveries){
// if(err){
// throw err;
// }
// res.json(deliveries);
// });
// });
app.listen(3005,()=>{
console.log('Listening on port 3005...');
});
item.js
var mongoose = require('mongoose');
var itemSchema = mongoose.Schema({
_ID:{
type: String,
required: true
},
ITEM:{
type: String,
required: true
},
KEY:{
type: Number,
required: true
},
STATUS:{
type: String,
required: true
}
});
var Item = module.exports = mongoose.model('item', itemSchema);
// module.exports.getPlayerStats = function (playerName, callback) {
// Deliveries.aggregate([{$project:{_id: 1,batsman: 1 ,batsman_runs: 1, dismissal:{
// $cond: [ { $eq: ["$player_dismissed", playerName ] }, 1, 0]
// }}},{ $match: { batsman: playerName } },
// { $group: {_id: "$batsman", total_runs: { $sum: "$batsman_runs" },total_dismissal: { $sum: "$dismissal"}}}
// ], callback);
// }
module.exports.getItems = function (callback, limit) {
Item.find(callback).limit(limit);
};
There are two issues I see in getItems function:
Condition for find() is not specified. In case you want to read all records, you can specify it as empty object.
Limit parameter is not being passed from your request handler to getItems function. You would either need to default it to some number or handle the scenario where limit won't be passed.
Modifying getItems() to something like below should work:
module.exports.getItems = function (callback, limit) {
Item.find({}, callback).limit(limit || 20); // Default limit to a feasible number
};
Also, you can pass limit to getItems() function from request handler if you want to override default:
app.get("/api/items",(req, res)=> {
Items.getItems(function(err, items){
if(err){
throw err;
}
console.log(res.json(items));
return res.json(items);
}, 50); // Pass limit
});
I have one schema defined in userref.js
module.exports = (function userref() {
var Schema = mongoose.Schema;
var newSchema= new Schema([{
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
index: true
},
value: { type: Number }
}]);
var results = mongoose.model('UserRef', newSchema);
return results;
})();
I have inserted some data and when I try to fetch some data I am getting proper values from mongodb console
db.getCollection('userrefs').find({'userId':ObjectId('57a48fa57429b91000e224a6')})
It returns properly some data
Now issue is that when I try to fetch some data in code by giving objectId I am getting empty array. In below function userrefs is returned as empty array
//req.params.userId=57a48fa57429b91000e224a6
var UserRef = require('../userref.js');
this.getuserref = function (req, res, next) {
try {
var o_userId =mongoose.Types.ObjectId(req.params.userId);
var query = { userId: o_userId };
var projection = '_id userId value';
UserRef.find(query, projection, function (err, usrrefs) {
if (err) return next(err);
res.send(usrrefs);
console.log("userref fetched Properly");
});
} catch (err) {
console.log('Error While Fetching ' + err);
return next(err);
}
};
Also when I debug code I can see o_userId as objectId with id value as some junk character
o_userId: ObjectID
_bsontype: "ObjectID"
id: "W¤¥t)¹â$¦"
Try this:
try {
var o_userId =mongoose.Types.ObjectId(req.params.userId);
var query = { userId: o_userId };
var projection = '_id $.userId $.value';
UserRef.find(query, projection, function (err, usrrefs) {
if (err) return next(err);
res.send(usrrefs);
console.log("userref fetched Properly");
});
} catch (err) {
console.log('Error While Fetching ' + err);
return next(err);
}
Add the export like this
module.exports.modelname= mongoose.model('userrefs', nameofschema, 'userrefs');
var z = require('../userref.js');
var UserRef = z.modelname;
Now call using UserRef.
Just simply try this man.
Model.find({ 'userId': objectidvariable}, '_id userid etc', function (err, docs) {
// docs is an array
});
Reference sample copied from their official doc.
While studying node I am trying to create a little commentthread application. Since the book I am using is a little outdated, I had to adjust the model provided in the book to get the application running. However, I think something is still wrong with my model, because a part of the data is stored as object Object. Does anyone see the problem in the model?
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var ReplySchema = new Schema();
ReplySchema.add({
username: String,
subject: String,
timestamp: { type: Date, default: Date.now },
body: String,
replies:[ReplySchema]
}, { _id: true });
var CommentThreadSchema = new Schema({
title: String,
replies:[ReplySchema]
});
mongoose.model('Reply', ReplySchema);
mongoose.model('CommentThread', CommentThreadSchema);
The result in Mongo:
{ _id: 56c8c91b011c7db2608159d6,
'[object Object]timestamp': Sat Feb 20 2016 21:14:19 GMT+0100 (CET), '[object Object]replies': [] }
The controller
var mongoose = require('mongoose'),
CommentThread = mongoose.model('CommentThread'),
Reply = mongoose.model('Reply');
exports.getComment = function(req, res) {
CommentThread.findOne({ _id: req.query.commentId })
.exec(function(err, comment) {
if (!comment){
res.json(404, {msg: 'CommentThread Not Found.'});
} else {
res.json(comment);
}
});
};
exports.addComment = function(req, res) {
CommentThread.findOne({ _id: req.body.rootCommentId })
.exec(function(err, commentThread) {
if (!commentThread){
res.json(404, {msg: 'CommentThread Not Found.'});
} else {
var newComment = Reply(req.body.newComment);
newComment.username = generateRandomUsername();
addComment(req, res, commentThread, commentThread,
req.body.parentCommentId, newComment);
}
});
};
function addComment(req, res, commentThread, currentComment,
parentId, newComment){
if (commentThread.id == parentId){
console.log(newComment);
commentThread.replies.push(newComment);
updateCommentThread(req, res, commentThread);
} else {
for(var i=0; i< currentComment.replies.length; i++){
var c = currentComment.replies[i];
if (c._id == parentId){
c.replies.push(newComment);
var replyThread = commentThread.replies.toObject();
updateCommentThread(req, res, commentThread);
break;
} else {
addComment(req, res, commentThread, c,
parentId, newComment);
}
}
}
};
function updateCommentThread(req, res, commentThread){
CommentThread.update({ _id: commentThread.id },
{$set:{replies:commentThread.replies}})
.exec(function(err, savedComment){
if (err){
res.json(404, {msg: 'Failed to update CommentThread.'});
} else {
res.json({msg: "success"});
}
});
}
function generateRandomUsername(){
//typically the username would come from an authenticated session
var users=['Mojos', 'Milo', 'Mihaal', 'Haly', 'MilodeBaesz', 'Mihaly'];
return users[Math.floor((Math.random()*5))];
}
I'd need some help on returning values after saving a new entry to my db using mongoose.
This is how my controller looks:
var userModel = require('../models/users');
module.exports = {
findAll: function(req, res) {
userModel.user.findAll(function(err, users) {
return res.json(users);
});
},
findId: function(req, res) {
var id;
id = req.params.id;
userModel.user.findId(id, function(err, user) {
return res.json(user);
});
},
addUser: function(req, res) {
newUser = new userModel.user;
newUser.username = req.body.username;
newUser.password = req.body.password;
newUser.addUser(function(err, user) {
return res.json(user);
});
}
};
And here's my users.js:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var UserSchema = new Schema({
username: {
type: String,
required: true
},
password: {
type: String,
required: true
}
});
module.exports = {
findAll: UserSchema.statics.findAll = function(cb) {
return this.find(cb);
},
findId: UserSchema.statics.findId = function(id, cb) {
return this.find({
_id: id
}, cb);
},
addUser: UserSchema.methods.addUser = function(cb) {
return this.save(cb);
}
};
This all works ok, but it only returns me the newly added user with addUser. I would like to get all the entries, including the newsly added one, as a return value. Just like using "findAll". How would be able to do this?
Yes, like bernhardw said there doesn't seem to be a way to return anything but the added document with save().
I followed his advice and called findAll() inside addUser() and it all works perfect now -> I can return all my users after saving a new new one. Thanks.