Mongoose toObject - node.js

I have the following Mongoose Schema:
const UserSchema = new Schema({
email: {
type: String,
minlength: 1,
required: true,
trim: true,
unique: true,
validate: {
validator: isEmail,
message: '{VALUE} is not a valid email.',
},
},
emailPhrase: {
type: String,
},
tokens: [
{
access: {
type: String,
required: true,
},
token: {
type: String,
required: true,
},
},
],
});
And the following pre-hook:
UserSchema.pre('save', function (next) {
const user = this;
if (!user.toObject().tokens[0].token) {
// do something
next();
} else {
// do something else
next();
}
});
The issue is, even when the tokens property is completely empty, the first case (do something) doesn't run. What am I doing wrong here?

You need to change your if in the pre hook method:
UserSchema.pre('save', function (next) {
const user = this;
if (user.toObject().tokens && user.toObject().tokens.length > 0) {
console.log('do something');
next();
} else {
console.log(' do something else');
next();
}
});
There are a lot of ways for validate a array, here one link with some options:
Check is Array Null or Empty

Related

How to disable/inactive the data in mongoDB database instead of delete using nodejs

when I hit the delete method in postman I need to disable the data in MongoDB instead of completely delete. how to do that?
router.delete("/admin/delete_profile/:id", async (req, res) => {
try {
await SomeModel.findByIdAndDelete(req.params.id.trim());
return send(res, RESPONSE.SUCCESS);
} catch (err) {
// res.status(404).send(err.message);
return send(res, RESPONSE.UNKNOWN_ERROR);
}
});
schema.js
const { json } = require("body-parser");
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const SomeModelSchema = new Schema({
first_name: {
type: String,
required: true,
},
last_name: {
type: String,
required: true,
},
image: {
data: Buffer,
type: String,
required: true,
},
});
module.exports = mongoose.model("SomeModel", SomeModelSchema);
Actually, the approach suggested by #PawanYadav is a good one in my opinion.
Declare a Boolean flag isActive in your Schema (default to true):
const SomeModelSchema = new Schema({
first_name: {
type: String,
required: true,
},
last_name: {
type: String,
required: true,
},
image: {
data: Buffer,
type: String,
required: true,
},
isActive: {
type: Boolean,
default: true,
}
});
And use findByIdAndUpdate to set the flag to false to disable the data:
try {
await SomeModel.findByIdAndUpdate(req.params.id.trim(), {
isActive: false,
});
return send(res, RESPONSE.SUCCESS);
} catch (err) {
// res.status(404).send(err.message);
return send(res, RESPONSE.UNKNOWN_ERROR);
}
You can keep a key like isActive:true in your database for soft delete purpose.When you hit delete api you can simply change this key to false.In this way you can differentiate this document from others and when you want list you can check for documents with isActive:true in your query.

Delete the associated blog posts with user before deleting the respective user in mongodb and nodejs

