Related
Here is my Schema
I am trying to add replies array inside answers array. If someone answers a question and if someone wants to reply on the given answer
const mongoose = require("mongoose");
const { ObjectId } = mongoose.Schema;
const questionSchema = new mongoose.Schema(
{
postedBy: {
type: ObjectId,
required: true,
ref: "User",
},
question: {
type: String,
required: true,
},
photo: {
data: String,
required: false,
},
answers: [
{
userId: { type: ObjectId, ref: "User" },
answerType: {
data: String,
required: false,
},
answer: String,
replies: [
{
userId: { type: ObjectId, ref: "User" },
reply: String,
replyType: {
data: String,
required: false,
},
},
],
},
],
questionType: {
data: String,
required: false,
},
createdAt: {
type: Date,
required: true,
default: Date.now,
},
},
{ timeStamps: true }
);
module.exports = mongoose.model("Question", questionSchema);
Here is my Controller method
exports.postReply = (req, res) => {
const reply = req.body.reply || "";
const userId = req.user._id || "";
const answerId = req.body.answerId || "";
Question.findByIdAndUpdate(
{ _id: answerId },
({ $push: { answers: { answer: { replies: { reply, userId } } } } },
{ new: true }),
(err, newReply) => {
if (err) {
res.status(400).json({
error: errorHandler(err),
});
} else {
res.json({
msg: "Reply posted successfully",
newReply,
});
}
}
);
};
I feel I am going wrong on the findOneAndUpdate method. I am getting no error on the console but newReply comes null. Any help will be appreciated.
I would suggest you using the $addToSet instead of the $push operator as you are adding a document to the array. (see: https://docs.mongodb.com/manual/reference/operator/update/addToSet/).
If you want to add more than one document to the array, refer also to the $each operator together with $addToSet.
So your coding can look similiar to this (note: the variable 'yourDocument' is the document you want to add):
Question.findByIdAndUpdate(
{ _id: answerId },
{ $addToSet: { answers: yourDocument } },
{ new: true },
(err, newReply) => {
if (err) {
res.status(400).json({
error: errorHandler(err),
});
} else {
res.json({
msg: "Reply posted successfully",
newReply,
});
}
}
);
};
The problem is clearly the parentesis around
({ $push: { answers: { answer: { replies: { reply, userId } } } } }, { new: true })
Doing this console.log( ({a:1}, {b:2}) ); will log {b: 2} which means you are doing this
Question.findByIdAndUpdate( { _id: answerId }, { new: true }, (err, newReply) => {
So remove the parentesis and you should be good
Question.findByIdAndUpdate(
{ _id: answerId },
{ $push: { answers: { answer: { replies: { reply, userId } } } } },
{ new: true },
(err, newReply) => {
if (err) {
res.status(400).json({
error: errorHandler(err),
});
} else {
res.json({
msg: "Reply posted successfully",
newReply,
});
}
}
);
This is my Profile Schema:
const mongoose = require('mongoose');
const ProfileSchema = new mongoose.Schema({
user: {
// Special field type because
// it will be associated to different user
type: mongoose.Schema.Types.ObjectId,
ref: 'user',
},
company: {
type: String,
},
website: {
type: String,
},
location: {
type: String,
},
status: {
type: String,
required: true,
},
skills: {
type: [String],
required: true,
},
bio: {
type: String,
},
githubusername: {
type: String,
},
experience: [
{
title: {
type: String,
required: true,
},
company: {
type: String,
required: true,
},
location: {
type: String,
},
from: {
type: Date,
required: true,
},
to: {
type: Date,
},
current: {
type: Boolean,
default: false,
},
description: {
type: String,
},
},
],
education: [
{
school: {
type: String,
required: true,
},
degree: {
type: String,
required: true,
},
fieldofstudy: {
type: String,
required: true,
},
from: {
type: Date,
required: true,
},
to: {
type: Date,
},
current: {
type: Boolean,
default: false,
},
description: {
type: String,
},
},
],
social: {
youtube: {
type: String,
},
twitter: {
type: String,
},
facebook: {
type: String,
},
linkedin: {
type: String,
},
instagram: {
type: String,
},
},
date: {
type: Date,
default: Date.now,
},
});
module.exports = Profile = mongoose.model('profile', ProfileSchema);
This is my view api. It doesn't work. it only return Cast to ObjectId failed for value { 'experience._id': '5edcb6933c0bb75b3c90a263' } at path _id for model profile
router.get('/experience/viewing/:viewexp_id', auth, async (req, res) => {
try {
const exp = await Profile.findById({
'experience._id': req.params.viewexp_id,
});
if (!exp) {
return res.status(404).json({ msg: 'Experience not found' });
}
res.json(exp);
} catch (err) {
console.error(err.message);
res.status(500).send(err.message);
}
});
How can I fix this? I tried looking at the stackoverflow of the same errors. still it doesn't seem to work.
and this is what I am trying to hit
The problem is that you have to convert your string _id to mongoose object id using this function mongoose.Types.ObjectId and my suggestion is to use findOne function instead of findById,
var mongoose = require('mongoose');
router.get('/experience/viewing/:viewexp_id', auth, async (req, res) => {
try {
let id = mongoose.Types.ObjectId(req.params.viewexp_id);
const exp = await Profile.findOne(
{ "experience._id": req.params.viewexp_id },
// This will show your sub record only and exclude parent _id
{ "experience.$": 1, "_id": 0 }
);
if (!exp) {
return res.status(404).json({ msg: 'Experience not found' });
}
res.json(exp);
} catch (err) {
console.error(err.message);
res.status(500).send(err.message);
}
});
var mongoose = require('mongoose');
router.get('/experience/viewing/:viewexp_id', auth, async (req, res) => {
try {
const exp = await Profile.findOne({
'experience._id': mongoose.Types.ObjectId(req.params.viewexp_id),
});
if (!exp) {
return res.status(404).json({ msg: 'Experience not found' });
}
res.json(exp);
} catch (err) {
console.error(err.message);
res.status(500).send(err.message);
}
});
You are saving object id . but your param id is string. convert it in ObjectId. Please check my solution.
router.post(
"/",
[
auth,
[
check("status", "status is required").not().isEmpty(),
check("skills", "skills is required").not().isEmpty(),
],
],
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const {
company,
website,
location,
bio,
status,
githubuername,
skills,
youtube,
facebook,
twitter,
instagram,
linkedin,
} = req.body;
const profileFileds = {};
profileFileds.user = req.user.id;
if (company) profileFileds.company = company;
if (website) profileFileds.website = website;
if (location) profileFileds.location = location;
if (bio) profileFileds.bio = bio;
if (status) profileFileds.status = status;
if (githubuername) profileFileds.githubuername = githubuername;
if (skills) {
profileFileds.skills = skills.split(",").map((skill) => skill.trim());
}
//Build profile object
profileFileds.social = {};
if (youtube) profileFileds.social.youtube = youtube;
if (twitter) profileFileds.social.twitter = twitter;
if (facebook) profileFileds.social.facebook = facebook;
if (linkedin) profileFileds.social.linkedin = linkedin;
if (instagram) profileFileds.social.instagram = instagram;
try {
let profile = await Profile.findOne({ user: req.user.id });
if (profile) {
//update
profile = await Profile.findOneAndUpdate(
{ user: req.user.id },
{ $set: profileFileds },
{ new: true }
);
return res.json(profile);
}
//Create profile
profile = new Profile(profileFileds);
await profile.save();
res.json(profile);
} catch (err) {
console.error(err.message);
res.status(500).send("server Error");
}
}
);
I'm trying to create a "wishlist" feature for users on my node / mongo application. I've assumed the best way to model the scheme would be to reference the items they like. So far my reading has brought me to this point (I'm not very familiar with the Types.ObjectID):
Schema Model
var UserSchema = new mongoose.Schema({
email: {
type: String,
unique: true,
required: true,
trim: true
},
password: {
type: String,
required: true
},
wishlist: [{
type: mongoose.Schema.Types.ObjectId,
ref: "Wishlist",
required: true
}]
});
I've managed to write some code which pushes the relevant _id into the "Likes" array:
Product.findById(productID).exec(function (err, user) {
User.updateOne({ _id: req.session.userId }, { "$push": { "wishlist": productID } }, function (err, user) {
if (err) {
console.log("Failed to add")
} else {
console.log(productID + " has been added")
}
});
});
This outputs in the database like so:
{
"_id" : ObjectId("5c3f7e1f1268203b1f31cb17"),
"email" : "email",
"password" : "password",
"__v" : 0,
"wishlist" : [
ObjectId("5c41f4b42f82b14798d5c7fc"),
ObjectId("5c41f4b42f82b14798d5c7ff")
]
}
I'm stuck on how I'd output these wishlist items in my template. My assumption was to get the data like this:
router.get('/wishlist', middleware.requiresLogin, function(req, res, next) {
User.findOne({ _id: req.session.userId }, function(err, user) {
res.render('wishlist', {
title: 'Wishlist',
template: 'wishlist',
saved: user.wishlist,
header: true,
footer: true
});
});
});
And the loop through the items like this:
{{#each saved }} Code goes here {{/each }}
Am I approaching this correctly?
you'll need to populate the wishlist field, try this,
User.findOne({ _id: req.session.userId }).
populate('wishlist').
exec(function (err, user) {
res.render('wishlist', {
title: 'Wishlist',
template: 'wishlist',
saved: user.wishlist,
header: true,
footer: true
});
});
You can refer to the Populate (mongoose documentation).
//User_controller.js
exports.getUser = (req, res) => {
User.findOne({ _id: req.session.userId })
.populate('wishlist')
.then((user) => { res.json(user) })
.catch((error) => { res.status(500).json({ error })
});
};
// UserRoute.js
const express = require("express");
const router = express.Router();
const userCtrl = require('./user_controller');
router.get('/:id', userCtrl.getUser);
module.exports = router;
//server.js
//...
const userRoute = require("./UserRoute");
app.use("/user", userRoute);
//...
I am trying to access the data in my User-Model (using Express, Pug and Mongoose).
This is my schema:
var userSchema = mongoose.Schema({
userData: {
name: { type: String, required: true, unique: true },
password: String
},
notes: [ String ],
contacts: [{
name: String,
notes: [ String ]
}],
locations: [ String ]
});
This is my pug-template file:
each user in allUsers
p #{user.userData.name}
My Route looks like this:
app.get('/', function(req, res) {
if (app.locals.userData.name) {
res.render('app', {
currentUser: app.locals.userData,
allUsers: User.find({})
});
} else {
res.redirect('signup');
}
});
Where could be my mistake?
The Browser shows a Cannot read property 'name' of undefined error.
I had to use the callback function!!!
app.get('/', function(req, res) {
User.find({}, function(err, users) {
if (app.locals.userData.name) {
res.render('app', {
currentUser: app.locals.userData,
allUsers: users
});
} else {
res.redirect('signup');
}
});
});
I have this "schema" in mongoose, an array in my dictionary and have other embedded array,
I can save if no embedded object is present, how to save embedded objects?
var ReportSchema = new Schema({
appVersion: { type: String, required: true},
osVersion: { type: String, required: true},
deviceType: { type: String, required: true},
userID: { type: String, required: true},
sessionIDtimestamp: { type: String, required: true},
eventItem : [new Schema ({
eventType:{type :String},
startTime:{type :String},
endTime:{type :String},
params:[new Schema ({
val:{type :String}
})]
})]
});
on my router:
apiRouter.route('/report')
.post(function(req, res) {
var report = new Report();
report.appVersion = req.body.appVersion;
report.osVersion = req.body.osVersion;
report.deviceType = req.body.deviceType;
report.userID = req.body.userID;
report.sessionIDtimestamp = req.body.sessionIDtimestamp;
for (var i = req.body.events.length - 1; i >= 0; i--) {
var requestStringa = util.inspect(req.body.events, {showHidden: false, depth: null});
console.log("entro :"+requestStringa);
};
report.save(function(err) {
if (err) {
return res.send(err);
}
res.json({ message: 'report created!' });
});
})
also i dont think that way to enumerate the array is nice?
edit
the log of events
entro :[ { eventType: 'Account_Rated_Pressed',
startTime: '1435819399',
endTime: '1435819399',
params: [ { paramKey: 'rating', paramValue: '5' } ] },
{ eventType: 'RateableDetail',
startTime: '1435819399',
endTime: '1435819399',
params: [ { paramKey: 'rating', paramValue: '5' } ] } ]
how to save my embedded objects cheers
For saving embedded documents, just assign the array field eventItem the request object value as follows:
apiRouter.route('/report')
.post(function(req, res) {
var report = new Report();
report.appVersion = req.body.appVersion;
report.osVersion = req.body.osVersion;
report.deviceType = req.body.deviceType;
report.userID = req.body.userID;
report.sessionIDtimestamp = req.body.sessionIDtimestamp;
report.eventItem = req.body.events;
report.save(function(err) {
if (err) {
return res.send(err);
}
res.json({ message: 'report created!' });
});
})
In the instance that req.body.event is an object not an array, you would then need to use the JavaScript push() method to push the object to the array. Say for example, if req.body.event has the structure
{
eventType: 'Account_Rated_Pressed',
startTime: '1435819399',
endTime: '1435819399',
params: [ { paramKey: 'rating', paramValue: '5' } ]
}
you can then do
apiRouter.route('/report')
.post(function(req, res) {
var report = new Report();
report.appVersion = req.body.appVersion;
report.osVersion = req.body.osVersion;
report.deviceType = req.body.deviceType;
report.userID = req.body.userID;
report.sessionIDtimestamp = req.body.sessionIDtimestamp;
report.eventItem.push(req.body.event);
report.save(function(err) {
if (err) {
return res.send(err);
}
res.json({ message: 'report created!' });
});
})