Mongoose: How to post into array inside Schema - javascript

I have a Company Schema that will hold some data for that company and an array of posts. When a user submits a post I use passport to decode the token and get some user information. Inside that user information there is an object ID which allows me to find the company that the user belongs to.
So now I have found the company that the user belongs to I need to save the submitted post into the board_posts array inside this company
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const BoardPostSchema = new Schema({
name: {
type: String
}
});
const CompanySchema = new Schema({
company_name: {
type: String
},
board_posts: [BoardPostSchema],
});
module.exports = Company = mongoose.model('companies', CompanySchema);
router.post('/new-opportunity', passport.authenticate('jwt', {
session: false
}), (req, res) => {
let user = req.user;
let newPost = req.body;
let companyId = user.company_id;
const boardPost = {
name: newPost.name
};
Company.find({'_id': companyId})
.then(company => {
// push boardPost into this company.board_posts array
})
.catch(error => {
});
});

An alternative solution with findByIdAndUpdate:
router.post("/new-opportunity", passport.authenticate("jwt", { session: false }), (req, res) => {
let user = req.user;
let newPost = req.body;
let companyId = user.company_id;
const boardPost = {
name: newPost.name,
};
Company.findByIdAndUpdate(
companyId,
{
$push: {
board_posts: boardPost,
},
},
{
new: true,
}
)
.then((company) => {
console.log("Updated compay if found:", company);
res.send(company);
})
.catch((error) => {
console.log(error);
res.status(500);
});
});
Or if you want only update status, you can use updateOne:
router.post("/new-opportunity", passport.authenticate("jwt", { session: false }), (req, res) => {
let user = req.user;
let newPost = req.body;
let companyId = user.company_id;
const boardPost = {
name: newPost.name,
};
Company.updateOne(
{ _id: companyId },
{
$push: {
board_posts: boardPost,
},
}
)
.then((result) => {
console.log(result);
// result.n; // Number of documents matched
// result.nModified; // Number of documents modified
res.send(result);
})
.catch((error) => {
console.log(error);
res.status(500);
});
});

You can use $push and update
router.post('/new-opportunity', passport.authenticate('jwt', {
session: false
}), (req, res) => {
let user = req.user;
let newPost = req.body;
let companyId = user.company_id;
const boardPost = {
name: newPost.name
};
Company.update({_id: user.company_id},{
$push{
//the things you want to add
}
});
});
Hopefully this is what you want to do!

Yes you can use the $pushand findOneAndUpdate operator. It would look nicer if you use the async/await approach.
router.post('/new-opportunity', passport.authenticate('jwt', {
session: false
}), async (req, res) => {
let user = req.user;
let newPost = req.body;
let companyId = user.company_id;
const boardPost = {
name: newPost.name
};
let response = await Company.findOneAndUpdate({'_id': companyId}, {
$push: {
board_posts: "testword1"
}
},{ new: true }) //i set this to true so mongodb will return me the new updated document
res.send(response);
});

Related

Getting erros using passport-google-oauth20 InternalOAuthError: Failed to fetch user profile and Cannot set headers after they are sent to the client

