Mongoose unable to get _id from Schema - node.js

I 've a UserSchema that looks like:
export var UserSchema: Schema = new mongoose.Schema({
createdAt: Date,
email: {
type: String,
required: true,
trim: true,
unique: false,
},
firstName: {
type: String,
required: false,
trim: true
},
lastName: {
type: String,
required: false,
trim: true
},
password: {
type: String,
trim: true,
minlength: 6
},
tokens: [{
access: {
type: String,
required: true
},
token: {
type: String,
required: true
}
}]
});
And I 've a instance method like:
UserSchema.methods.printThis = () => {
var user = this;
console.log("========>>> PRINTING USER NOW");
console.log(user);
};
The method printThis is being called from
router.post('/signup', (req, res) => {
var body = _.pick(req.body, ['email', 'password']);
var user = new User(body);
console.log("created user as: ", user);
user.printThis();
});
Below is the output:
created user as: { email: 'prsabodh.r#gmail.com',
password: '123456',
_id: 59be50683606a91647b7a738,
tokens: [] }
========>>> PRINTING USER NOW
{}
You can see that the user is getting created properly. However, when I call printThis method on User - I'm not able to print the same user back and an empty {} is printed. How to fix this?

You shouldn't use arrow functions (=>) if the calling function is explicitly setting a context (which is what Mongoose does):
UserSchema.methods.printThis = function() {
var user = this;
console.log("========>>> PRINTING USER NOW");
console.log(user);
};
More info on arrow functions and their handling of this here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this#Arrow_functions

To get the _id value from the instance method can use _conditions that should work
UserSchema.methods.printThis = function(password) {
var user = this;
console.log(user._conditions['_id']);
};

Related

Log a user in and get their profile

