Manually populating Mongodb field on document creation with Mongoose - node.js

Following the Mongoose documentation, I was able to create two docs, but am unable to populate one with the other.
Despite manually setting the 'account' value to reference the other document, my database doesn't seem to create the relation.
Below is the code I've used:
UserAuth.findOne({ email }, (err, user) => {
if (err) return done(err);
if (user) {
return done(null, false,
{ message: 'It appears that email address has already been used to sign up!' });
}
// Create the user account
const newAccount = new UserAccount({
name: {
first: req.body.firstName,
last: req.body.lastName,
},
});
newAccount.save((err) => {
if (err) throw err;
// Create the user authentication
const newAuth = new UserAuth({
email,
account: newAccount,
});
newAuth.password = newAuth.generateHash(password);
newAuth.save((err) => {
if (err) throw err;
return done(null, newAuth);
});
return done(null, newAccount);
});
});
Collections:
User Auth
const UserAuthSchema = new Schema({
email: {
type: String,
required: true,
},
password: {
type: String,
required: true,
},
account: {
type: Schema.Types.ObjectId,
ref: 'User',
},
});
module.exports = mongoose.model('UserAuth', UserAuthSchema);
User Account
const UserSchema = new Schema({
name: {
first: {
type: String,
required: true,
},
last: {
type: String,
required: true,
},
},
team: {
type: Schema.Types.ObjectId,
ref: 'Team',
},
image: {
type: String,
default: 'assets/default_user.png',
},
});
module.exports = mongoose.model('User', UserSchema);

It looks like the part:
// Create the user authentication
const newAuth = new UserAuth({
email,
account: newAccount,
});
should be:
// Create the user authentication
const newAuth = new UserAuth({
email,
account: newAccount._id,
});
And then, when you query the collection, you have to say which field should be populate, as shown in (Mongoose documentation)[http://mongoosejs.com/docs/populate.html]
Ad please check that the types of the 2 linked fields are the same as mentioned in the documentation.

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

How to save data in mongodb express by referring another document

I have created the following User schema
const UserSchema = mongoose.Schema(
{
fullName: {
type: String,
required: true,
index: true,
},
department: {
type: mongoose.Schema.Types.ObjectId,
ref: "Department",
required: true,
},
},
});
and my Department schema looks like the following
const DepartmentSchema = mongoose.Schema(
{
name: {
type: String,
unique: true,
required: true,
},
description: {
desc: "Description.",
type: String,
},
},
{
strict: true,
versionKey: false,
timestamps: { createdAt: "createdAt", updatedAt: "updatedAt" },
});
Now I'm able to create departments and all is working well in Postman and also my Angular application. But while creating User it only saves the department ID that I've selected or added in my form. This is the data that I've tried to create
user = {
department: "5f806be7c9a3c02c7c61f9f1"
fullName: "John Joshua"
}
and my create route controller looks like this
exports.create = async (req, res) => {
const newUser = new User({
fullName: req.body.fullName,
department: req.body.department,
});
try {
const errors = validationResult(req); // I've used express validator here
if (!errors.isEmpty()) {
res.status(400).json({ errors: errors.array() });
return;
}
const savedUser = await newUser.save();
res.json(savedUser); // Here I was expecting to return the new saved user with Department details
} catch (err) {
res.status(500).json({ message: err });
}
};
So my response is all good and working well but department only contains the ID with nothing more.
How can I send a response data with Department details? Any help is appreciated.

How to add user's name as well as Id to users field in a chat?

I'm creating a web application that has chats, and users can join the chat. Once the user joins the chat, I want to add the user's ID as well as their name to the users field in the Chat schema. So far, I'm able to add their ID, but I am finding it difficult to add their name. Below, I have attached my Chat mongoose model, as well as my route to add a user to a chat. Also, I have attached my User mongoose model. Any help is greatly appreciated. Thank you!
Chat model:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const ChatSchema = new Schema({
title: {
type: String,
required: true
},
password: {
type: String,
required: true
},
creator: {
type: Schema.Types.ObjectId,
ref: 'user'
},
users: [
{
user: {
type: Schema.Types.ObjectId,
ref: 'user'
},
name: {
type: String,
required: true
}
}
],
code: {
type: String,
required: true
},
posts: [
{
text: {
type: String,
required: true
},
title: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now
}
}
],
date: {
type: Date,
default: Date.now
}
});
module.exports = Chat = mongoose.model('chat', ChatSchema);
route to add user to chat:
// #route Put api/chats
// #desc Add a user to a chat
// #access Private
router.put('/', [auth,
[
check(
'code',
'Please include the code for the chat')
.not()
.isEmpty(),
check(
'password',
'Please include the password for the chat'
).not()
.isEmpty()
]
],
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
try {
const chat = await Chat.findOne({ code: req.body.code });
//const user = await User.findOne({ user: req.user.id });
if (!chat) {
return res.status(400).json({ msg: 'Invalid Credentials' });
}
// Check if the chat has already been joined by the user
if (chat.users.filter(member => member.user.toString() === req.user.id).length > 0) {
return res.status(400).json({ msg: 'Chat already joined' });
}
//console.log(chat.password);
const isMatch = await bcrypt.compare(req.body.password, chat.password);
if (!isMatch) {
return res.status(400).json({ errors: [{ msg: 'Invalid Credentials' }] });
}
const newUser = {
user: req.user.id,
text: req.user.name
}
chat.users.unshift(newUser);
await chat.save();
res.json(chat.users);
} catch (err) {
console.error(err.message);
res.status(500).send('Server Error');
}
});
User model:
const mongoose = require('mongoose');
const UserSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now
}
});
module.exports = User = mongoose.model('user', UserSchema);
in this part, it seems you are assigning the user's name to a text property, which I think it should be name not text.
const newUser = {
user: req.user.id,
text: req.user.name
}
The code should be:
const newUser = {
user: req.user.id,
name: req.user.name //Property should be name
}
I hope this works!