I'm using passport strategies for different socialMedia logins and getting the following two errors
InternalOAuthError: Failed to fetch user profile
Cannot set headers after they are sent to the client
I have doubt there somewhere I have returned a callback or response so getting 2nd error but for 1st don't know reasons scope seems to be correct!
strategy code
passport.use(new GoogleStrategy({
clientID: GOOGLE_CLIENT_ID,
clientSecret: GOOGLE_SECRET_KEY,
callbackURL: GOOGLE_CALLBACK_URL
}, async (acessToken, refreshToken, profile, done) => {
await User.findOne({ email: profile._json.email }, async (err, user) => {
if (err) {
console.log("passport.config --> err", err);
done(err, null);
} else if (user) {
if (user.socialType !== "GOOGLE" || user.socialType === null)
done(`LOGIN_CREDENTIALS_WITH_${(user.socialType || "PASSWORD").toUpperCase()}`, false);
else {
done(null, user);
}
} else {
// console.log(profile);
const user = {
email: profile._json.email,
socialId: profile.id,
socialType: "GOOGLE",
firstName: profile.name.givenName,
lastName: profile.name.familyName,
isActive: profile._json.email_verified,
isVerified: profile._json.email_verified,
socialImageUrl: profile._json.picture,
userType: "CUSTOMER"
};
const newUser = new User({ ...user });
const newUserData = await newUser.save();
done(null, newUserData);
}
});
}));
route code:
router.get('/auth/:socialType', customerCtrl.socialTypeLogin);
router.get('/auth/:socialType/callback', customerCtrl.socialTypeLoginCallback);
controller code:
const socialTypeLogin = async (req, res) => {
await customerService.socialTypeLogin(req, res);
};
const socialTypeLoginCallback = async (req,res) => {
await customerService.socialTypeLoginCallback(req,res);
};
service code:
const socialTypeLogin = async (req, res) => {
try {
const socialType = (req.params.socialType || '').toLowerCase();
const GOOGLE_SCOPE = ['email', 'profile'];
const FACEBOOK_SCOPE = ['email'];
let scope = [];
if (socialType === 'google') {
scope = GOOGLE_SCOPE;
} else if (socialType === 'facebook') {
scope = FACEBOOK_SCOPE;
}
let oauthOptions = { scope: scope};
const { returnUrl } = req.query;
if(returnUrl && returnUrl.trim().length !== 0) {
oauthOptions['state'] =JSON.stringify({ returnUrl: returnUrl });
}
passport.authenticate(socialType, oauthOptions)(req, res);
}
catch (error) {
}
}
/**
* #param {string} socialType
*/
const socialTypeLoginCallback = async (req, res) => {
const socialType = (req.params.socialType || '').toLowerCase();
// return new Promise((resolve, reject) => {
try {
passport.authenticate(socialType, async (err, user) => {
let webappRedirectURL = WEBAPP_LOGIN_URL;
try {
const state = req.query.state;
if(state) {
const stateObj = JSON.parse(state);
webappRedirectURL = stateObj.returnUrl;
}
} catch (err1) {
console.log("customer.service --> parsing error",err1);
}
if (err || !user) {
console.log("customer.service --> !user",err);
res.render('oauth-redirect', {
webappRedirectURL: webappRedirectURL,
success: false,
error: err,
timerCounter: 5,
accessToken: undefined
});
}
else {
console.log("customer.service --> Generating Token",user.generateJWT());
res.render('oauth-redirect', {
webappRedirectURL: webappRedirectURL,
success: true,
timerCounter: 5,
accessToken: user.generateJWT(),
error: undefined
});
}
})(req, res);
}
catch (error) {
console.log("customerService.js ==> socialTypeLoginCallback -->",error);
}
};
Thanks for help in advance!
I have doubt there somewhere I have returned a callback or response so getting 2nd error but for 1st don't know reasons scope seems to be correct!
In socialTypeLogin
add line
oauthOptions['session'] = false;

Check if document exists in collection with Mongoose Model

I want to to check if email already exists in 'users' collection:
I have this model:
const isEmailExists = async (value) => {
const res = await User.countDocuments({ email: value });
return res > 0;
}
const User = mongoose.model('User', {
email: {
type: String,
required: true,
validate(value) {
isEmailExists(value).then(res => {
if (res) {
throw new Error('Email already exists');
}
})
}
}
});
And I use post method with express router:
router
.route('/register')
.get((req, res) => {
res.sendFile(publicDirPath + '/auth/register.html');
})
.post(async (req, res) => {
const user = new User(req.body);
try {
const saveUser = await user.save();
res.send(saveUser);
} catch (error) {
res.send(error);
}
});
For some reason, it does not work and the user is been added anyway..
What am i doing wrong ?
If you want to check if one document with a certain entry/value exists you can do this :
function emailExists(value) {
User.findOne({email: value}).then((err, user) => !!user)
}

Soft delete using nodejs + mongodb

