Trouble with object selection in Mongoose schema - node.js

Currently building an app whereby the user can log in either locally or via google (will be expanded to other social sites). I'm using an enum array to hold the possible login methods. My user schema looks as follows:
const userSchema = new Schema({
method :{
type: String,
enum: ['local', 'google', 'twitter'],
required: true
},
local: {
email: String,
username: String,
password: String,
projects: [
{
projectName: String,
sessionLength: Number,
timestamp: Date
}
]
},
google: {
googleId: String,
email: String,
username: String,
projects: [
{
projectName: String,
sessionLength: Number,
timestamp: Date
}
]
}
});
const User = mongoose.model('user', userSchema);
module.exports = User;
The following code is used to create the new user and save to the database after authentication by Google.
let newUser = new User({
method: 'google',
google: {
googleId: profile.id,
email: profile.emails[0].value,
username: profile.name.givenName
}
});
My problem is that newUser contains, as well as the correct google object, a 'local' object with a projects array. Below is how the newUser looks. I dont want it to include the local object. It also only seems to only appear if the projects property is an array, works as expected when projects is set to be a string. What am I doing wrong?
{ method: 'google',
_id: 5a32aaa4c665b062df9c0d00,
google:
{ googleId: 'randomgoogleId',
email: 'name#gmail.com',
username: 'name',
projects: [] },
local: { projects: [] } }

Related

Mongoose pre hook findOneAndUpdate to modify document before saving

I am using the mongoose pre hook for findOneAndUpdate. I went through the documentation to understand better it's usage. I would like to update the password field before it saves to DB. However, I am not getting the disired result - nothing gets changed. What would be the right approach for using the findOneAndUpdate pre hook to modify a certain field in the doc?
Actual Document
{
_id: new ObjectId("622457f5555562da89b7a1dd"),
id: '5982ca552aeb2b12344eb6cd',
name: 'Test User',
configuration: [
{
email: 'test2#gmail.com',
password: 'p#ssw0rd',
_id: new ObjectId("9473l58f2ad34efb816963dd"),
},
{
email: 'test3#gmail.com',
password: 'trUstN0oNe',
_id: new ObjectId("8674884cec1877c59c8838e0")
}
],
__v: 0
}
Desired Document
{
_id: new ObjectId("622457f5555562da89b7a1dd"),
id: '5982ca552aeb2b12344eb6cd',
name: 'Test User',
configuration: [
{
email: 'test2#gmail.com',
password: '0f359740bd1cda994f8b55330c86d845',
_id: new ObjectId("9473l58f2ad34efb816963dd"),
},
{
email: 'test3#gmail.com',
password: '3dba7872281dfe3900672545356943ce',
_id: new ObjectId("8674884cec1877c59c8838e0")
}
],
__v: 0
}
Code:
const UserSchema = new Schema({
id: {
type: String,
required: [true, "'id' value is required"]
},
name: {
type: String,
required: [true, "'name' value is required"]
},
configuration: [ConfigModel.schema]
});
const ConfigSchema = new Schema({
email: {
type: String,
required: [true, "Email is required"]
},
password: {
type: String,
required: [true, "Password is required"]
}
});
UserSchema.pre('findOneAndUpdate', async function(next) {
const docToUpdate = await this.model.findOne(this.getQuery());
docToUpdate.configuration.forEach((item,i) => {
docToUpdate.configuration[i].password = md5(item.password);
});
return next();
});
You are missing the .save() document command after changing the information inside the document, because you are only using findOne
const docToUpdate = await this.model.findOne(this.getQuery());
docToUpdate.botconfiguration.forEach((item,i) => {
docToUpdate.configuration[i].password = md5(item.password);
});
await docToUpdate.save() // <---- this line
You dont need the updateMany() here because the ConfigSchema is nested inside the user collection
in userModel you read configuration from ConfigModel so you have to modify the config model not user model it just read and populate the data from config model.

How to search in multiple collections in MongoDB using Mongoose