I am attempting to log a user in to my DB. When I log the user in, it returns the first userId in the DB and not the user who logged in. I have been struggling with this for a while and really am at a dead end.
This is my POST route to log the user in:
// login
router.post("/login", async (req, res) => {
const user = await User.findOne({
email: req.body.email,
});
const secret = process.env.SECRET;
if (!user) {
return res.status(400).send("the user not found!");
}
if (user && bcrypt.compareSync(req.body.password, user.passwordHash)) {
const token = jwt.sign(
{
userId: user.id,
isAdmin: user.isAdmin,
},
secret,
{ expiresIn: "1d" }
);
res.status(200).send({ user: user.email, token: token });
} else {
res.status(400).send("password is wrong!");
}
});
The const user = await User.findOne({ email: req.body.email, }); this returns the wrong user.
When I query the endpoint get a users profile with the userId it gets the right information. So its got nothing to do with the DB.
This is the call in the app.
const handleSubmit = () => {
axios
.post(`${baseURL}users/login`, {
email: email,
passwordHash: password,
})
.then(res => {
console.log('USER ID TOKEN', res.data.token);
setbearerToken(res.data.token);
AsyncStorage.setItem('bearerToken', res.data.token);
const decoded = decode(res.data.token);
setTokenID(decoded.userId);
dispatch(setUser(res.data));
});
};
user.js model
const userSchema = mongoose.Schema({
contactName: {
type: String,
required: true,
minlength: 5,
maxlength: 50
},
phone: {
type: String,
required: true,
minlength: 5,
maxlength: 50
},
passwordHash: {
type: String,
required: true,
minlength: 5,
maxlength: 1024
},
token: {
type: String,
},
isAdmin: {
type: Boolean,
default: false
},
clubName: {
type: String,
required: true,
},
clubAddress: {
type: String,
required: true,
},
clubEmail: {
type: String,
required: true,
},
clubPhone: {
type: String,
required: true,
},
clubWebsite: {
type: String,
required: true,
},
clubContact: {
type: String,
required: true,
},
})
Your schema doesn't have a field email to filter on.
const user = await User.findOne({
email: req.body.email,
});
Maybe you try clubEmail field. I reproduced the behavior and it looks like that mongoose ignores the filter if the field does not exist in the Schema an just returns the first document in the collection.
E.g.
const userSchema = new Schema(
{
name: String,
age: Number
}
)
const User = mongoose.model('User', userSchema);
User.findOne({name: "Superman"}, ...
Returns the user with name "Superman".
const userSchema = new Schema(
{
name: String,
age: Number
}
)
const User = mongoose.model('User', userSchema);
User.findOne({xname: "Superman"}, ...
But when using xname in the filter document which does not exist in my schema neither in the collection as field the query returns the first document in my test collection (its not Superman).
Also look here similar issue: Model.find Mongoose 6.012 always return all documents even though having filter
Issue reported: https://github.com/Automattic/mongoose/issues/10763
Migration Guide to Mongoose 6:
https://mongoosejs.com/docs/migrating_to_6.html#strictquery-is-removed-and-replaced-by-strict

Static method for filtering all documents based on property of enum type

I have in my userSchema for role a user and admin but I'm not sure how I can create a static method to return all documents based on if the user is an admin or user.
I did try the following...
userSchema.statics.findByRole = function (role) {
return this.find({ role: role });
};
but that didn't work.
const mongoose = require('mongoose');
const userSchema = new mongoose.Schema({
firstName: {
type: String,
},
lastName: {
type: String,
},
email: {
type: String,
required: [true, 'Please provide a valid email address!'],
unique: true,
lowercase: true,
},
password: {
type: String,
required: [true, 'Password is required!'],
},
role: {
type: String,
enum: ['user', 'admin'],
default: 'user',
},
createdAt: {
type: Date,
default: Date.now,
},
});
userSchema.virtual('fullName').get(function () {
return this.firstName + ' ' + this.lastName;
});
userSchema.statics.findByRole = function (role) {
return this.find({ role: role });
};
const User = mongoose.model('User', userSchema);
module.exports = User;
for find, findOneAndUpdate, etc you should use async/await; Because getting connected to the database can be time consuming.
userSchema.statics.findByRole = async function (role) {
await this.find({ role: role })
}

Having an issue getting user id

So I am working on building a web app and I have a lot of it working. However I am trying to link my user._id to my items database. But when I try to post an item using postman the app crashes saying it cant read property '_id' of null. I know I am missing something but I honestly can't figure out what other code I need to implement. Any help would be great. Thanks
Here is the code for the UserSchema:
const mongoose = require('mongoose');
const passportLocalMongoose = require("passport-local-mongoose");
const UserSchema = new mongoose.Schema({
username: {
type: String,
trim: true,
unique: true,
required: true,
minlength: 3,
maxlength: 15
},
firstName: {
type: String,
required: true,
minlength: 3,
maxlength: 15
},
lastName: {
type: String,
required: true,
minlength: 3,
maxlength: 15
},
email: {
type: String,
unique: true,
required: true
},
resetPasswordToken: String,
resetPasswordExpires: Date,
isAdmin: {
type: Boolean,
default: false
}
});
UserSchema.plugin(passportLocalMongoose);
module.exports = mongoose.model("user", UserSchema);
Here is the code for the ItemSchema:
const mongoose = require('mongoose');
const User = require('./user');
const ItemSchema = new mongoose.Schema({
name: {
type: String,
required: true,
minlength: 3,
maxlength: 20
},
description: {
type: String,
required: true
},
price: {
type: Number,
required: true
},
createdBy: {
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
},
username: String
},
createdAt: {
type: Date,
default: Date.now
}
});
module.exports = mongoose.model("items", ItemSchema);
And here is the code for the route thats throwing the error:
const express = require("express");
const router = express.Router();
const User = require("../models/user");
router.route("/item/add")
.post(function(req, res) {
User.findById(req.user._id, function(user, err) {
if (err) {
console.log(err);
}
var item = new Item();
item.name = req.body.name;
item.description = req.body.description;
item.price = req.body.price;
item.createdBy = { id: req.user._id, username: req.user.username };
item.save(function(err) {
if (err) {
res.send(err);
}
res.json({ message: "Item was successfully saved" });
console.log(item);
});
});
});
You need to send your data in json format in postman for example:
{'id':1, 'name':'jhon doe', 'email':'jhondoe#example.com'}
in your backend file you need to call
req.body.id not req.user._id

Referencing Object Id not working in Mongoose 4.11.6

I have this problem. Basically, I have 2 schemas - a User schema and a Document schema. The Document schema has an owner which references the _id field of documents in the User collection.
The problem is that I am still able to save documents in the Document collection with owner ids that do not exist in the User collection which should not be so.
Here is my User schema and Document schema respectively
const UserSchema = new Schema({
firstName: {
type: String,
required: true,
},
lastName: {
type: String,
required: true,
},
email: {
type: String,
validate: [{ validator: value => isEmail(value), msg: 'Invalid email.'
}],
unique: true,
required: true,
},
password: {
type: String,
required: true,
},
isAdmin: {
type: Boolean,
default: false,
},
}, {
timestamps: true,
});
const User = mongoose.model('User', UserSchema);
And the Document Schema
const DocumentSchema = new Schema({
title: {
type: String,
required: true,
},
text: {
type: String,
},
access: {
type: String,
enum: ['public', 'private'],
default: 'public',
},
owner: {
type: Schema.Types.ObjectId,
ref: 'User',
required: true,
},
}, {
timestamps: true,
});
const Document = mongoose.model('Document', DocumentSchema);
Any help will be appreciated thanks.
For that situation you can add pre save function in your Document schema that will call before save your Document.
const DocumentSchema = new Schema({
// ...
}, {
timestamps: true,
});
DocumentSchema .pre("save",function(next) {
var self = this;
if (self.owner) {
mongoose.models['User'].findOne({_id : self.owner }, function(err, existUser){
if(err){
return next(false, err);
}
if(!existUser)
return next(false, "Invalid user reference");
else
return next(true);
});
} else {
next(false, "Owner is required");
}
});
const Document = mongoose.model('Document', DocumentSchema);

Questions about Mongoose Schema design

I'm completely new to the NoSQL world and it's been difficult to wrap my mind around it. This week I was learning MongoDB (Mongoose) with Node.js and here is my current schema:
var eventDataSchema = new Schema({
_id : Number,
notes : {type: String, required: true},
start_date : {type: Date, required: true},
end_date : {type: Date, required: true},
}, {
id : false,
collection : 'event-data'
});
eventDataSchema.plugin(AutoIncrement);
var EventData = mongoose.model('EventData', eventDataSchema);
Now that this is working, I would like to add a user and password and have access to have personal access to EventData.
Also... later if I want to send a JSON of only the eventData, but not the user to my javascript, how would I do that?
The way I am currently sending my eventData to my js in this format:
router.get('/data', function(req, res){
EventData.find({}, function(err, data){
if (err) {
console.error('Error occured');
}
res.send(data);
});
});
Thanks again
As i can understand you want to add events key in your schema. Then your schema will be like that:
var userSchema = new Schema({
user: { type: String, required: true, trim: true },
password: { type: String, required: true, trim: true },
events: [{
notes: { type: String,required: true, trim: true },
start_date: { type: Date,required: true },
end_date: { type: Date,required: true }
}]
}
userSchema.plugin(AutoIncrement);
var userSchema = mongoose.model('userSchema', userSchema);
});
If the above code is not working then you can create two schema,one for user and other for eventData, and can populate your eventData in userSchema.
so your code will be like that:
userSchema.js:
var userSchema = new Schema({
user: { type: String, required: true, trim: true },
password: { type: String, required: true, trim: true },
events: {type: mongoose.Schema.Types.ObjectId, ref: 'EventData' }
userSchema.plugin(AutoIncrement);
module.exports = mongoose.model('userSchema', userSchema);
});
And your eventDataSchema will be:
eventSchema.js:
var eventDataSchema = new Schema({
notes: { type: 'string',required: true, trim: true },
start_date: { type: Date,required: true },
end_date: { type: Date,required: true }
}
eventDataSchema.plugin(AutoIncrement);
module.exports = mongoose.model('EventData', eventDataSchema);
});
and then you can get the result like that:
index.js:
var eventSchema = require('./eventSchema');
var userSchema = require('./userSchema');
var populate = [{
path: 'events',
model: 'EventData',
select: '_id notes start_dat end_date'
}];
var find = function (query) {
return userSchema.find(query).populate(populate).exec();
}
console.log(find());
Result:
{
_id:cfgvhbjnkmkdcfxghgjklxnmbxhdhjxjhjhgx,
user: John Doe,
password: 123,
events: [ { _id: 1gfye56785g3ycgevhxeftx568765egcd,
notes: Event A,
start_date: 1/1/01,
end_date: 1/1/01
} ]
}

Resources