Why does the secondaryUser field affect wether findOne works? - node.js

In this I am using nodejs with express and mongoose. My question is how does changing the secondaryUser field affect whether or not the findOne works? If I have it as friends.id it works and it finds the right profile, but I want to tie it to the user field in the profile. If I change it to friends.user.id the findOne fails and it sends the 404 error in the catch.
router.post(
"/:handle",
passport.authenticate("jwt", {
session: false
}),
(req, res) => {
Profile.findOne({ handle: req.params.handle }).then(friends => {
const newFriend = new Friend({
initialAccepted: true,
initialUser: req.user.id,
secondaryUser: friends.id
});
newFriend
.save()
.then(Friend => res.json(Friend))
.catch(err =>
res.status(404).json({
friendnotfound: "No people found with that handle"
})
);
});
}
);
The schema used for friend is
const FriendSchema = new Schema({
initialUser: {
type: Schema.Types.ObjectId,
ref: "profile"
},
secondaryUser: {
type: Schema.Types.ObjectId,
ref: "profile"
},
initialAccepted: {
type: Boolean,
default: false
},
initialSecondary: {
type: Boolean,
default: false
},
date: {
type: Date,
default: Date.now()
}
});
This is the schema for the profile
const ProfileSchema = new Schema({
user: {
type: Schema.Types.ObjectId,
ref: "users"
},
handle: {
type: String,
required: true,
max: 40
},
bio: {
type: String
},
platforms: {
type: [String]
},
website: {
type: String
},
social: {
youtube: {
type: String
},
twitter: {
type: String
},
facebook: {
type: String
},
linkedin: {
type: String
},
twitch: {
type: String
}
},
games: [
{
name: {
type: String
},
platform: {
type: String
},
handle: {
type: String
},
rank: {
type: String
}
}
],
date: {
type: Date,
default: Date.now
}
});

