How does module.exports work - node.js

I'm using Express and Mongodb to write my first web app and this is probably a noob question, but let's say I were to define a user model in a file called users.js and then called const User = module.exports = mongoose.model('User', UserSchema) somewhere in the file.
Upon importing users.js (via const User = require([path to users.js]) to some other file in the app, why can I then call new User and have access to the model instead of having to call new users.User?

The standard way to define the model and using the schema in the controller
User.js
//User Model
var mongoose = require('mongoose');
var userSchema = new mongoose.Schema({
id: String,
name: {
type: String,
index: true
},
email: {
type: String,
trim: true,
index: true,
lowercase: true,
unique: true
},
mobile: {
type: String,
trim: true,
index: true,
unique: true,
required: true
},
profilePic: String,
password: { type: String },
locations: [{}],
location: {
type: { type: String, default: 'Point', enum: ['Point'] },
coordinates: { type: [], default: [0, 0] },
name: String,
shortAddress: String
},
address: String,
gender: String,
dob: Date,
signupType: { type: String, enum: ['facebook', 'google'] },
deviceType: String,
createdTime: Date,
updatedTime: Date,
googleToken: String,
facebookToken: String,
fcmToken: String,
facebookLink: String,
facebookId: String,
memberType: String,
deviceId: String,
preferences: [{}],
loginData: [{}],
token:String,
isVerified: Boolean,
isMobileVerified: Boolean,
isEmailVerified: Boolean,
lastSeen: Date
});
// 2D sphere index for user location
userSchema.index({ location: '2dsphere' });
mongoose.model('User', userSchema);
module.exports = mongoose.model('User');
UserController.js
//User Controller
var User = require('./User');
// RETURNS ALL THE USERS IN THE DATABASE
router.get('/', function (req, res) {
User.find({}, function (err, users) {
if (err) return res.status(500).send({ errors: "There was a problem finding the users." });
res.status(200).send(users);
});
});

Related

populate() doesn't fetch referenced profile instead it fetches the first profile in my collection

I have three collections which are User, Profile and Userpost and all referenced accordingly. The challenge I am facing is that when I use the .populate(), instead of fetching the Profile information of the logged in user, it fetches the data of the first profile on the profile collections and it does so for any user that is logged in. Kindly help me resolve. Thanks
How I populate
router.get('/getpost/:id', (req, res) => {
const id = req.params.id;
Userpost.find({User:id}).populate('Profile').populate('User', {password: 0}).exec((err,docs) => {
if(err) throw(err);
res.json(docs);
})
});
UserpostSchema
const UserpostSchema = new Schema({
post: {
type: String,
required: true
},
User: {
type: mongoose.Schema.Types.ObjectId,
ref: 'user',
required: true,
},
Profile: {
type: mongoose.Schema.Types.ObjectId,
ref: 'profile',
required: true,
}
});
const Userpost = mongoose.model('userpost', UserpostSchema);
module.exports = Userpost;
Profile
const ProfileSchema = new Schema({
lastname: {
type: String,
required: true,
},
firstname: {
type: String,
required: true,
},
othernames: {
type: String,
required: true,
},
countries: {
type: String,
required: true,
},
phones: {
type: String,
required: true,
},
User: {
type: mongoose.Schema.Types.ObjectId,
ref: 'user',
required: true,
},
});
const Profile = mongoose.model('profile', ProfileSchema);
module.exports = Profile;
and User
const userSchema = new Schema({
username: {
type: String,
required: true
},
roles: {
User: {
type: Number,
default: 2001
},
Mentor: Number,
Admin: Number
},
password: {
type: String,
required: true
},
userID: {
type: String,
required: true
},
refreshToken: String
});
const User = mongoose.model('user', userSchema);
module.exports = User;

mongoose schema reference returning undefined

I'm just trying to reference accountSchema from productSchema and I'm receiving an undefined result. I think I've referenced 'Account' correctly and that I'm properly creating and saving products. My understanding is that the account field should populate itself with the appropriate account objectId just from being referenced in the model.
//acount.model.js
const accountSchema = new Schema({
email: { type: String, unique: true, required: true },
passwordHash: { type: String, required: true },
title: { type: String, required: true },
firstName: { type: String, required: true },
lastName: { type: String, required: true },
acceptTerms: Boolean,
role: { type: String, required: true },
verificationToken: String,
verified: Date,
resetToken: {
token: String,
expires: Date
},
passwordReset: Date,
created: { type: Date, default: Date.now },
updated: Date
});
module.exports = mongoose.model('Account', accountSchema);
//product.model.js
const productSchema = new Schema({
title: { type: String, trim: true, required: true },
description: { type: String, trim: true, required: true },
category: { type: String, trim: true},
subCategory: { type: String, trim: true },
quantity: { type: Number, trim: true },
price: { type: Number, trim: true },
created: { type: Date, default: Date.now },
updated: Date,
account: { type: Schema.Types.ObjectId, ref: 'Account' }
});
module.exports = mongoose.model('Product', productSchema);
//products.controller.js
//routes
router.post('/', authorize([Role.Admin, Role.Vendor]), createSchema, create);
function createSchema(req, res, next) {
const schema = Joi.object({
title: Joi.string().required(),
description: Joi.string().required(),
category: Joi.string(),
subCategory: Joi.string(),
quantity: Joi.number(),
price: Joi.number()
});
validateRequest(req, next, schema);
}
function create(req, res, next) {
productService.create(req.body)
.then(product => res.json(product))
.catch(next);
}
//product.service.js
async function create(params) {
const product = new db.Product(params);
// save account
await product.save();
// res.json(product);
console.log(basicDetails(product));
return basicDetails(product);
}
function basicDetails(product) {
const { id, title, description, category, subCategory, quantity, price, account } = product;
return { id, title, description, category, subCategory, quantity, price, account };
}
//console.log output of basicDetails(product)
{
id: '600614014d927f29a0fbb162',
title: 'delete from mcjaggerrrrrrrr6789',
description: 'testing delete from role.vendor mcjaggerrrrrrrrrr',
category: undefined,
subCategory: undefined,
quantity: undefined,
price: undefined,
account: undefined
}
category, subCat, quant, and price are all undefined because I didn't input values for them but account is undefined and I'm not sure why. Any help is appreciated!

Mongoose(mongoDB) Linking multiple schema's

Im relatively new to MongoDB and Mongoose. Im much used to MySQL so in used to inner joining tables on calls. Ive read a lot that you can link two Mongoose Schemas to achieve the same outcome. How would like like the two schemas together to when I make a call to get a chore by id it'll return the chore and then for the assignedTo & createdBy have the user scheme data for the said userId?
Chore Schema
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var ChoreSchema = new Schema({
title: {
type: String,
required: true
},
desc: {
type: String,
required: true
},
time: {
type: Number,
required: true
},
reaccurance: {
type: [{
type: String,
enum: ['Daily', 'Weekly', 'Bi-Weekly', 'Monthly']
}]
},
reward: {
type: Number,
required: true
},
retryDeduction: {
type: Number,
required: false
},
createdDate: {
type: Date,
default: Date.now
},
createdBy: {
type: String,
required: true
},
dueDate: {
type: Date,
required: true
},
status: {
type: [{
type: String,
enum: ['new', 'pending', 'rejected', 'completed', 'pastDue']
}],
default: ['new']
},
retryCount: {
type: Number,
default: 0,
required: false
},
rejectedReason: {
type: String,
required: false
},
familyId: {
type: String,
required: true
},
assignedTo: {
type: String,
required: false,
default: ""
}
});
let Chores = module.exports = mongoose.model('Chores', ChoreSchema);
module.exports.get = function (callback, limit) {
Chores.find(callback).limit(limit);
};
User Schema
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var UserSchema = new Schema({
firstName: {
type: String,
required: true
},
lastName: {
type: String,
required: true
},
role: {
type: [{
type: String,
enum: ['Adult', 'Child']
}]
},
birthday: {
type: String,
required: false
},
familyId: {
type: String,
required: true
},
balance: {
type: Number,
required: true,
default: 0.00
}
});
let Users = module.exports = mongoose.model('Users', UserSchema);
module.exports.get = function (callback, limit) {
Users.find(callback).limit(limit);
};
Im trying to link ChoreSchema.createdBy & ChoreScheme.assignedTo by UserSchema._id
How I make the call in Node.js:
exports.index = function(req, res) {
Chore.get(function(err, chore) {
if (err)
res.send(err);
res.json({
message: 'Chore List',
data: chore
});
});
};
Mongoose has a more powerful alternative called populate(),
which lets you reference documents in other collections.
https://mongoosejs.com/docs/populate.html
Here is how you can link ChoreSchema.createdBy and ChoreScheme.assignedTo by UserSchema._id
var mongoose = require('mongoose');
const { Schema, Types } = mongoose;
var UserSchema = new Schema({
firstName: { type: String, required: true },
...
})
var ChoreSchema = new Schema({
title: { type: String, required: true },
...
//The ref option is what tells Mongoose which model to use during population
assignedTo: { type: Types.ObjectId, ref: 'Users' },
createdBy: { type: Types.ObjectId, ref: 'Users' },
})
let Chores = mongoose.model('Chores', ChoreSchema);
let Users = mongoose.model('Users', UserSchema);
Then in your express route handler you can populate assignedTo & createdBy like this
router.get('/chores/:id', function (req, res) {
const choreId = req.params.id;
Chores.find({ _id: choreId })
.populate('createdBy') // populate createdBy
.populate('assignedTo') // populate assignedTo
.exec(function (err, chore) {
if(err) {
return res.send(err)
}
res.json({ message: 'Chore List', data: chore });
});
})

Populate() specify multiple model

I'm looking to make a populate() on a find() request but specifying multiple models, so if no occurrence is found on the first specified model the population will make on the second.
Here is an example of what I would like to do :
const userSch = new Schema({
username: {
type: String,
required: true,
},
password: {
type: String,
required: true,
},
mail: {
type: String,
required: true,
unique: true,
}
});
const botSch = new Schema({
username: {
type: String,
required: true,
},
token: {
type: String,
required: true,
unique: true,
},
owner: mongoose.Schema.Types.ObjectId
});
const nspSch = new Schema({
name: String,
channels: [channels],
users: [{
id: {
type: mongoose.Schema.Types.ObjectId,
},
bot: {
type: Boolean,
}
}],
});
const channels = new Schema({
name: String,
message: [{
user: {
type: Schema.Types.ObjectId,
},
content: String,
}],
});
const nsp = mongoose.model('namespace', nspSch);
const Auth = mongoose.model('users', userSch);
const BotAuth = mongoose.model('bots', botSch);
function returnMs(nspID, channelID, callback) {
nsp.findOne({'_id': nspID}).populate({
path: 'channels.message.user',
model: 'users' || 'bots',
select: '_id username',
})
.exec(function(err, r) {
...
})
}
If there is ever an npm package, or a solution or even a track to code it, please share it.
Thank you

Mongoose Discriminators unable to add dicriminator details

Im currently working on adding discriminators to my express rest api. I have added different types of users to the user schema using the discriminators as different user require additional information. The problem I am facing is that when I post to the api get no errors when adding the information and only the general information is added to the schema, the details within the discriminators are ignored.
The schema is as follows:
var options = { discriminatorKey: 'type' };
var UserSchema = new Schema({
local: {
email: {
type: String,
sparse: true,
lowercase: true,
},
password: { type: String },
},
facebook: {
id: String,
token: String,
email: String,
name: String,
profileIMG: String,
},
twitter: {
id: String,
token: String,
displayName: String,
username: String
},
google: {
id: String,
token: String,
email: String,
name: String,
profileIMG: String,
}
}, options);
var addressSubschema = {
street: {
type: String,
required: true
},
number: {
type: String,
required: true
},
city: {
type: String,
required: true
},
};
var workingHoursSchema = {
start: {
type: String,
required: true
},
finish: {
type: String,
required: true
}
};
var adminSchema = new Schema({
description: {
type: String,
required: true
},
category: {
type: String,
required: true
},
workingHours: workingHoursSchema,
address: addressSubschema,
workingRadius: {
type: Number,
required: true
},
}, options);
var User = mongoose.model('User', UserSchema);
var Admin = User.discriminator('AdminUser', adminSchema);
module.exports = User;
I then export the model and when saving a new user I get a success however the admin details are not saved.
User.findOne({'local.email': email}, function(err, existingUser) {
if (err) { return next(err) }
if (existingUser) {return res.status(422).json({error: "Email already exists"})}
var user = new User({
"local.email": req.body.email,
"local.password": req.body.password,
"description": req.body.description,
"category": req.body.category,
"workingRadius": req.body.workingRadius,
"street": req.body.street,
"number": req.body.number,
"city": req.body.city,
"start": req.body.start,
"finish": req.body.finish
});
user.save(function(err) {
if (err) { return next(err) }
res.json({success: true});
});
});
Im new to using the discriminator so any help is greatly appreciated.

Resources