How do i perform a soft delete using nodejs on mongodb
for example using this code, can it be modified to do a soft delete instead or is there another way?
Controllers/ category.js
exports.remove = (req, res) => {
const category = req.category;
category.remove((error, data) => {
if (error) {
return res.status(400).json({
error: errorHandler(error)
});
}
res.json({
message: "Category deleted"
});
});
};
routes/category.js
const express = require("express");
const router = express.Router();
const { create, categoryById, read, update, remove, list } = require("../controllers/category");
const { requireSignin, isAuth, isAdmin } = require("../controllers/auth");
const { userById } = require("../controllers/user");
router.get("/category/:categoryId", read);
router.post("/category/create/:userId", requireSignin, isAuth, isAdmin, create);
router.put("/category/:categoryId/:userId", requireSignin, isAuth, isAdmin, update);
router.delete("/category/:categoryId/:userId", requireSignin, isAuth, isAdmin, remove);
router.post("/categories", list);
router.param("categoryId", categoryById);
router.param("userId", userById);
module.exports = router;
models/category.js
const mongoose = require("mongoose");
const categorySchema = new mongoose.Schema(
{
name: {
type: String,
trim: true,
required: true,
maxlength: 32
}
},
{ timestamps: true }
);
module.exports = mongoose.model("Category", categorySchema);
I'm not sure req.category is instance of model or model itself.
So in my answer below I assume that somehow You've got instance of model and injected it as req.category
1) Add deleted field to schemas where You want to have soft delete:
const mongoose = require("mongoose");
const {Schema} = mongoose;
const categorySchema = new Schema(
{
name: {
type: Schema.Types.String,
trim: true,
required: true,
maxlength: 32
},
// deleted flag for soft delete feature
deleted: {
type: Schema.Types.Boolean,
index: true,
default: false
}
},
{ timestamps: true }
);
module.exports = mongoose.model("Category", categorySchema);
2) Change delete procedure:
module.exports.remove = async (req, res) => {
try {
const category = req.category; // if it's an instance of model
// or if it's mongoose model comment line above
// const category = await req.category.findOne({
// _id: req.params.categoryId,
// deleted: false
// });
if (!category || category.deleted === true) {
return res.status(404).json({
error: 'Requested category does not exist'
});
}
category.deleted = true;
await category.save();
res.status(200).json({
message: "Category deleted"
});
}
catch (error) {
res.status(400).json({
error: errorHandler(error)
});
}
};
3) Change category read route: /category/:categoryId handler:
module.exports.read = async (req, res) => {
try {
const category = req.category; // if it's an instance of model
// or if it's mongoose model comment line above
// const category = await req.category.findOne({
// _id: req.params.categoryId,
// deleted: false
// });
if (!category || category.deleted === true) {
return res.status(404).json({
error: 'Requested category does not exist'
});
}
res.status(200).json(category);
}
catch (error) {
res.status(400).json({
error: errorHandler(error)
});
}
};
4) Change listing procedure:
module.exports.list = async (req, res) => {
try {
const categories = await req.category.find({deleted: false});
res.status(200).json({categories});
}
catch (error) {
res.status(400).json({
error: errorHandler(error)
});
}
};
Step 1:
Add a new field in your Schema "Deleted" with type Boolean and Default value 'false'
deleted: { type: Boolean, default: false }
Step 2
Change Delete Process To:
router.delete('/:id', verifyTokenAndAuthorization, async (req, res) => {
try {
await User.findByIdAndUpdate(req.params.id, { deleted: true }); <= change delete status to 'true'
res.status(200).json('user Deleted');
} catch (error) {
res.status(500).json(error)
}
})

Mongoose not pushing into an array

