mongoose best practices for objects - node.js

I have a user.js file that contains all my mongoose schema and methods, roughly like this:
var userSchema = mongoose.Schema({
name: { type: String, required: true, trim: true },
surname: { type: String, required: true },
});
var User = mongoose.model('User', userSchema);
Now, Is it best to expose functions that model the data? like this:
exports.addUser = function(data, next){
user = new User(data);
user.save(next);
};
exports.getAll = function(next){
var query = User.find();
query.exec(next);
};
or to expose just the User object to be used elsewhere like this?
module.exports = User; //or
exports.User = User;
I am also facing a problem derived from the second solution, suppose I want to add a list of cars to my userSchema, and the car schema is defined in another file car.js, then it means that I will have to expose the carSchema for my user.js file, right? This means I am actually nullifying the second solution I provided above, then, what is the right way of doing this sort of things?

Interesting question.
This is probably just a "syntactic sugar" thing but I am sticking with the second variant because there is IMHO no need to capsule the generation etc. mongoose is the wrapper around such stuff and one can then use the pure mongoose Models and Schemas. Does this sounds reasonable for you?
exports.User = User;

Related

How to get a list of available Mongoose Discriminators?

Given a situation where you have a User Scheme that you use to create a base model called User. And then for user roles, you use mongoose discriminators to create inherited models called Admin, Employee and Client. Is there a way to programmatically determine how many discriminations/inheritances/roles of the User model are available, as well as the available names?
My question in terms of code:
File: models/user.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var options = {discriminatorKey: 'role'};
var userSchema = mongoose.Schema({
name: String,
email: String,
password: String,
},options);
var User = mongoose.model('User', userSchema);
var Client = User.discriminator("Client", mongoose.Schema({
Address : String,
Tax_identification : String,
Phone_number : String,
Internal_Remarks : String,
CRM_status : String,
Recent_contact : String,
}));
var Employee = User.discriminator("Employee",mongoose.Schema({
Staff_Id: String,
}));
module.exports = {User: User, Client: Client, Employee: Employee };
File: controllers/usersController.js
var User = require('../models/user.js').User;
module.exports = {
registerRoutes: function(app){
app.get('user/create',this.userCreateCallback)
},
userCreateCallback: function(req,res){
//Get Available User Roles - The function below doesn't exist,
//Just what I hypothetically want to achieve:
User.geAvailableDiscriminators(function(err,roles){
res.render('user/create',{roles:roles})
});
}
};
I hope I managed to express what I want to do. Alternative approaches are also welcome.
Since v4.11.13, mongoose model has model.discriminators which is an array of models, keyed on the name of the discriminator model.
In your case if you do console.log(User.discriminators) you will get:
{
Client: {
....
},
Employee: {
}
}
As far as I can see, this is not documented anywhere.
Line 158 in lib.helpers.model.discriminators.js is where this is created.
I think you want to fetch the names and values of all the discriminators as for the names you can simply use
User.discriminators
but for finding values you can use this
return Promise.all(Object.keys(discriminators).map(i =>
discriminators[i].find({ userId: this._id }))
).then(promiseResults =>
promiseResults.reduce((arr, el) => arr.concat(el), [])
);
you need to put userId under each discriminators for that.

How to combine models using Mongoose Population

At the moment I'm defining different models in different files depending on where I use them but there are relations between them and I find myself constantly fetching one, then the other using an ID from the first. E.g.:
File 'models/users.js'
var mongoose = require('mongoose');
var db = mongoose.connection;
var UserSchema = mongoose.Schema({
// fields
companyId: {
type: String
}
});
var User = module.exports = mongoose.model('User', UserSchema);
module.exports.getUserById = function(id, callback){
User.findById(id, callback);
}
File 'models/company.js'
var mongoose = require('mongoose');
var db = mongoose.connection;
var CompanySchema = mongoose.Schema({
// fields
});
// module export and functions etc.
So in that example the User companyId is the ID of one of the companies. Then in my routes I'll import both models, get a User, find the company ID and then fetch that company data:
var Promise = require('bluebird');
var User = Promise.promisifyAll(require('../models/user'));
var Company = Promise.promisifyAll(require('../models/company'));
User.getUserByIdAsync()
.then(function(user){
return [user, Company.getCompanyByIdAsync(user.companyId)];
}).spread(function(user,company){
// Do stuff
}).catch(function(err)... etc });
I've been reading about population on the Mongoose docs here: http://mongoosejs.com/docs/populate.html but in the examples everything is in one file. I'm trying to keep models separate for maintainability (and my sanity) so any calls to other models will throw errors.
Is there a way to join the models or reference other external models so I can do this? Is the schema even right?...
I'm using Express and Mongoose.
For reference, the fix is pretty simple. The models being in different files makes no difference.
Change the user model so that the data type is an ObjectID and the ref field references the correct model like so:
companyId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Company'
}
And use it:
User.find({}).populate('companyId').exec(callback);
Really simple in the end..
You can save company reference in Users Model and hence While fetching details you can get company details as well :
var getUserWithPopulate = function (criteria, projection, options, callback) {
Models.Users.find(criteria, projection, options).populate([
{path: 'company', select:'companyId, companyName'}
]).exec(callback);
};