When I am deleting a user, I also want to delete all the associated blog posts with that user. I have used MongoDB's pre() middleware. when it is fired it only sets the postedBy property to null in the post and then MongoDB compass the postedBy is still there along with userId
here is User schema.
const crypto = require("crypto");
const mongoose = require("mongoose");
const Post = require("./post");
const userSchema = mongoose.Schema(
{
username: {
type: String,
trim: true,
required: true,
unique: true,
index: true,
lowercase: true,
},
name: {
type: String,
index: true,
required: true,
max: 32,
},
email: {
type: String,
index: true,
required: true,
trim: true,
unique: true,
},
hashed_password: {
type: String,
required: true,
},
role: {
type: Number,
default: 0,
},
profile: {
type: String,
required: true,
},
photo: {
data: Buffer,
contentType: String,
},
salt: String,
resetPassword: {
data: String,
default: "",
},
},
{ timestamp: true }
);
userSchema
.virtual("password")
.set(function (password) {
this._password = password;
this.salt = this.makeSalt();
this.hashed_password = this.encryptPassword(password);
})
.get(function () {
return this._password;
});
userSchema.methods = {
authenticate: function (plainText) {
return this.encryptPassword(plainText) === this.hashed_password;
},
encryptPassword: function (password) {
if (!password) return "";
try {
return crypto
.createHmac("sha1", this.salt)
.update(password)
.digest("hex");
} catch (err) {
return "";
}
},
makeSalt: function () {
return Math.round(new Date().valueOf() * Math.random()) + "";
},
};
userSchema.pre("findByIdAndRemove", function (next) {
Post.deleteMany({ postedBy: this._id }, function (err, result) {
if (err) {
console.log("error");
} else {
console.log(result);
}
});
next();
});
module.exports = mongoose.model("User", userSchema);
here is Post schema
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const postSchema = new Schema({
postedBy: {
type: Schema.Types.ObjectId,
ref: "User",
},
title: {
type: String,
},
body: {
type: String,
},
name: {
type: String,
},
photo: {
data: Buffer,
contentType: String,
},
updated: Date,
avatar: {
type: String,
},
created: {
type: Date,
default: Date.now,
},
comments: [
{
postedBy: {
type: Schema.Types.ObjectId,
refPath: "onModel",
},
text: String,
created: {
type: Date,
default: Date.now,
},
},
],
likes: [
{
type: Schema.Types.ObjectId,
refPath: "onModel",
},
],
onModel: {
type: String,
enum: ["User", "Imam"],
},
});
module.exports = mongoose.model("Post", postSchema);
this is delete route function
exports.userdelete = (req, res) => {
User.findByIdAndRemove(req.params.id).exec((err, doc) => {
if (err) {
return res.status(400).json({
error: "Something went wrong",
});
}
return res.json({
message: "User deleted",
});
});
};
You Can follow this code
You can use cascade delete in mongoose
User.findOne({...}, function(err, customer) {
Post.remove();
});

Mongoose Schema pre save doesn't return on call to next()