Follow proper naming convention for variables
Profile.findOne({ handle: req.params.handle }).then(profile => { // changed name from friends to profile
const newFriend = new Friend({
initialAccepted: true,
initialUser: req.user.id,
secondaryUser: profile.id // changed name from friends to profile
// profile.user.id (ref to user table not provided in schema)
});
if you give profile.user.id the object will not get created ( checking for id inside profile schema but user id provided)
Friend Schema:
secondaryUser: {
type: Schema.Types.ObjectId,
ref: "profile" // checking for id inside profile schema
},

Related

Mongoose How to sort .populate field

I'm work with an user/articles profile system. I have been using the .populate() to render the posts but I cannot get the articles sorted by the date they were created.
I am using the createdAt variable as the main way of ordering the posts displayed.
For reference:
router.get('/:id', async (req, res) => {
const user = await User.findById(req.params.id, function(error) {
if(error) {
req.flash("error", "something went wrong")
res.redirect("/");
}
}).populate('articles')
res.render('users/show',{
user: user
});
and the article.js:
const ArticleSchema = new mongoose.Schema({
title: {
type: String,
required: true
},
author: {
type: String
},
markdown: {
type: String,
required: true
},
createdAt: {
type: Date,
default: Date.now
},
slug: {
type: String,
required: true,
unique: true
},
sanitizedHtml: {
type: String,
required: true
},
img: {
type: String
},
type:{
type: String
},
user : { type: Schema.Types.ObjectId, ref: 'User' },
}, {timestamps: true});
In advance thank you all for the help.
There is a property called options in populate,
.populate({
path: 'articles',
options: { sort: { createdAt: -1 } }
})

Mongoose do not populate objectid in an objectid of array

THIS PROBLEM IS A LITTLE LONGER. SO I TYPED BOLD THE CRITICAL INFORMATIONS FOR YOU.
I develop a project like stackoverflow. I have 4 databases which are:
problems
users
solutions
comments
I referrenced these schemas each other. Here is the Schemas:
Problem Schema
const problemSchema = new mongoose.Schema({
title: {
type: String,
required: [true, 'You have to enter a title']
},
content: {
type: String,
required: [true, 'You have to enter a content']
},
createdAt: {
type: Date,
default: Date.now()
},
slug: {
type: String
},
solution: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Solution'
},
],
comment: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Comment'
}
],
votes: {
type: Number,
default: 0
},
views: {
type: Number,
default: 0
},
user: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'User'
}
})
module.exports = mongoose.model('Problem', problemSchema)
User Schema:
const userSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: [true, 'You have to enter an email'],
unique: true,
match: [
/^([\w-\.]+#([\w-]+\.)+[\w-]{2,4})?$/,
'Please provide a valid email address.'
]
},
password: {
type: String,
required: [true, 'You have to enter a password'],
minlength: [6, 'Your password cannot be less than 6 character.'],
select: false
},
role: {
type: String,
default: 'user',
enum: ['user', 'admin']
},
createdAt: {
type: Date,
default: Date.now()
},
about: {
type: String
},
place: {
type: String
},
age: {
type: Number
},
blocked: {
type: Boolean,
default: false
},
problem: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Problem'
},
],
solution: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Solution'
}
],
comment: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Comment'
}
]
})
and Comments Schema:
const commentSchema = new mongoose.Schema({
content: {
type: String,
required: [true, 'You have to enter a content']
},
createdAt: {
type: Date,
default: Date.now()
},
isFunctional: {
type: Boolean,
default: false
},
user: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'User'
},
problem: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Problem'
},
})
module.exports = mongoose.model('Comment', commentSchema)
In my project, I send problems into MongoDB. Then I send comment. After save comments, I add these comments into problems and user DB with a function.
function that comments are saved in DB:
const Comment = require('../models/comment/Comment')
const Problem = require('../models/problem/Problem')
const User = require('../models/user/User')
const asyncErrorWrapper = require('express-async-handler')
const addCommentToProblem = asyncErrorWrapper(async (req, res, next) => {
const {content, problemId} = req.body
const newComment = await Comment.create({
content: content,
problem: problemId,
user: req.user.id,
})
const problemOfComment = await Problem.findByIdAndUpdate(problemId, {
$push: { comment: newComment._id }
})
const userOfComment = await User.findByIdAndUpdate(req.user.id, {
$push: { comment: newComment._id }
})
})
Okey everything is so far so good. The problem comes here. When I try to get a problem, I populate some fields for example user fields. So I can add user information in this detail of problem. When populate user and comment in problem schema, it sends me the data. Still, we're ok. But when I try to get user field in comments, it doesn't populate user. It turns just objectId of user information.
Here is the function that I get problem:
const getAProblem = asyncErrorWrapper(async (req, res, next) => {
const {id} = req.params
const problems = null
await Problem.findByIdAndUpdate(id, {
$inc: { views: 1 }
}, { new: true })
.populate('user') ==> THIS LINE WORKS
.populate('comment') ==> THIS LINE WORKS
.populate('comment.user') ==> THIS LINE DOES NOT WORK
.exec(function(err, post) {
if(err) {
console.log(err)
}
res
.status(200)
.json({
success: true,
data: post
})
});
})
Thanks for reading and your patience. Any help will be appreciated.
See doc at https://mongoosejs.com/docs/populate.html
And try this way.
const getAProblem = asyncErrorWrapper(async (req, res, next) => {
const {id} = req.params
const problems = null
await Problem.findByIdAndUpdate(id, {
$inc: { views: 1 }
}, { new: true })
.populate('user') ==> THIS LINE WORKS
.populate({
'path': 'comment',
'populate': {
'path':'user'
}
})
.exec(function(err, post) {
if(err) {
console.log(err)
}
res
.status(200)
.json({
success: true,
data: post
})
});
})

How can I update some fields of an embedded object using mongoose's findOneAndUpdate method and not lose the other fields?

router.put('/experience/update/:exp_id',
auth,
async (req, res) => {
const {
title,
company,
location,
from,
to,
current,
description
} = req.body;
const newExp = {};
newExp._id = req.params.exp_id;
if (title) newExp.title = title;
if (company) newExp.company = company;
if (location) newExp.location = location;
if (from) newExp.from = from;
if (to) newExp.to = to;
if (current) newExp.current = current;
if (description) newExp.description = description;
try {
let profile = await Profile.findOne({ user: req.user.id });
if (profile) {
//UPDATE Experience
profile = await Profile.findOneAndUpdate(
{ user: req.user.id });
const updateIndex = profile.experience.map(exp => exp._id).indexOf(req.params.exp_id);
profile.experience[updateIndex] = newExp;
console.log('Experience updated!')
}
await profile.save();
res.json(profile);
} catch (error) {
console.log(error.message);
res.status(500).send('Internal Server Error');
}
}
)
I am using the findOneAndUpdate method to update the experience field inside a profile mongoose model.
After accesssing the endpoint, I put the updated details, for eg. company and location. But I lose all the other fields. So how can I update only select fields while others remain unchanged ?
Below is the profile schema:
const mongoose = require('mongoose');
const ProfileSchema = new mongoose.Schema({
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,
required: true
},
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);
There are some problems in your code.
You are passing only one argument to findOneAndUpdate. Ideally the syntax is findOneAndUpdate(filter, update). So basically you need to pass update query as 2nd argument.
profile = await Profile.findOneAndUpdate(
{ user: req.user.id });
In below code you are modifying the profile object and saving it. Which is not required. And this is also the reason why you are losing fields.
const updateIndex = profile.experience.map(exp => exp._id).indexOf(req.params.exp_id);
profile.experience[updateIndex] = newExp;
console.log('Experience updated!')
}
await profile.save();
Solution-
We need to figure out the update part of findOneAndUpdate(filter, update).
Here is the update query -
db.collection.update({
"user": "5f96dc85ac5ae03160a024a8",
"experience._id": "5f9826c3a3fa002ce0f11853"
},
{
"$set": {
"experience.$": {
"current": false,
"_id": "5f9826c3a3fa002ce0f11853",
"title": "Senior developer",
"company": "Morgan Stanley",
"location": "Pune",
"from": "2017-04-30T18:30:00.000Z",
"to": "2020-07-08T18:30:00.000Z",
"description": "testing"
}
}
})
Try it here
Trying Mongoose way :
const filter = { user: req.user.id, "experience._id": req.params.exp_id }
const update = { $set: { "experience.$": newExp } }
profile = await Profile.findOneAndUpdate(filter,update);

