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))];
}
Related
exports.clearHours = (req, res, next) => {
Hour
.find({ user: req.body.userId })
.then(hour => {
for (let i=0;i<hour.length;i++) {
hour[i].hours = 0;
}
return hour.save()
})
.then(result => {
res.status(200).json({message: 'Working hours have been successfully updated.'});
})
.catch(err => {
if (!err.statusCode) {
err.statusCode = 500;
}
next(err);
})
};
I am trying to save the formatted array on the database and I get this error. The updated code is passed properly but when I am trying to save the array it comes up with this error. Any ideas why?
This is my hour model:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const hourSchema = new Schema ({
day: {
type: String,
required: true
},
hours: {
type: Number,
required: true
},
user: {
type: Schema.Types.ObjectId,
ref: 'User',
required: true
}
});
module.exports = mongoose.model('Hour', hourSchema);
It seems you are fetching the document into memory and re-setting the hour field to 0, better you can run an update query into the database itself.
On top require mongoose -
const mongoose = require('mongoose');
Below is clearHours method refactored.
exports.clearHours = async (req, res, next) => {
try {
const query = {
user: mongoose.Types.ObjectId(req.body.userId)
};
const update = {
$set: {
hours: 0
}
};
await Hour.update(query, update).exec();
res.status(200).json({message: 'Working hours have been successfully updated.'});
} catch (err) {
if (!err.statusCode) {
err.statusCode = 500;
}
next(err);
}
};
Here's my Schema:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const PostsSchema = new Schema({
userId: String,
postId: String,
title: String,
description: String,
tags: { many: String, where: String, what: String },
date: { type: Date, default: Date.now },
}, { collection : 'posts'});
const Posts = mongoose.model('Post', PostsSchema);
module.exports = Posts;
Here's my route with the query:
const express = require('express');
const router = express.Router();
const mongoose = require('mongoose');
const Posts = require('../models/Posts');
router.get('/', (req, res, next) => {
const refreshOrLoadMore = params.refreshOrLoadMore || '';
if (refreshOrLoadMore === 'loadMore') {
console.log('1');
Posts.find({}).sort({date:-1}).limit(10, (err, data) => {
if(err) {
console.log('2');
res.send(err);
} else {
console.log('3');
res.json(data);
}
});
}
});
The if statement returns true and the first console.log is triggered. But after that none of the other console.logs are triggered and just nothing happens. No data is being send and no error is being send.
So my guess is, that i did something wrong with the Schema, but i did it just as i did my other ones and they do work.
Can someone point out where i went wrong?
Thanks in advance!
Try this
if (refreshOrLoadMore === 'loadMore') {
console.log('1');
Posts.find({}).sort({date:-1}).limit(10)
.exec((err, data) => {
if(err) {
console.log('2');
res.send(err);
} else {
console.log('3');
res.json(data);
}
});
}
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);
});
});
I am trying to set up my nodejs app with a CRUD for mongodb sub-docs using Mongoose but can't figure out how to access the nested object's _id. I can only get the parent ObjectId. I can perform a .push on a new child object but can't perform a simple get, put or delete on an existing child object.
Here is my schema:
//new user model
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var ObjectId = Schema.ObjectId;
// Task schema
var taskSchema = mongoose.Schema({
clientEasyTask : { type: String },
clientHardTask : { type: String },
clientStupidTask : { type: String }
});
var userSchema = new mongoose.Schema({
email: { type: String, unique: true, lowercase: true },
password: String,
task : [taskSchema]
});
module.exports = mongoose.model('Task', taskSchema);
module.exports = mongoose.model('User', userSchema);
Here is my routes:
'use strict';
var isAuthenticated = require('./middleware/auth').isAuthenticated,
isUnauthenticated = require('./middleware/auth').isUnauthenticated;
var User = require('./models/user');
var Task = require('./models/user');
// Create user.task
module.exports = function (app, passport) {
app.post('/api/tasks', isAuthenticated, function (req, res) {
var userEmail = req.body.email;
var easyTask = req.body.easyTask;
User.findOne({ 'email' : userEmail }, function(err, user) {
console.log('found user and defining status and data');
var status;
var data;
if (err) {
status = 'error';
data = 'unknown error occurred';
}
if (user === null) {
status = 'error';
data = 'user not found';
} else {
status = 'ok';
data = user;
}
user.task.push({
clientEasyTask: easyTask
});
user.save();
res.json({
response: {
'status': status
}
});
});
});
// Get one user.task
app.get('/api/tasks/:id', function (req, res) {
return Task.findById(req.params.id, function(err, task) {
if(!task) {
res.statusCode = 404;
return res.send({ error: 'Not found' });
}
if(!err) {
return res.send({ status: 'OK', task:task });
} else {
res.statusCode = 500;
console.log('Internal error(%d): %s', res.statusCode, err.message);
return res.send({ error: 'Server error' });
}
});
});
};
I am using Postman to test everything so there is no fronted code. When I pass the _id of the task (nested in the user) I receive null when I call Get on '/api/tasks/:id'. How can I can get only the specific task?
The mongoose documentation states that you can use parent.children.id(id); but I couldn't get it to work.
The task field of User contains the tasks as embedded subdocs, not references to another collection, so you can't query tasks independent of users (like you're trying to do).
To query for the embedded task subdoc, you can use a query like this:
User.findOne({'task._id': req.params.id})
.select('task.$') // Just include the matching task element
.exec(function(err, user) {
if(!user) {
res.statusCode = 404;
return res.send({ error: 'Not found' });
}
if(!err) {
// The matching task will always be in the first element of the task array
return res.send({ status: 'OK', task: user.task[0] });
} else {
res.statusCode = 500;
console.log('Internal error(%d): %s', res.statusCode, err.message);
return res.send({ error: 'Server error' });
}
}
);
To make this efficient, you'd want to add an index on {'task._id': 1}.
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.