My code is:
Model:
var mongoose = require('mongoose');
var eventSchema = new mongoose.Schema({
'eventTitle': String,
'location': String,
'startDate': String,
'endDate': String,
'startTime': String,
'endTime': String,
'createdBy': mongoose.Schema.Types.ObjectId, // Here we will store the _ID from the user EOSP.
'attendants': {
'seekers': [mongoose.Schema.Types.ObjectId],
'employers': [],
},
'isFinished': {'type': Boolean, 'default': false},
'uploadedResumes': Number,
'downloadedResumes': Number,
'survey': {
'seekers': [],
'employers': [],
'host': [],
}
});
module.exports = mongoose.model('event', eventSchema);
Router:
.post(async (req, res) => {
let {user, params} = req;
let {eid, uid} = params;
eid = mongoose.Types.ObjectId(eid);
uid = mongoose.Types.ObjectId(uid);
user = user ? user : await Account.findById(uid).catch(e => console.log(e));
if (user.accType.toLowerCase() === 'seeker') {
const {
rating,
recommendation,
websiteEaseOfUse,
chatHelpfulness,
skillsSuitability,
tips,
} = req.body;
const data = {
userId: user._id,
rating,
recommendation,
websiteEaseOfUse,
chatHelpfulness,
skillsSuitability,
tips,
};
console.log(data);
Event.findOneAndUpdate({'_id': eid}, {
$push: {
'survey.seeker': {
userId: user._id,
rating,
recommendation,
websiteEaseOfUse,
chatHelpfulness,
skillsSuitability,
tips,
},
},
}, {$upsert: true,}).then(r => console.log('pushed', r.survey.seekers)).catch(e => console.log(e));
}
const title = await Event.findById(eid).then(r => r.eventTitle).catch(e => console.log(e));
const event = await functions.getCurrentEvent();
res.render('survey/thanks', {
title: title,
user: user,
event: event
})
});
So what this code should do, is take the data passed to the router via post (This actually works), and push it into an array (This doesn't work).
Here's the log:
pushed []
So my question is why even though the console says it passed, (console.log('push')), it doesn't put the data in there? What am I missing?
You are using the wrong key, use survey.seekers instead of survey.seeker
.post(async (req, res) => {
let {user, params} = req;
let {eid, uid} = params;
eid = mongoose.Types.ObjectId(eid);
uid = mongoose.Types.ObjectId(uid);
user = user ? user : await Account.findById(uid).catch(e => console.log(e));
if (user.accType.toLowerCase() === 'seeker') {
const {
rating,
recommendation,
websiteEaseOfUse,
chatHelpfulness,
skillsSuitability,
tips,
} = req.body;
const data = {
userId: user._id,
rating,
recommendation,
websiteEaseOfUse,
chatHelpfulness,
skillsSuitability,
tips,
};
console.log(data);
Event.findOneAndUpdate({'_id': eid}, {
$push: {
'survey.seekers': {
userId: user._id,
rating,
recommendation,
websiteEaseOfUse,
chatHelpfulness,
skillsSuitability,
tips,
},
},
}, {$upsert: true,}).then(r => console.log('pushed', r.survey.seekers)).catch(e => console.log(e));
}
const title = await Event.findById(eid).then(r => r.eventTitle).catch(e => console.log(e));
const event = await functions.getCurrentEvent();
res.render('survey/thanks', {
title: title,
user: user,
event: event
})
});

Mongoose assign a collection to another

I am trying to add a post to a user collection after the user was created with empty posts. I have tried with populate with no success .. any help is much appreciated.
// Post Model
const mongoose = require('mongoose');
const { Schema } = mongoose;
const UserModel = require('./user-model');
let PostSchema = new Schema({
author: {
ref: 'users',
type: Schema.Types.ObjectId
},
content: String,
description: String,
date: {
default: new Date(),
type: Date
},
title: String,
updatedAt: {
default: new Date(),
type: Date
}
});
let PostModel = mongoose.model('posts', PostSchema);
module.exports = PostModel;
// User Model
const mongoose = require('mongoose');
const { Schema } = mongoose;
const PostModel = require('./post-model');
let UserSchema = new Schema({
name: {
type: String
},
email: {
lowercase: true,
type: String,
trim: true,
unique: true
},
password: {
type: String,
},
postList: [{
ref: 'posts',
type: Schema.Types.ObjectId
}],
});
const UserModel = mongoose.model('users', UserSchema);
module.exports = UserModel;
// save post controller
exports.savePost = (request, response, next) => {
let { author, description, title } = request.body;
let post = new PostModel({ author, description, title }).save();
UserModel.findById(author)
.then((user) => {
user.postList.push(post);
// Still Fails
// How can i assign the post to the user ?
});
}
Is there any way of doing this other then push or populate ?
To solve this problem I prefer to use $push of mongo
UserModel.findOneAndUpdate({
_id: author.id,
{
$push: {
postList: post
}
}
});
You need to follow this process to save successfully
Save post if success then
Update user to push postId in postlist
can try this one
exports.savePost = (request, response, next) => {
let post = new PostModel(request.body)
post.save(function(err, data) {
if(err) {
//return error
}
// check by console your author is present or not
// let author in your req.body
let author = req.body.author
UserModel.findOneAndUpdate({_id: author},{$push: {postList: post._id}},{new:true} function(error, user) {
if(error) {
// return error
}
console.log(user)
// return success
})
});
}
exports.savePost = (request, response, next) => {
let { user, description, title } = request.body;
let post = new PostModel({ user, description, title });
post.save()
.catch((error) => {
if (error)
throw new Error(error);
})
.then((post) => {
UserModel.findOneAndUpdate({ _id: user }, {$push: { postList: post._id } })
.populate('postList')
.catch((error) => {
if (error)
throw new Error(error);
})
.then((user) => {
user.postList.forEach((item, postion) => {
console.log(`${item} -at ${postion} \n`);
});
});
});
}
This is what i did and it worked after all. I don't know if this is the correct solution but this is working.

Categories