Mongoose doesn't save all the fields of my POST request

I have a very simple "social network" app: users can register, write posts, like/unlike them and comment a post.
I have a probleme with my Post Schema :
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
// Create Schema
const PostSchema = new Schema({
user: {
type: Schema.Types.ObjectId,
ref: "user",
},
text: {
type: String,
required: true,
},
name: {
type: String,
},
avatar: {
type: String,
},
likes: [
{
user: {
type: Schema.Types.ObjectId,
ref: "user",
},
},
],
comments: [
{
user: {
type: Schema.Types.ObjectId,
ref: "user",
},
},
{
text: {
type: String,
required: true,
},
},
{
name: {
type: String,
},
},
{
avatar: {
type: String,
},
},
{
date: {
type: Date,
default: Date.now,
},
},
],
date: {
type: Date,
default: Date.now,
},
});
module.exports = Profile = mongoose.model("post", PostSchema);
When I receive my POST request for comments...
// #route POST api/posts/comment/:id
// #desc Add comment to post
// #access Private
router.post(
"/comment/:id",
passport.authenticate("jwt", { session: false }),
(req, res) => {
const { errors, isValid } = validatePostInput(req.body);
// Check Validation
if (!isValid) {
// If any errors, send 400 with errors object
return res.status(400).json(errors);
}
Post.findById(req.params.id)
.then(post => {
const newComment = {
text: req.body.text,
name: req.body.name,
avatar: req.body.avatar,
user: req.user.id,
};
console.log("newComment: ", newComment);
// Add to comments array
post.comments.unshift(newComment);
console.log("post: ", post.comments);
// Save
post.save().then(post => res.json(post));
})
.catch(err => res.status(404).json({ postnotfound: "No post found" }));
},
);
The only fields that are saved in the post.comments array is the User. Not the other fields (text, name, avatar, date).
My console.log("newComment: ", newComment); properly returns the complete object with all its properties but then, 2 lines below, console.log("post: ", post.comments); only returns the comment _id and the user and those are the only fields saved in the DB...
What did I miss here?
There is some Problem in the creation of the schema structure, this is the correct way:
comments: [
{
user: {
type: Schema.Types.ObjectId,
ref: "user",
},
text: {
type: String,
required: true,
},
name: {
type: String,
},
avatar: {
type: String,
},
date: {
type: Date,
default: Date.now,
},
}
]
the valid structure is nothing but like this(showing the changes made above):
comments: [{
user: {},
text: {},
// others...
}]

How create collection remotely form mlab api in remote-mongodb using nodejs,expressjs?

I am following Brad Traversy's MERN stack development course and In 4th section, I can't make post request to route localhost:5000/api/profile,
after sending post request with data as handle,status,skills which are fields in my collection, it returns error skills field required.
skills is array of strings sent from user-input.
when I checked collection-profile is created or not then it's not created and only one collection is shown as main collection user,
I followed each line of code in tutorial but getting error, I wanted to get Profile collection to be created at remote mlab mongodb,
my profile model code is
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
// Create Schema
const ProfileSchema = new Schema({
user: {
type: Schema.Types.ObjectId,
ref: "users"
},
handle: {
type: String,
required: true,
max: 40
},
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);
using validator.js module, I am validating input-fields,
if (Validator.isEmpty(data.skills)) {
errors.skills = "Skills field is required";
}
so, I can't find why exactly I can't find new collection as profile?
I tried using mongodb-locally, but it didn't help.
my Github repo link.
I returned user's details when route /api/users/current and changed response on success
before changing:
res.json({ msg: "Success" });
after:
passport.authenticate("jwt", { session: false }),
(req, res) => {
res.json({
id: req.user.id,
name: req.user.name,
email: req.user.email
});
}
);
Here is my commit history,
then It didn't give error as skills field is required,

Resources