User with favourites and likes system in Mongoose schemas

I want to create a DB with Users which also have a reference to another DB called "Library" which has "favourites" and "likes". I will show the idea here:
User Model
const userSchema = Schema({
username: {type: String, minlength: 4, maxlength: 10, required: true, unique: true},
email: {type: String, required: true, unique: true},
password: {type: String, required: true},
isVerified: { type: Boolean, default: false },
library: {type: Schema.Types.ObjectId, ref: 'Library'}
}, { timestamps: true});
Library Model
const librarySchema = new Schema({
likes: [{
likeId: {type: String},
mediaType: {type: String}
}],
favourites: [{
favId: {type: String},
mediaType: {type: String}
}],
user: {type: Schema.Types.ObjectId, ref: 'User'}
});
Can you please tell me if this is the right way to implement these models or if there is a better way?
At the moment if I try to call
User.findOne({email: 'xxx#xxx.com'}).populate('library').exec(function (err, library)
it doesn't find anything...
Library POST request
router.post('/favourites', passport.authenticate('jwt', {session: false}), function (req, res) {
const favouritesFields = {};
if (req.body.favId) favouritesFields.favId = req.body.favId;
if (req.body.mediaType) favouritesFields.mediaType = req.body.mediaType;
Library.findOne({user: req.user._id}).then(library => {
if (library) {
Library.update({user: req.user._id}, {$push: {favourites: favouritesFields}})
.then(library => res.json(library));
} else {
new Library({user: req.user._id, favourites: favouritesFields}).save().then(library => res.json(library));
}
});
});
User POST request
router.post('/signup', function (req, res) {
const {errors, isValid} = validateSignupInput(req.body);
if (!isValid) {
return res.status(400).json(errors);
}
// Check if email already exists
User.findOne({email: req.body.email}, function (user) {
if (user) {
return res.status(400).json({
title: 'Email already exists'
});
}
});
// Create and save the new user
let user = new User({
username: req.body.username.toLowerCase(),
email: req.body.email.toLowerCase(),
password: bcrypt.hashSync(req.body.password, 10)
});
user.save(function (err, result) {
if (err) {
return res.status(500).json({
title: 'An error occurred during the signup',
error: err
});
}
res.status(201).json({
title: 'User created',
obj: result
});
Your problem is not with the query you're making. there is no foundUser.library because one was never added.
You're adding users to libraries, but you're not adding libraries to your users. if you run the following code in your app:
Library.find({}).populate("user").exec(function(err, foundLibraries){
if (err){
console.log(err);
} else {
console.log(foundLibraries);
}
});
You would see that the libraries have their "user" properties, that when populated contain the entire user document as an object. But, the reason that isn't working for foundUser.library when you query for users is that foundUser.library was never assigned. you know how you're assigning the email, username and password when creating users, you have to do the same for the library property. Or, in your case, since a library is only created after the user, you can just set the value of user.library in the callback of creating/saving the library.

How to save a refrence between 2 collections in MongoDB?

I'm developing an app using Node.js, Mongoose, MongoDb, express.
I have 2 schemas one for student and one for snippets. I'm using the population model population model. I can create a user, and create a snippet and link it to the user. But I can't link and save the snippets in the user collection.
How to link and save the user so that it can have a reference to his snippets?
user and snippet schema
var userSchema = Schema({
name: { type: String, required: true, unique: true },
password: { type: String, required: true },
snippet: [{ type: Schema.Types.ObjectId, ref: 'Snippet' }]
})
var snippetSchema = Schema({
user: {type: Schema.Types.ObjectId, ref: 'User'},
title: String,
body: String,
createdAt: {
type: Date,
require: true,
default: Date.now
}
})
This is how I save the user which usually when the user register.
var username = request.body.name
var password = request.body.password
var newUser = new User({
name: username,
password: password
})
newUser.save().then(function () {
console.log('success')
response.redirect('/')
})
This is how I save the snippets I add it inside a user .save() function so that it saves the snippet ref but it gives me name.save() is not a function error.
var name = request.session.name.name
name.save().then(function () {
// Using a promise in this case
var newSnippet = new Snippet({
user: name._id,
title: title,
body: snippet
})
newSnippet.save().then(function () {
// Successful
console.log('success')
response.redirect('/')
})
}).catch(function (error) {
console.log(error.message)
response.redirect('/')
})
name.save() is not a function because name is just a string in this case, not a Mongoose object.
One way to do it is to create your user, then retrieve it and add a snippet:
var name = request.session.name.name
User.find({ name: name }).then(function (user) {
// Here you get your user by name
var newSnippet = new Snippet({
user: user._id, // And here you can pass user _id
title: 'your title',
body: 'your snippet'
})
newSnippet.save()
})

Resources