I'm trying to build a simple application that requires user sign up and login functionality. I have defined the user schema as follows:
// User Schema
const userSchema = new mongoose.Schema({
username: {
type: String,
required: true,
trim: true,
},
password: {
type: String,
required: true,
minlength: 7,
trim: true,
validate(value) {
if (value.toLowerCase().includes('password')) {
throw new Error('Password cannot contain "password"')
}
}
},
email: {
type: String,
unique: true,
required: true,
trim: true,
lowercase: true,
validate(value) {
if (!validator.isEmail(value)) {
throw new Error('Email is invalid')
}
}
},
name: {
// TODO : Add validation for name
type: String,
required: true
},
mobile:{
// TODO : Add validation for mobile numbers
type: String
}
}, {
timestamps: true
});
userSchema.pre('save', async function (next) {
const user = this
console.log("inside pre")
if (user.isModified('password')) {
console.log("about to hash ", user.password )
user.password = await bcrypt.hash(user.password, 8)
console.log("hashed pwd ", user.password )
}
next();
})
and in my routes, I have the following:
router.post('/users', async (req, res) => {
const user = new User(req.body)
try {
console.log("about to save ")
await user.save()
console.log("About to gen token")
const token = await user.generateAuthToken()
console.log("About to send res")
res.status(201).send({ user, token })
} catch (e) {
res.status(400).send(e)
}
})
Both the original password and the plaintext prints but the response is never sent. The line right after the save function call never gets executed. Why might this be happening?
Remove the async from the .pre('save'. You cannot mix promises with callbacks. You can use either, but you cannot use both.

Nodejs:Array data not added in mongodb

This is my model profile.js
var mongoose = require('mongoose');
const ProfileSchema = mongoose.Schema({
educationinfo: [{
universityname:
{
type: String,
required: true
},
degree:
{
type: String,
required: true
},
coursecompletionyear:
{
type: String,
required: true
},
collegename:
{
type: String,
required: true
},
specialization:
{
type: String,
required: true
},
marks:
{
type: String,
required: true
},
courselevel:
{
type: String,
required: true
}
}]
});
const Profile = module.exports = mongoose.model('Profile', ProfileSchema);
This is my route.js post function
router.post('/addprofiledetails', function(req, res, next) {
let newProfile = new Profile({
$educationinfo:[{
universityname:req.body.universityname,
degree:req.body.degree
}]
});
newProfile.save((err, profile) => {
if (err) {
res.json({ msg: 'Failded to add profiledetails' });
} else {
res.json({ msg: 'successfully add profile details' });
}
});
});
I got success msg in post function but the data not added in mongodb. i don't know where i did mistake .please help me.
In mongoDB I got data like,
{
"educationinfo": [],
"_id": "5bed14b93a107411a0334530",
"__v": 0
}
I want details inside educationinfo, please help.
You need to change schema definition and query.
1.remove required from schema Or apply required to those field that you must provide value.
educationinfo: [{
universityname:
{
type: String,
// required: true
},
degree:
{
type: String,
//required: true
},
coursecompletionyear:
{
type: String,
// required: true
},
collegename:
{
type: String,
// required: true
},
specialization:
{
type: String,
//required: true
},
marks:
{
type: String,
// required: true
},
courselevel:
{
type: String,
// required: true
}
}]
2.change $educationinfo with educationinfo
educationinfo:[{
universityname:req.body.universityname,
degree:req.body.degree
}]
Since you marked the properties of educationinfo as required, you need to provide them when you create an instance of Profile. If you don't want to do that you need to remove the required property from those properties that you won't be supplying on instance creation like below:
const mongoose = require('mongoose');
const ProfileSchema = mongoose.Schema({
educationinfo: [{
universityname:
{
type: String,
required: true
},
degree:
{
type: String,
required: true
},
coursecompletionyear:
{
type: String
},
collegename:
{
type: String
},
specialization:
{
type: String
},
marks:
{
type: String
},
courselevel:
{
type: String
}
}]
});
const Profile = module.exports = mongoose.model('Profile', ProfileSchema);
After making those changes, you need to make one more change in your POST route, change $educationinfo to educationinfo
router.post('/addprofiledetails', function(req, res, next) {
const newProfile = new Profile({
educationinfo:[{
universityname: req.body.universityname,
degree: req.body.degree
}]
});
newProfile.save((err, profile) => {
if (err) {
res.json({ msg: 'Failded to add profiledetails' });
} else {
res.json({ msg: 'successfully add profile details' });
}
});
});
The data you insert is incomplete.
The properties in your schema marked as required: true need to be inserted aswell. Because you do not meet the schema requirements, it is failing.

MEAN Stack - Update route not posting correctly

I have built a mean app but am having an issue with it posting a number value. I'm not sure if it is a mongoose validation error but for some reason mongoose can not upsert the number value but will when it is a string.
Here's the route:
//Edit A Site
router.put('/api/sites/:site_id', function(req, res) {
Site.findById(req.params.site_id, function(err, site) {
if (err) {
res.send(err);
} else {
if(req.body.ip) site.ip = req.body.ip;
if(req.body.domain) site.domain = req.body.domain;
if(req.body.wp) site.wp = req.body.wp;
if(req.body.host_name) site.host_name = req.body.host_name;
if(req.body.hosted) site.hosted = req.body.hosted;
console.log(req.body);
// save the site
site.save(function(err) {
if (err)
res.send(err);
res.json(site);
});
}
});
});
The console.log has the full request body:
{ hosted: 1, host_name: 'sup', wp: 'n/a' }
But this is the mongoose response: Mongoose: sites.update({ _id: ObjectId("57a16c4a7f7e5b7a7e1f5ad1") }, { '$set': { host_name: 'sup', wp: 'n/a' } })
Schema:
// grab the things we need
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
// create a schema
var sitesEntrySchema = new Schema({
ip: {
type: String,
required: true,
trim: true
},
domain: {
type: String,
required: true,
trim: true
},
wp: {
type: String,
required: true,
trim: true
},
host_name: {
type: String,
required: true
},
hosted: {
type: Number,
min: 0,
max: 1,
required: true
}
});
// make this available to our users in our Node applications
var Site = mongoose.model('Site', sitesEntrySchema);
module.exports = Site;
EDIT:
I believe I found the solution. When checking for the req.body.hosted, because it is a number it fails. I had to update to check for undefined:
if(req.body.hosted != undefined) site.hosted = req.body.hosted;

Resources