Find subdocument object in another db - arrays

I'm trying to check each email of attendees and see if they are a registered user. If not, I will send them an email (not yet coded, will do later).
Here's the event and user schema:
const UserSchema = new Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true
},
password: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now
}
});
const Event = new Schema({
title: {
type: String,
required: true
},
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'users'
},
attendees:[
{email: {
type: String,
required: true
},
name: {
type: String,
required: true
},
status: {
type: String
}}
]
});
router.post('/', auth, async (req, res) => {
const {title,
attendees
} = req.body
if (!title) {
return res.status(400).json({ msg: 'Please enter a title' });
}
try{
const newEvent = new Event({
title,
user: req.user.id,
attendees: attendees.map(x => ({
email: x.email,
name: x.name,
status: x.status,
})),
});
const attendeeExists = await User.findOne({"attendees.email":email});
if (!attendeeExists) throw Error("User doesn't exist. Send email");
The last two lines are giving me an error: email is not defined.
Not sure what I'm missing.
This works in the user routes:
const user = await User.findOne({ email });

Thanks #ambianBeing, your solution helped me get a working model.
const email = attendees.map((a) => a.email);
const attendeesFound = await User.find({email});

For checking any of the attendee's email found, .find() with $in can be used which'll return the users found with any of the email ids.
/*collect all emails to test*/
const emails = attendees.map((a) => a.email);
const attendeesFound = await User.find({ "email": { $in: emails } });
Another Mongoose syntax wihich does the same thing as above:
/*collect all emails to test*/
const emails = attendees.map((a) => a.email);
const attendeesFound = await User.find({}).where("email").in(emails);

Related

Mongoose deleting subdocuments from arrays using $pull

Hi I have a comment and post model that has a one-to-many relationship. It successfully creates a comments array and pushes the object id correctly, but when I try to delete a comment using $pull It doesn't delete or show errors just returns the object back unchanged and also I'm not sure how to update subdocuments either , it doesn't work
In comment.js
const mongoose = require("mongoose");
const AutoIncrement = require('mongoose-sequence')(mongoose);
const CommentSchema = new mongoose.Schema({
username: {
type: String,
required: true,
},
content:{
type: String,
required: true
},
createdAt:{
type: Date,
default: new Date()
},
})
CommentSchema.plugin(AutoIncrement, {inc_field: 'commentID'});
module.exports = mongoose.model("Comment", CommentSchema);
In post.js
const mongoose = require("mongoose");
const AutoIncrement = require('mongoose-sequence')(mongoose);
const PostsSchema = new mongoose.Schema({
title: {
type: String,
required: true
},
content: {
type: String,
required: true
},
createdAt:{
type: Date,
default: new Date()
},
comments: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Comment"
}
]
});
PostsSchema.plugin(AutoIncrement, {inc_field: 'postID'});
module.exports = mongoose.model("Post", PostsSchema);
For the routes
router.post("/:id/comments", CommentController.createComment)
router.get("/:id/comments", CommentController.viewComments)
router.delete("/:id/comments/:commentID", CommentController.deleteComment)
router.patch("/:id/comments/:commentID", CommentController.updateComment)
And the controllers
const commentsModel = require("../models/comments")
const PostsModel = require("../models/posts");
const mongoose = require("mongoose")
const objID = mongoose.Types.ObjectId;
async function createComment(req, res){
try{
const comment = await commentsModel.create(req.body)
const data = await PostsModel.findOneAndUpdate(
{postID: req.params.id},
{$push: {comments: objID(comment._id)}},
{ new: true, useFindAndModify: false }
)
res.status(200).json({data})
}catch (e) {
res.status(404).json(e.message)
}
}
async function viewComments(req, res){
try{
const data = await PostsModel.findOne({postID: req.params.id}).populate("comments");
if(!data){
return res.status(404).json({msg: "No data found"});
}
res.status(200).json({comments: data.comments});
}catch (e) {
res.status(500).json(e.message);
}
}
async function deleteComment(req, res){
try{
const post = await PostsModel.update({ postID: req.params.id },
{
'$pull': {
comments: {
_id: objID(req.params.commentID)
}
}
}, {new: true});
if (!post){
return res.status(404).json({msg: "No post found"});
}
res.status(200).json({post});
}catch (e) {
res.status(500).json(e.message);
}
}
async function updateComment(req, res){
try{
const post = await PostsModel.findOneAndUpdate(
{"postID": req.params.id, "comments._id": objID(req.params.commentID)},
{
"$set":{
"comments.$.username": req.body.username,
"comments.$.content": req.body.content
}
}
)
if (!post){
return res.status(404).json({msg: "No post found"});
}
res.status(200).json({post});
}catch (e) {
res.status(500).json(e.message);
}
}

Mern stack bcrypt password works many times, and suddenly doesn't match

i have a strange problem and don't find the same so i search some help :) :
I make a mern stack social media, and first all is okay with register new user and logged in.
I'm on development of the app, so i login and logout a lot. And after a few times, i don't count, maybe 20 or 30, the password is not recognized, and i can't login with the account.
So i make a new user to continue my work, and the same thing happen after a few logins.
Here is my userSchema
const mongoose = require("mongoose");
const {isEmail} = require("validator");
const bcrypt = require("bcrypt");
function validEmail(email) {
if(!isEmail(email)) {
return false;
}
else {
return true
}
}
const userSchema = new mongoose.Schema(
{
pseudo: {
type: String,
required: true,
minLength: 3,
maxLength: 20,
unique: true,
trim: true
},
email: {
type: String,
required: true,
lowercase: true,
trim: true,
unique: true,
validate: validEmail
},
password: {
type: String,
required: true,
maxLength: 1024,
minLength: 6
},
dimension: {
type: String,
},
galaxy: {
type: String,
},
picture: {
type: String,
default: "./uploads/profil/random-user.png"
},
bio: {
type: String,
maxLength: 1024
},
followers: {
type: [String],
},
following: {
type: [String]
},
likes: {
type: [String]
}
},
{
timestamps: true,
}
)
// Play function before save into display: block
userSchema.pre("save", async function(next) {
const salt = await bcrypt.genSalt();
this.password = await bcrypt.hash(this.password, salt);
next();
})
userSchema.statics.login = async function(email, password) {
const user = await this.findOne({email});
console.log(user)
if(user) {
const auth = await bcrypt.compare(password, user.password);
if(auth) {
return user;
} else {
throw Error("Incorrect password");
}
}
throw Error("Incorrect email");
}
const UserModel = mongoose.model("user", userSchema);
module.exports = UserModel;
And here the login function in my auth.controller.js:
module.exports.signIn = async (req, res) => {
const { email, password } = req.body;
try {
const user = await UserModel.login(email, password);
const token = createToken(user._id);
res.cookie("jwt", token, {
maxAge: maxAge, sameSite: "none",
secure: true,
})
res.status(200).json({ user: user._id })
}
catch (err) {
const errors = signInErrors(err);
res.status(200).json({ errors });
// console.log(err)
}
}
It's my first project with back-end side, and i follow a tutorial from 2020 to make it, so maybe there is new best practices to do it now. But it seems very strange that it works fine with new user, and not after some connections. I hope someone have an idea to try to help me :)
I find this and try, seems to work for me after 2 days, with adding a condition on the pre hook like this:
userSchema.pre("save", async function(next) {
if(!this.isModified("password")) {
return next()
}
const salt = await bcrypt.genSalt();
this.password = await bcrypt.hashSync(this.password, salt);
next();
})
Hope it help someone

Allow user to see all of their posts using mongoose / nextjs / react

I am trying to create a my-posts page which will consist of all the posts made by the user.
User Schema
const userSchema = new Mongoose.Schema({
email: { type: String},
password: { type: String},
name: { type: String},
createdAt: { type: String},
posts: [{ type: Mongoose.Schema.Types.ObjectId, ref: 'Post'}]
},{
timestamps: true,
});
Posts Schema
const postSchema = new Mongoose.Schema({
name: { type: String},
category: { type: String},
userId: {type: Mongoose.Schema.Types.ObjectId, ref: "User"},
},{
timestamps: true
});
Creating a new post
handler.post(async(req, res) => {
await db.connect();
const newPost = new Post({
name: req.body.name,
category: req.body.category,
userId: req.body.userId
})
const userBy = await User.findById(userId)
const thePost = await newPost.save();
userBy.posts = user.posts.concat(post._id)
await user.save()
await db.disconnect();
});
export default handler;
Retrieve 'My Posts'
export async function getServerSideProps( {query} ) {
await db.connect();
const data = await User.findById(req.query.id).populate("vehicles").lean
await database.disconnect();
const userPosts = data.map(database.convertObj)
return {
props: {
userPosts
}
}
}
I'm not too sure how to pass the current logged in users _id to the getServerSideProps to then query the database for all the posts attached to that user in the posts array. If there is a better way to approach this please let me know or if you know what I am currently doing wrong, thanks.
This is an area where you might want to use a request from the client side to get the data, as suggested by the Nextjs docs rather than using getServerSideProps.
But if you really want to do it with SSR, you can use getServerSideProps and pass in data (like the userId) to getServerSideProps through the context parameter as either a query or a parameter.
If you want to do it as a query, you can get query as a prop, as you are already doing, or you can do it using a param like in this codesandbox example

TypeError: Cannot read property 'upvotes' of null

I am working on basic Stack Overflow clone using Node Js and MongoDB. When I was testing upvote I fall in error TypeError: Cannot read property 'upvotes' of null. I tried everything on the internet but didn't get anything working. I am getting an error for the code below
if (question.upvotes.filter(upvote => upvote.user.toString() === req.user.id.toString()).length > 0)
You can see the above syntax in API Code for upvote route.
This is my model for upvotes:
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const QuestionSchema = new Schema({
user: {
type: Schema.Types.ObjectId,
ref: "myPerson"
},
textone: {
type: String,
required: true
},
texttwo: {
type: String,
required: true
},
name: {
type: String
},
upvotes: [
{
user: {
type: Schema.Types.ObjectId,
ref: "myPerson"
}
}
],
answers: [
{
user: {
type: Schema.Types.ObjectId,
ref: "myPerson"
},
text: {
type: String,
required: true
},
name: {
type: String
},
date: {
type: Date,
default: Date.now
}
}
],
date: {
type: Date,
default: Date.now
}
});
module.exports = Question = mongoose.model("myQuestion", QuestionSchema);
Below i have pasted my API for upvote
const express = require("express");
const router = express.Router();
const mongoose = require("mongoose");
const passport = require("passport");
const jwt = require("../../strategies/jsonwtStrategy")(passport);
//Load person model
const Person = require("../../models/Person");
//Load profile model
const Profile = require("../../models/Profile");
//Load Question model
const Question = require("../../models/Question");
//#type GET
//#route /api/questions
// #desc route for getting questions
// #access PUBLIC
router.get("/", (req, res) => {
Question.find()
.sort("-date")
.then(questions => res.json(questions))
.catch(err => res.json({ noquestions: "No questions to display" }));
});
//#type POST
//#route /api/questions/
// #desc route for submitting question
// #access PRIVATE
router.post(
"/",
passport.authenticate("jwt", { session: false }),
(req, res) => {
const newQuestion = new Question({
textone: req.body.textone,
texttwo: req.body.texttwo,
user: req.user.id,
name: req.body.name
});
newQuestion
.save()
.then(question => res.json(question))
.catch(err => console.log("unable to post question" + err));
}
);
//#type POST
//#route /api/questions/answers/:id
// #desc route for submitting answers to questions
// #access PRIVATE
router.post(
"/answers/:id",
passport.authenticate("jwt", { session: false }),
(req, res) => {
Question.findById(req.params.id)
.then(question => {
const newAnswer = {
user: req.body.id,
name: req.body.name,
text: req.body.text
};
question.answers.unshift(newAnswer);
question
.save()
.then(question => res.json(question))
.catch(err => console.log(err));
})
.catch(err => console.log(err));
}
);
//#type POST
//#route /api/questions/upvote/:_id
// #desc route for upvoting
// #access PRIVATE
router.post(
"/upvote/:id",
passport.authenticate("jwt", { session: false }),
(req, res) => {
Profile.findOne({ user: req.user.id })
.then(profile => {
Question.findById(req.params.id)
.then(question => {
if (
question.upvotes.filter(
upvote => upvote.user.toString() === req.user.id.toString()
).length > 0
) {
return res.status(400).json({ noupvote: "User already upvoted" });
}
question.upvotes.unshift({ user: req.user.id });
question
.save()
.then(question => res.json(question))
.catch(err => console.log(err));
})
.catch(err => console.log(err));
})
.catch(err => console.log(err));
}
);
module.exports = router;
Error i'm getting..
TypeError: Cannot read property 'upvotes' of null
at C:\Users\Subhash Chaudhary\Desktop\NodeJsLearning\09bigstack\routes\api\questions.js:87:24
at processTicksAndRejections (internal/process/task_queues.js:93:5)
you can do this using mongoose schema like ;
var mongoose = require('mongoose');
var id = mongoose.Types.ObjectId(req.params.id);
Question.findById(id);
here, we convert the id in to the '_id' or we can say that 'ObjectId' of mongoose which is the schema type object id.
it happened because in the tutorial instructor was using MLab which returns id and I was using MongoDB which returns _id. Question.findById(req.params.id) returned nothing then i changed it to Question.findById(req.params._id) and it worked. The suggestion i got in comment.

Need help saving a game to a user's favorited games

I'm receiving an error when trying to associate a saved game to the user that saves it. The error says "cannot read property push of undefined"
The user, and game can be read in the console. I think it may have something to do with the user model during the initial creation of the user, however I can't be sure. I did notice if I try to console.log(user.favGames) it will be returned undefined.
I've tried everything I can think of, I've re-written the controller roughly 10 times, to no avail.
user model
const mongoose = require('mongoose')
const bcrypt = require('bcrypt')
const SALT_ROUNDS = 6
const Schema = mongoose.Schema
const userSchema = new Schema(
{
username: { type: String, unique: true },
email: { type: String, unique: true, unique: true },
password: { type: String, required: true },
avatar: { type: String },
favGames: { type: Schema.Types.ObjectId, ref: 'Game', default: null },
comments: { type: Schema.Types.ObjectId, ref: 'Comment', default: null }
},
{
timestamps: true
}
)
userSchema.set('toJSON', {
transform: function(doc, ret) {
delete ret.password
return ret
}
})
userSchema.pre('save', function(next) {
const user = this
if (!user.isModified('password')) return next()
bcrypt.hash(user.password, SALT_ROUNDS, function(err, hash) {
if (err) return next()
user.password = hash
next()
})
})
userSchema.methods.comparePassword = function(tryPassword, cb) {
bcrypt.compare(tryPassword, this.password, cb)
}
module.exports = mongoose.model('User', userSchema)
game model
const mongoose = require('mongoose')
const Schema = mongoose.Schema
let gameSchema = new Schema({
name: { type: String, required: true },
boxArtUrl: { type: String, required: true },
twitchID: { type: String, required: true },
comments: { type: Schema.Types.ObjectId, ref: "Comment"}
})
module.exports = mongoose.model('Game', gameSchema)
game router
const express = require('express')
const router = express.Router()
const gamesCtrl = require('../../controllers/gameCtrl')
function isAuthed(req, res, next) {
if (req.user) return next()
return res.status(401).json({ msg: 'Unauthorized ' })
}
router.get('/')
router.post('/', isAuthed, gamesCtrl.addGame)
module.exports = router
game controller
const User = require('../models/user')
const Game = require('../models/Game')
function addGame(req, res) {
Game.create({
name: req.body.name,
twitchID: req.body.id,
boxArtUrl: req.body.box_art_url
})
.then(game => {
User.findById(req.user._id)
.then(user => {
console.log(game)
console.log(user.favGames)
// user.favGames.push(game)
// user.save()
})
.catch(err =>
console.log('error when updating user with new game', err)
)
})
.catch(err => console.log('error saving game', err))
}
module.exports = {
addGame
}
the error is flagged in my controller at user.favGames.push(game). Note that when a user creates a profile there are no games associated with their profile. I'm pretty sure I'm calling on the actual data instance of the model, not the model itself. Thanks in advance for your assistance.
Your favGames (and also comments) must be defined as array in user model like this.
const userSchema = new Schema(
{
username: { type: String, unique: true },
email: { type: String, unique: true, unique: true },
password: { type: String, required: true },
avatar: { type: String },
favGames: [{ type: Schema.Types.ObjectId, ref: 'Game', default: null }],
comments: [{ type: Schema.Types.ObjectId, ref: 'Comment', default: null }]
},
{
timestamps: true
}
)
Also user.save() returns a promise, so you need use then block, or await.
So the addGame function must be like this (I converted the code to async/await)
async function addGame(req, res) {
try {
let game = await Game.create({
name: req.body.name,
twitchID: req.body.id,
boxArtUrl: req.body.box_art_url
});
let user = await User.findById(req.user._id);
if (user) {
user.favGames.push(game);
await user.save();
res.status(200).send("game and user saved");
} else {
console.log("user not found");
res.status(404).send("user not found");
}
} catch (err) {
console.log("Err: ", err);
res.status(500).send("Something went wrong");
}
}
Looks like it's a matter of checking to see if it exists:
User.findById(req.user._id)
.then(user => {
if (!Array.isArray(user.favGames)) {
user.favGames = [];
}
user.favGames.push(game);
user.save();
})

Resources