What is an equivalent work around to $setOnInsert in mongoose

I have below collection structure in mongodb. Say
Collection - student - _id,firstname,lastname,class
Now I want to insert 2 extra columns say marks as array{mark1:m1,mark2:m2}when inserting a newrow`.
I did it as below but it inserted record excluding marks values.
var student=new Student({
_id:id,
firstname:result.fname,
lastname:result.lname,
class:result.class,
marks:{
mark1:result.mark.m1,
mark2:result.mark.m2
}
})
Is this possible in Mongoose?
I came across $setOnInsert, but not sure whether this fits here?
So if it fits, is there any equivalent workaround to use MongoDb's $setOnInsert? if not what approach I could use?
Yes, it's possible but that depends on the options set when defining your schema. You don't necessarily need to use the $setOnInsert operator when inserting a new record, the save() method on the model suffices.
The strict option ensures that values added to your model instance that were not specified in the schema gets saved or not to the db.
For example, if you had defined your schema like this:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var studentSchema = new Schema({
firstname: String,
lastname: String,
class: String
})
var Student = mongoose.model('Student', studentSchema);
var student= new Student({
_id: id,
firstname: result.fname,
lastname: result.lname,
class: result.class,
marks: {
mark1: result.mark.m1,
mark2: result.mark.m2
}
});
student.save(); // marks is not saved to the db
But if you set the strict option to false:
var studentSchema = new Schema({..}, { strict: false });
var student= new Student({
_id: id,
firstname: result.fname,
lastname: result.lname,
class: result.class,
marks: {
mark1: result.mark.m1,
mark2: result.mark.m2
}
});
student.save(); // marks is now saved to the db!!
NOTE: The strict option is set to false in mongoose v2 by default for backward compatibility. Set it to true and sleep peacefully. Do not set to false unless you have good reason.

One-To-Many relation in MongoDB

At the moment I am looking at mongoDB. I try to implement a simple one-to-many relation using nodejs and mongoose:
Model/Schema User:
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var UserSchema = new Schema({
name: String
});
module.exports = mongoose.model('User', UserSchema);
Model/Schema Article:
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var ArticleSchema = new Schema({
name: {
type: String,
required: true
},
user: {
type: Schema.ObjectId,
ref: 'User'
}
});
module.exports = mongoose.model('Article', ArticleSchema);
So my question, now:
How can I get all Users including its Articles?
Do I really have to add a ref to my UserScheme, too? What is the best-practice in an one-to-many relation like this? Is there something like a join in mongodb?
One User has many Articles - an Article belongs to one User.
Calling something like /user/:user_id , I want to receive the user with _id=user_id containing all of his articles.
That is the most horrible idea, for various reasons.
First, there is a 16MB BSON document size limit. You simply can not put more into a document. Embedding documents is rather suited for "One-to-(VERY-)Few" relationships than for a "One-to-Many".
As for the use case: What is your question here? It is
For a given user, what are the articles?
REST wise, you should only return the articles when /users/:id/articles is GETed and the articles (and only the articles) should be returned as a JSON array.
So, your model seems to be natural. As for the user:
{
_id: theObjectId,
username: someString
…
}
and an article should look like this:
{
_id: articleIdOrSlugOrWhatever,
authors: [theObjectId],
// or author: theObjectId
retention: someISODate,
published: someOtherISODate
}
So when your REST service is called for /users/:id you'd simply look up
var user = db.users.findOne({_id:id})
And when /users/:id/articles is called, you'd lookup
var articles = db.articles.find({author:id})
Problem solved in a scalable way, adhering to REST principles.

how to convert mongoose js model to object

i am working with node.js and mongoosejs framwork for mongodb. I am trying to convert a mongoose model to an object, I was able to do that, but am getting only fewer elements rather than getting all. Below code which I tried.
user.js
var schema = new Schema({
name:{ type:string },
title:{ type:string, default:"mr" }
});
module.exports = mongoose.model('Users', schema);
usermanager.js
var User = require(../user.js);
var user = new User();
console.log(user.toString());
//printed as {_id:2583457assda312, title:'mr'}
i am expecting name key in that object. i have also tryed toObject it also giveing me the same response.
ther is any posiblty to achive this?
Your usage is intended to be like this:
var user = new User({ name: "Fred" })
and you will get the values from what you have defined, so in this case:
//printed as {_id:2583457assda312, name: "Fred", title:'mr'}
Or you supply your title as here:
var user = new User({ name: "Wilma", title: "Ms" })
and again get your output
//printed as {_id:2583457assda312, name: "Wilma", title: "Ms"}
If what you are trying to do is inspect the schema there is a paths property on Mongoose schema objects
console.log( user.schema.paths )
And that should give you a definition of the various parts of the schema you defined.

Resources