Reputation: 151
I am new to nodejs. I have a application with mongoose and user login. Some of the collections in mongoDB are for all users and some like e.g. the customer-collection is individual so that all users have a collection of their own customers. When the user login and get his jwt-token the individual mongoose models will be instantiated:
var models = require('../models')(userID);
in the folder /models there is a index.js:
var Customer = require('./customermodel');
module.exports = function (userID) {
Customer(userID)
}
the file /models/customermodel.js:
var mongooseCustomer = function(userID) {
var schema = mongoose.Schema({
_id: { type: Number},
Name: { type: String },
Address: { type: String },
}, {collection: userID + 'userID'})
return mongoose.model(userID + 'customer', schema);
}
Module.exports = mongooseCustomer;
The problem occurs when a customer logges in from two different browsers and return the error:
Cannot overwrite 123customer
model once compiled.
I solved the problem it with changing :
var models = require('../models')(userID);
to:
try {
var models = require('../models')(userID);
console.log('Schema was created');
}
catch (err) {
console.log('User is already logged in');
}
Now my question is if the whole setup is too complicated and could there be a more clean way to deal with creating models in a dynamic way?
Upvotes: 2
Views: 3351
Reputation: 6898
If you really want to stick with dynamically creating collections for users a better way than try{ } catch(err)
is to query mongoose if there is already a model compiled for the user. The mongoose.model
function can be used to define and retrieve a model:
var mongooseCustomer = function(userID) {
var model = mongoose.model(userID + 'customer');
if (!model) {
var schema = mongoose.Schema({
_id: { type: Number},
Name: { type: String },
Address: { type: String },
}, {collection: userID + 'userID'});
model = mongoose.model(userID + 'customer', schema);
}
return model;
};
Module.exports = mongooseCustomer;
I expect that your approach of dynamically creating collections for every user will not scale since mongoose is not built for handling 1000th of models and Mongodb has max collection limit as most databases have.
IMHO a better approach could be to use a discriminator in the collections to identify the tenant and expose a db layer to handle tenants in a transparent way.
Let's define a models node.js module that exposes the minimal data layer
// First define the schema and model
var customerSchema = mongoose.Schema({
_id: { type: Number},
// Additional tenant discriminator
userID: { type: Number, required: true },
Name: { type: String },
Address: { type: String },
});
var customerModel = mongoose.model('customer', schema);
module.exports = function(userID) {
return {
customer: {
find: function(query, callback) {
query.userID = userID;
customerModel.find(query, callback);
},
findOne: function(callback) {
var query = { userID : userID };
customerModel.findOne(query, callback);
}
}
}
}
When using this minimal layer you made sure that the userID is always added to Mongodb functions.
var models = require('../models')(123124432);
models.customer.findOne(...);
You could also use a mongoose middleware (http://mongoosejs.com/docs/middleware.html) that handles multi tenancy in a transparent way instead of defining your own data layer on top of mongoose.
Upvotes: 6