I have two collections User and UserType :-
var User = new mongoose.Schema({
username: {
type: String,
required: true,
},
userType: {
type: ObjectId,
ref: "UserType",
required: true,
},
});
var UserTypeSchema = new mongoose.Schema(
{
type: String,
type_code: Number,
type_description: String,
},
{ timestamps: true }
);
I want to search user based on username and typecode which is in UserType Collection.
I tried this code: -
User.findOne({
username: mobileNumber,
userType: { type_code: userTypeCode },
})
.populate("userType");
please correct this query.
you must to filter out populate results, with match option
In your case answer would be::
User.findOne({
username: mobileNumber,
}).populate({
path: "userType",
match: { type_code: userTypeCode },
});
you can check the documentation

mongoose schema contains entire DB object instead of defined schema object

I'm using two schemas for users. One that contains the password/salt, one that doesn't for returning to the front end. When I use the model that uses the schema WITHOUT the password, it still returns the password :/
Generic User (For sending to the client)
module.exports = {
username: String,
email: String,
firstName: String,
lastName: String,
createdOn: Date,
updatedOn: Date,
scopes: [String]
}
Auth User (for creating/updating/authenticating users)
module.exports = {
username: String,
email: String,
password: String,
salt: String,
firstName: String,
lastName: String,
createdOn: Date,
updatedOn: Date,
scopes: [String]
}
Creating the models with
var modelInstance = mongoose.model("authUser", authUserSchema, 'users')
(in a different file)
var modelInstance = mongoose.model("user", userSchema, 'users')
modelInstance is exported with
module.exports = modelInstance;
Update This question answers mine.
How to protect the password field in Mongoose/MongoDB so it won't return in a query when I populate collections?
You don't have a clear question, but I guess you are asking if you can restrict it. The answer is 'no' by default.
There is a plugin for this: https://www.npmjs.com/package/mongoose-strictmodel
But it's really out of date.
It's easy enough though to create a wrapper function:
function safeUser(userModel) {
return {
username: userModel.username,
email: userModel.email,
firstName: userModel.firstName,
lastName: userModel.lastName,
createdOn: userModel.createdOn,
updatedOn: userModel.updatedOn,
scopes: userModel.scopes
}
}

Node POST req returns same values regardless of input

I'm making a POST request like this:
router.post('/register2', (req, res) => {
const newUser = new User({
name: req.body.name,
email: req.body.email,
password: req.body.password
});
newUser.save()
.then(res.json(newUser));
})
But, in Postman, no matter what I enter for my name/email/password values, it just returns the first set of values I ever tried. The id and date update each time, but name, email, and password ignore the input and give me the same things every time.
Here's the "User" that newUser refers to:
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
}
});
module.exports = User = mongoose.model('users', UserSchema);

Correct structure for grouping data in Mongoose Schema

I've created a Mongoose Schema that looks like this:
var UserSchema = new Schema({
user: [{
name: {
type: String,
required: true
},
email: {
type: String,
required: true,
index: { unique: true }
},
password: {
type: String,
required: true
},
mobile: Number
}],
account: [{
locked: {
type: Boolean,
default: false
},
accountType: {
type: String,
default: "guest"
},
failedLogins: {
type: Number,
default: 0
}
}],
reset: [{
resetToken: String,
resetExpirey: Date
}],
details: [{
accountCreated: Date,
lastLogin: Date
}]
});
As you can see I've tried to group certain fields. Is this the correct way to do it? I'm now having trouble referencing the fields. I get an error when I try this:
User.create({
user.name : req.body.name,
user.email : req.body.email,
user.password: req.body.password
}, function(err) {
if (err) res.send(err);
});
Error is unexpected token '.' in user.name
Your create statement still needs to be proper javascript, so your left-side object literals will need to be strings.
User.create({
'user.name' : req.body.name,
'user.email' : req.body.email,
'user.password': req.body.password
}, function(err) {
if (err) res.send(err);
});
Furthermore, because Mongoose requires you to have an array instead of allowing proper sub-objects, you'll need to actually insert these as an array.
var user = {user: [{name:req.body.name, email: req.body.email, password: req.body.password}]};
User.create(user) ...
As to the "is it worth it" to do it like this, my opinion is: no. Just put all these things in the root object unless you plan on having more than one user or more than one account in this one document.

Resources